Help us shape the future of Go! Take the 2026 Go Developer Survey

cache

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 30 Imported by: 0

Documentation

Overview

Package cache binds semantic cache declarations to concrete Go key and value types. Applications use Cache methods; serialized bytes remain in Backend adapters. A missing entry is distinct from a stored zero value and an error.

Index

Constants

View Source
const (
	MaxMemoEntries = 256
	MaxMemoBytes   = 1 << 20
)

Memo bounds: a full memo stops recording; reads then use the Store as usual.

View Source
const (
	KeyFeature          keyspace.Feature = "cache"
	NamespaceKeyFeature keyspace.Feature = "cache-namespace"
)

Physical address families include the namespace generation metadata used by tags.

View Source
const MaxBatchEntries = 256

MaxBatchEntries bounds input keys before canonicalization. Config may lower it.

View Source
const MaxKeyBytes = keyspace.MaxKeyBytes
View Source
const MaxLoadTimeout = 24 * time.Hour

MaxLoadTimeout bounds how long a detached Remember loader can own its fill.

View Source
const MaxTags = 64

MaxTags bounds application tag references per operation. One additional reserved namespace stamp is permitted. Config.MaxTags may impose a smaller application limit.

View Source
const TagVersionBytes = 16

Variables

This section is empty.

Functions

func FillLeaseKey

func FillLeaseKey(fill EntryKey) (lease.Key, error)

FillLeaseKey is the adapter address for a fill identity. Tagged callers use TaggedKey.FillKey, so a new tag generation has independent ownership.

func ValidateBatchKeys

func ValidateBatchKeys(keys []EntryKey) error

ValidateBatchKeys validates an adapter batch's namespace, order and uniqueness. Empty input is valid; the adapter still validates its lifecycle and context.

func ValidateFillProof

func ValidateFillProof(ctx context.Context, fill EntryKey, proof lease.Proof) error

ValidateFillProof checks cancellation, local validity and the exact cache-fill binding. Adapters must additionally compare ownership atomically at publication.

func ValidateTagKeys

func ValidateTagKeys(keys []EntryKey) error

ValidateTagKeys validates the bounded, canonical address sequence accepted by TaggedBackend metadata operations. Adapter implementations share this contract; their lifecycle and context checks remain adapter-owned. Input is not mutated.

func ValidateTaggedBatch

func ValidateTaggedBatch(keys []TaggedKey) error

ValidateTaggedBatch requires bounded canonical data addresses and the same exact immutable tag stamps on every entry, so one atomic metadata check covers a batch.

func WithMemo

func WithMemo(ctx context.Context) context.Context

WithMemo returns a context that memoizes typed cache reads for its lifetime, typically one request. Within it, the first Get, GetMany, Exists or Remember of a key reads the Store; later reads of that key through the same context (and its children) return the memoized result without I/O, even if another request or process has since changed the entry. Writes (Put, PutMany, Add, Forget, ForgetMany, Increment, Expire) through the context forget the key's memoized results, and Store.Invalidate/InvalidateTags forget the whole store's. At most MaxMemoEntries results and MaxMemoBytes of payload are memoized; later reads are not recorded. A context that already memoizes is returned unchanged. Each result still decodes its own copy.

Types

type Backend

type Backend interface {
	// Get returns owned bytes and reports whether the entry is present.
	Get(context.Context, EntryKey) ([]byte, bool, error)
	// Put replaces the entry and its expiry with a copy of the supplied bytes.
	Put(context.Context, EntryKey, []byte, TTL) error
	// Add atomically stores the bytes and TTL only when the entry is absent.
	Add(context.Context, EntryKey, []byte, TTL) (bool, error)
	// Forget removes the entry and reports whether it was present.
	Forget(context.Context, EntryKey) (bool, error)
}

Backend is the serialized cache adapter boundary, not an application value API. Implementations must be concurrency-safe, observe cancellation, copy retained input bytes without modifying the caller's input and return owned read bytes. Get distinguishes absence from failure; a present empty byte slice is a hit. Add checks absence and writes atomically. A failed mutating command may have reached a remote backend: never retry it implicitly. Adapter lifecycle is owned by the caller that constructs the backend. Store borrows this capability and does not close a shared adapter.

type BatchBackend

type BatchBackend interface {
	// ForgetMany atomically removes the canonical batch and counts distinct live entries.
	ForgetMany(context.Context, []EntryKey) (uint64, error)
}

BatchBackend removes a bounded canonical batch in one atomic operation. All validation precedes deletion. Count distinct live entries; preserve all live entries if a later key fails validation. Errors return zero, but a remote error may hide an applied whole batch. Adapters validate with ValidateBatchKeys.

type BatchReadBackend

type BatchReadBackend interface {
	// GetMany returns owned read results in the canonical input order.
	GetMany(context.Context, []EntryKey) ([]BatchValue, error)
}

BatchReadBackend reads a bounded canonical batch (see ValidateBatchKeys) in one operation, returning results in input order. Missing, expired or unusable entries are misses. Size bounds follow Get; nothing is mutated except removal of unusable storage.

type BatchValue

type BatchValue struct {
	Data  []byte
	Found bool
}

BatchValue is one adapter batch-read result. Found distinguishes a miss from a present empty payload; Data is owned by the caller.

type Cache

type Cache[K, V any] struct {
	// contains filtered or unexported fields
}

Cache accepts only its declared key and value types. Values are serialized snapshots; reading a hit does not return a reference into backend storage. The zero value rejects operations. Use Declaration.Bind to construct a handle.

func (Cache[K, V]) Add

func (c Cache[K, V]) Add(ctx context.Context, key K, value V, ttl TTL) (bool, error)

Add atomically inserts only when no unexpired entry exists. False means an existing value was retained, including its original TTL. Encoding precedes Add.

func (Cache[K, V]) Exists

func (c Cache[K, V]) Exists(ctx context.Context, key K) (bool, error)

Exists checks whether a live entry belongs to the current namespace/tag snapshot. It does not decode or copy the value; it is not validation of the application DTO. A later Get can miss after expiry/invalidation or fail its own codec/size checks. Like Get, a tagged existence check that races an invalidation re-resolves its snapshot (bounded) and otherwise reports false; it never returns Conflict.

func (Cache[K, V]) Expire

func (c Cache[K, V]) Expire(ctx context.Context, key K, ttl TTL) (bool, error)

Expire updates an existing entry's expiry without changing its value. Forever removes expiry. A present entry returns true even if the TTL did not change; absent/invalidated entries return false. Invalid TTLs fail before I/O. This is atomic with snapshot validation, not with a subsequent Get or application write.

func (Cache[K, V]) Flexible

func (c Cache[K, V]) Flexible(ctx context.Context, key K, fresh, stale time.Duration, loader func(context.Context) (V, error), options ...RememberOption) (V, error)

Flexible is Remember with stale-while-revalidate. A value younger than fresh is returned as is. An older value, up to fresh+stale, is returned immediately while one Store-owned background fill (coalesced with other fills of the key) loads and publishes a replacement; its failure is only logged (WithLogger). A missing or expired value is loaded like Remember and stored for fresh+stale.

Freshness is a separate marker entry written after the value with TTL fresh and read with it in one batch, so no clock comparison is involved. The marker is an ordinary stored entry: it counts toward adapter capacity (MaxEntries and MaxBytes of file and PostgreSQL stores) and invalidation removes it with the value. The value stays an ordinary entry of this family (Get, Forget and invalidation apply). Put, PutMany, Add, Increment and Expire leave the marker unchanged: a value written while a marker lives counts as fresh until that marker expires; without one it is served stale and refreshed. Loader, timeout, coalescing and publication rules are those of Remember. The background fill keeps the caller's values but not its cancellation. Flexible reads are not memoized by WithMemo.

func (Cache[K, V]) Forget

func (c Cache[K, V]) Forget(ctx context.Context, key K) (bool, error)

Forget removes the current entry and reports whether it was present. A concurrent Remember fill may repopulate the entry afterwards.

func (Cache[K, V]) ForgetMany

func (c Cache[K, V]) ForgetMany(ctx context.Context, keys ...K) (uint64, error)

ForgetMany atomically removes distinct live entries from this typed view. The input count is bounded before key encoding/deduplication. All keys are encoded before metadata I/O, and one snapshot protects the entire batch. No payload codec runs. An empty batch validates handle/context but performs no backend I/O. A failed remote acknowledgement returns zero and may hide an applied batch. Like Forget, this does not prevent a concurrent Remember from later repopulating.

func (Cache[K, V]) Get

func (c Cache[K, V]) Get(ctx context.Context, key K) (V, bool, error)

Get returns found=true for every stored value, including nil or a zero V. Backend/decoder failure returns zero V, false and an error; it is never a miss. A tagged read that races an invalidation re-resolves its snapshot (bounded) and otherwise reports a miss; a pure read never returns a snapshot conflict. A stored value larger than the current MaxValueBytes (written under an earlier, larger bound) is a miss that a later write replaces.

func (Cache[K, V]) GetMany

func (c Cache[K, V]) GetMany(ctx context.Context, keys ...K) ([]Lookup[V], error)

GetMany reads up to Config.MaxBatchEntries keys under one tag snapshot and returns results in input order (a repeated key repeats its result). Adapters implementing BatchReadBackend/TaggedBatchReadBackend (memory and Redis) read the whole batch in one operation; others read each distinct key in turn. Like Get, a snapshot replaced by a concurrent invalidation is re-resolved (bounded) and otherwise every key is a miss. Each value is decoded independently.

func (Cache[K, V]) Pull

func (c Cache[K, V]) Pull(ctx context.Context, key K) (V, bool, error)

Pull reads and removes one entry. It is not atomic: a concurrent writer can replace the value between the read and the removal, which then removes the replacement. Use it for single-consumer values such as one-time notices.

func (Cache[K, V]) Put

func (c Cache[K, V]) Put(ctx context.Context, key K, value V, ttl TTL) error

Put replaces one entry with the supplied TTL. No implicit command retry occurs.

func (Cache[K, V]) PutMany

func (c Cache[K, V]) PutMany(ctx context.Context, ttl TTL, entries ...Entry[K, V]) error

PutMany writes up to Config.MaxBatchEntries entries with one TTL. Every key and value is encoded before the first write, so a codec failure writes nothing. Entries then use one tag snapshot (adapters with SnapshotWriteBackend resolve the current snapshot inside each write). The batch is not atomic: entries are written in input order (a repeated key keeps its last value), the first failure stops the remaining writes, and earlier entries stay stored. No write is retried; Config.Timeout bounds the whole batch.

func (Cache[K, V]) Remember

func (c Cache[K, V]) Remember(ctx context.Context, key K, ttl TTL, loader func(context.Context) (V, error), options ...RememberOption) (V, error)

Remember reads a snapshot or coalesces concurrent misses within this Store. The selected owner supplies the loader and TTL; followers do not run their loaders. Each result is decoded independently, including the owner's.

The loader runs in a fill owned by the Store, under a context that keeps the owner's values but not its cancellation, bounded by LoadTimeout (or WithLoadTimeout). A canceled caller, owner or follower, stops only its own wait; the fill finishes for everyone else and keeps its MaxFills slot until the loader actually exits. If a loader fails only because it used the owner's own (ended) request context, followers elect a new owner instead of failing. Other loader failures reach existing followers without retry.

Config.Timeout bounds each backend step, not the loader. A failed publication of a successfully loaded value does not fail the call: every caller receives the value, and the failure is counted in Stats and logged (WithLogger). When MaxFills is exhausted, the caller loads directly without coalescing rather than failing; a full MaxFillWaiters returns retryable fault.Overloaded.

A fill rechecks storage after election, but is not a transaction with Put or Forget: overlapping writes may be replaced and forgotten entries repopulated. NewCoordinatedStore adds lease-protected publication across instances. Ordinary stores coordinate locally. Neither mode promises exactly-once loaders. Recursive loads must propagate the supplied context; detected fill cycles fail with fault.Cycle.

func (Cache[K, V]) WithTags

func (c Cache[K, V]) WithTags(first Tag, rest ...Tag) (Cache[K, V], error)

WithTags returns a view with the supplied tag set; it replaces any earlier set. At least one tag is required. Construction validates ownership/capability without encoding keys or performing I/O. Operations share the same typed API and Store.

type Codec

type Codec[V any] struct {
	// contains filtered or unexported fields
}

Codec preserves V through serialization. Callbacks must be concurrency-safe, leave inputs unchanged and return owned results. The Cache copies buffers at adapter boundaries and catches codec panic/Goexit without publishing a value. Size limits bound serialized storage, not arbitrary allocations inside a codec.

func JSON

func JSON[V any]() Codec[V]

JSON uses Go's JSON encoding rules without declaring a public HTTP DTO schema. It preserves dynamic JSON numbers as json.Number and rejects trailing documents. Change the declaration name/version when changing an incompatible stored format.

func NewCodec

func NewCodec[V any](encode func(V) ([]byte, error), decode func([]byte) (V, error)) Codec[V]

NewCodec retains the supplied value encoder and decoder without validating them.

func (Codec[V]) Validate

func (c Codec[V]) Validate() error

Validate returns fault.Invalid if either codec callback is missing.

type Config

type Config struct {
	Namespace       Namespace
	MaxKeyBytes     int
	MaxValueBytes   int
	MaxDeclarations int
	MaxBatchEntries int // Maximum batch input keys before deduplication; bounded by MaxBatchEntries.
	MaxTags         int // Maximum references in a tagged view/invalidation; bounded by cache.MaxTags.
	// MaxFills bounds concurrent Remember loaders across this store.
	MaxFills int
	// MaxFillWaiters bounds followers of each Remember loader (excluding its owner).
	MaxFillWaiters int
	// Timeout bounds each backend step (read, write, metadata) and every other
	// operation. It does not cap Remember loaders; LoadTimeout does.
	Timeout time.Duration
	// LoadTimeout bounds one Remember loader run, detached from the requesting
	// caller's cancellation. A call can narrow it with WithLoadTimeout.
	LoadTimeout time.Duration
}

Config bounds one application's typed cache access. Namespace is mandatory. MaxValueBytes bounds encoded data; custom codecs may allocate before that check.

func DefaultConfig

func DefaultConfig(namespace Namespace) Config

DefaultConfig returns bounded cache defaults for the supplied namespace.

func (Config) Validate

func (c Config) Validate() error

Validate checks the namespace and operation limits, returning fault.Invalid for invalid configuration.

type CoordinatedBackend

type CoordinatedBackend interface {
	Backend
	// PutLeased publishes the bytes only while the proof identifies a live lease.
	PutLeased(context.Context, EntryKey, []byte, TTL, lease.Proof) error
}

CoordinatedBackend atomically checks a live lease and publishes a value using the same authority as the lease Backend. Validate the proof before I/O, then compare its complete address/owner and finite expiry in the write operation. Missing/replaced/expired ownership returns lease.ErrLost without changing data. Payload validation and ordinary Put semantics still apply. Never retry a write.

type CoordinatedTaggedBackend

type CoordinatedTaggedBackend interface {
	TaggedBackend
	// PutTaggedLeased publishes only while both the lease proof and tag snapshot remain current.
	PutTaggedLeased(context.Context, TaggedKey, []byte, TTL, lease.Proof) error
}

CoordinatedTaggedBackend checks both ownership and the tag snapshot atomically with publication. A stale tag returns Conflict and cannot replace newer data.

type CoordinationConfig

type CoordinationConfig struct{ LeaseDuration, Wait time.Duration }

CoordinationConfig sets the renewable fill lease and the maximum time a miss waits while another instance fills (polling both the lease and the cache at the lease manager's PollInterval). Loaders are bounded by LoadTimeout.

func DefaultCoordinationConfig

func DefaultCoordinationConfig() CoordinationConfig

DefaultCoordinationConfig returns a 30-second lease duration and a two-second fill wait.

type Counter

type Counter[K any] struct {
	// contains filtered or unexported fields
}

Counter retains its declared key type through exact int64 reads and mutations. It is an evictable cache counter, not durable accounting or a rate limiter. Operations use the Store's namespace, key codec, timeout and error handling. Construct it with CounterDeclaration.Bind; its zero value rejects operations.

func (Counter[K]) Add

func (c Counter[K]) Add(ctx context.Context, key K, value int64, ttl TTL) (bool, error)

Add initializes only an absent or expired counter. Existing value/TTL remain.

func (Counter[K]) Decrement

func (c Counter[K]) Decrement(ctx context.Context, key K, amount int64, initialTTL TTL) (int64, error)

Decrement subtracts a nonnegative amount. Use Increment for a signed delta.

func (Counter[K]) Exists

func (c Counter[K]) Exists(ctx context.Context, key K) (bool, error)

Exists retains the declared counter key type without decoding its value.

func (Counter[K]) Expire

func (c Counter[K]) Expire(ctx context.Context, key K, ttl TTL) (bool, error)

Expire changes the counter's expiry, preserving the existing exact integer.

func (Counter[K]) Forget

func (c Counter[K]) Forget(ctx context.Context, key K) (bool, error)

Forget removes the current counter and reports whether it was present.

func (Counter[K]) ForgetMany

func (c Counter[K]) ForgetMany(ctx context.Context, keys ...K) (uint64, error)

ForgetMany atomically removes a bounded batch of distinct counters in this view.

func (Counter[K]) Get

func (c Counter[K]) Get(ctx context.Context, key K) (int64, bool, error)

Get separates a missing entry from a stored zero. Corrupt or noncanonical data returns an error, never a miss or a rounded integer.

func (Counter[K]) Increment

func (c Counter[K]) Increment(ctx context.Context, key K, delta int64, initialTTL TTL) (int64, error)

Increment atomically adds a signed delta and returns the resulting value. A negative delta subtracts. Missing/expired values start at zero with initialTTL; existing entries retain their original expiry, even for delta=0. The initialTTL must always be valid. Overflow/corruption fails without modifying the entry.

func (Counter[K]) Put

func (c Counter[K]) Put(ctx context.Context, key K, value int64, ttl TTL) error

Put replaces the counter and its TTL atomically.

func (Counter[K]) WithTags

func (c Counter[K]) WithTags(first Tag, rest ...Tag) (Counter[K], error)

WithTags retains atomic counter operations while applying the selected tags.

type CounterBackend

type CounterBackend interface {
	// Increment atomically adds delta, creating a missing counter with initialTTL and preserving an existing expiry.
	Increment(ctx context.Context, key EntryKey, delta int64, initialTTL TTL) (int64, error)
}

CounterBackend is an optional capability of the Backend used by a Store. Increment must be atomic with Get/Put/Add/Forget on the same EntryKey. Its representation is canonical signed decimal int64, shared with Counter values. A missing or expired entry starts at zero; initialTTL applies only to creation. Existing expiry is retained. Corrupt data, overflow and capacity rejection must leave an existing entry unchanged. Invalid initialTTL is always an error. Failed remote writes may still have been applied; do not retry implicitly.

type CounterDeclaration

type CounterDeclaration[K any] struct {
	// contains filtered or unexported fields
}

CounterDeclaration binds one concrete key type to exact signed int64 values. Copies reuse the underlying declaration identity. The zero value is invalid.

func DefineCounter

func DefineCounter[K any](name Name, keys KeyCodec[K]) CounterDeclaration[K]

DefineCounter creates a declaration with canonical signed decimal int64 values.

func (CounterDeclaration[K]) Bind

func (d CounterDeclaration[K]) Bind(store *Store) (Counter[K], error)

Bind validates the counter capability and full int64 byte bound before claiming a family. Value caches and counters share the Store's declaration ownership and capacity checks. Construction performs no adapter operation.

func (CounterDeclaration[K]) Name

func (d CounterDeclaration[K]) Name() Name

Name returns the counter family name, or an empty name for the zero declaration.

func (CounterDeclaration[K]) Validate

func (d CounterDeclaration[K]) Validate() error

Validate checks the declaration name and key codec.

type Declaration

type Declaration[K, V any] struct {
	// contains filtered or unexported fields
}

Declaration is an immutable, reusable typed cache family. Its zero value is invalid.

func Define

func Define[K, V any](name Name, keys KeyCodec[K], values Codec[V]) Declaration[K, V]

Define creates a reusable declaration identity without validating its name or codecs.

func (Declaration[K, V]) Bind

func (d Declaration[K, V]) Bind(store *Store) (Cache[K, V], error)

Bind rejects conflicting declarations before they can read each other's data. Copies of the same declaration can bind repeatedly. Separate declarations with the same name fail, including declarations whose payload types happen to match.

func (Declaration[K, V]) Name

func (d Declaration[K, V]) Name() Name

Name returns the family name, or an empty name for the zero declaration.

func (Declaration[K, V]) Validate

func (d Declaration[K, V]) Validate() error

Validate checks that the declaration has a valid name and initialized codecs.

type Entry

type Entry[K, V any] struct {
	Key   K
	Value V
}

Entry is one typed key/value pair written by PutMany.

type EntryBackend

type EntryBackend interface {
	// Exists reports whether a usable live entry is present without decoding its value.
	Exists(context.Context, EntryKey) (bool, error)
	// Expire changes a live entry's TTL and reports its presence without creating missing entries.
	Expire(context.Context, EntryKey, TTL) (bool, error)
}

EntryBackend inspects existence and updates expiry without materializing payloads. Methods validate the adapter's stored type/envelope/size, but do not run a value codec. Expire returns true for a current entry even if its expiry is unchanged. Missing entries are never created. Context, lifecycle and uncertain-write rules are the same as Backend. No read/write fallback or retry is permitted.

type EntryKey

type EntryKey struct {
	// contains filtered or unexported fields
}

EntryKey is the explicit backend address boundary. Adapters must retain its complete namespace. Applications use their typed Cache to construct addresses.

func NewEntryKey

func NewEntryKey(namespace Namespace, name Name, logical string) (EntryKey, error)

NewEntryKey is intended for adapter authors. Codecs must distinguish semantic keys.

func NewNamespaceTagKey

func NewNamespaceTagKey(namespace Namespace) (EntryKey, error)

NewNamespaceTagKey returns the reserved adapter metadata address for a complete cache namespace. Its feature prefix cannot collide with application cache/tag declarations. Applications call Store.Invalidate instead of managing versions.

func TaggedDataKey

func TaggedDataKey(base EntryKey, tags []EntryKey) (EntryKey, error)

TaggedDataKey returns the stable payload address NewTaggedKey produces for base under the canonical metadata addresses tags, for any versions. Adapters use it to address a combined metadata/payload operation before versions are known.

func (EntryKey) Name

func (k EntryKey) Name() Name

Name returns the declared cache family name.

func (EntryKey) Namespace

func (k EntryKey) Namespace() Namespace

Namespace returns the application and environment namespace.

func (EntryKey) String

func (k EntryKey) String() string

String returns the physical address, including the namespace and cache feature prefix.

func (EntryKey) Validate

func (k EntryKey) Validate() error

Validate returns fault.Invalid for an uninitialized address.

type Event

type Event struct {
	Family       Name
	Operation    Operation
	Hits, Misses int
	Loaded       bool
	Unpublished  bool
	Stale        bool
	Duration     time.Duration
	Code         fault.Code
}

Event describes one completed typed cache call. Family is the declaration name; it is empty for Store.Invalidate and Store.InvalidateTags. Hits and Misses count the keys a read observed (Get, GetMany, Exists and the lookup inside Remember and Flexible). Loaded reports a miss that ran or joined a loader, and Unpublished that the loaded value could not be stored (the caller still received it). Stale reports a Flexible hit served while a background refresh was requested. Code is the framework classification of a failure and is empty on success. Keys, values and error text are never exposed.

type FlushBackend

type FlushBackend interface {
	// FlushNamespace removes entries in the namespace and returns the removal count.
	FlushNamespace(context.Context, Namespace) (uint64, error)
}

FlushBackend physically removes every entry of one cache namespace. Adapters without tag metadata (file and PostgreSQL) implement it so Store.Invalidate works for every store type. It must not remove other namespaces' entries and must serialize with the adapter's single-entry operations, but it is not a fence: a Remember fill that started earlier can publish afterwards. It returns the number of removed entries; a remote error may hide a partial removal.

type KeyCodec

type KeyCodec[K any] = keyspace.Codec[K]

KeyCodec preserves the declared key type using the shared keyspace codec.

func NewKeyCodec

func NewKeyCodec[K any](encode func(K) (string, error)) KeyCodec[K]

NewKeyCodec wraps the supplied typed key encoder.

func SignedKeys

func SignedKeys[K ~int | ~int8 | ~int16 | ~int32 | ~int64]() KeyCodec[K]

SignedKeys encodes signed integer keys as decimal text.

func StringKeys

func StringKeys[K ~string]() KeyCodec[K]

StringKeys encodes string keys without changing their text.

func TextKeys

func TextKeys[K encoding.TextMarshaler]() KeyCodec[K]

TextKeys encodes keys through their MarshalText method.

func UnsignedKeys

func UnsignedKeys[K ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr]() KeyCodec[K]

UnsignedKeys encodes unsigned integer keys as decimal text.

type Lookup

type Lookup[V any] struct {
	Value V
	Found bool
}

Lookup is one typed batch-read result. Found distinguishes a miss from a stored zero value.

type Name

type Name string

Name identifies a declared cache family within an application/environment.

type NamedStore

type NamedStore struct {
	Name  StoreName
	Value *Store
}

NamedStore binds a typed name to an existing borrowed instance.

type Namespace

type Namespace = keyspace.Namespace

Namespace separates applications and environments sharing a backend.

type Observer

type Observer func(context.Context, Event)

Observer receives completed calls synchronously on the calling goroutine. It must be fast, must not use the cache and must not retain the context. A panic is contained and never changes the operation's result.

type Operation

type Operation uint8

Operation identifies a typed cache call reported to an Observer.

const (
	OperationGet Operation = iota + 1
	OperationGetMany
	OperationRemember
	OperationPut
	OperationPutMany
	OperationAdd
	OperationForget
	OperationForgetMany
	OperationIncrement
	OperationExists
	OperationExpire
	OperationInvalidate
	OperationFlexible
)

func (Operation) String

func (o Operation) String() string

String returns a stable snake_case name suitable for metric labels.

type RememberOption

type RememberOption func(*rememberSettings) error

RememberOption narrows one Remember call.

func WithLoadTimeout

func WithLoadTimeout(timeout time.Duration) RememberOption

WithLoadTimeout bounds this call's loader instead of Config.LoadTimeout. The duration must be positive and at most MaxLoadTimeout. Store Config.Timeout never caps a loader; it bounds each backend step around it.

type Requirements

type Requirements struct{ Tags, Counters, Entries, Batches, DistributedFills bool }

Requirements declares capabilities an application's cache declarations need. Assembly checks them before boot instead of failing at the first request.

type SnapshotReadBackend

type SnapshotReadBackend interface {
	// ReadSnapshot atomically resolves tag versions and checks the entry, returning bytes only when payload is true.
	ReadSnapshot(ctx context.Context, base EntryKey, tags []EntryKey, payload bool) (TaggedKey, []byte, bool, error)
}

SnapshotReadBackend optionally combines ResolveTags with a tagged read in one atomic operation, halving round trips for Get, Exists and Remember lookups. tags are canonical metadata addresses (see ValidateTagKeys); missing metadata receives fresh random versions exactly as in ResolveTags. The returned key must be NewTaggedKey(base, stamps) for the versions read. found reports a current, readable payload for that key; payload=false skips transferring the value. An obsolete or over-bound stored payload is a miss.

type SnapshotWriteBackend

type SnapshotWriteBackend interface {
	// PutSnapshot resolves tags and replaces the entry atomically.
	PutSnapshot(ctx context.Context, base EntryKey, tags []EntryKey, data []byte, ttl TTL) error
	// AddSnapshot resolves tags and inserts an absent current entry atomically.
	AddSnapshot(ctx context.Context, base EntryKey, tags []EntryKey, data []byte, ttl TTL) (bool, error)
	// ForgetSnapshot resolves tags and removes the current entry atomically.
	ForgetSnapshot(ctx context.Context, base EntryKey, tags []EntryKey) (bool, error)
	// IncrementSnapshot resolves tags and increments the current counter atomically.
	IncrementSnapshot(ctx context.Context, base EntryKey, tags []EntryKey, delta int64, ttl TTL) (int64, error)
	// ExpireSnapshot resolves tags and changes a current entry's expiry atomically.
	ExpireSnapshot(ctx context.Context, base EntryKey, tags []EntryKey, ttl TTL) (bool, error)
}

SnapshotWriteBackend optionally resolves tag metadata (tags are canonical metadata addresses; missing metadata receives fresh versions exactly as in ResolveTags) and applies one tagged mutation under the resolved snapshot in the same atomic operation. Direct Put, Add, Forget, Increment and Expire then cost one round trip and never cross an invalidation; Remember publications keep the snapshot they read. Results and failures follow the matching TaggedBackend, TaggedCounterBackend and TaggedEntryBackend methods.

type Stats

type Stats struct {
	Hits, Misses, Writes, WriteFailures uint64
	Loads, Coalesced, Uncoalesced       uint64
	SnapshotRetries                     uint64
}

Stats is a snapshot of one Store's typed operation totals since construction. Hits and Misses count Get/Remember reads; Writes counts successful Put, Add and Remember publications; WriteFailures counts Remember publications that failed after a successful load (the caller still received the value). Loads counts Remember loader runs; Coalesced counts callers that joined an existing fill; Uncoalesced counts loads run outside the fill registry because MaxFills was exhausted. SnapshotRetries counts tagged reads that re-resolved a snapshot changed by a concurrent invalidation.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store binds cache declarations to one namespace and a borrowed Backend. NewStore is pure construction: it performs no I/O and starts no goroutines. Each application builds its own Store and owns the adapter lifecycle. Remember and Flexible run loaders in Store-owned fills; Close stops new operations and drains those fills before the owner closes the backend. TaggedBackend adapters automatically protect every value/counter operation with a reserved namespace generation, enabling Invalidate across stores and processes. FlushBackend adapters invalidate a namespace by physically removing its entries.

func NewCoordinatedStore

func NewCoordinatedStore(manager *lease.Manager, config Config, coordination CoordinationConfig, options ...StoreOption) (*Store, error)

NewCoordinatedStore enables distributed Remember before declarations are bound. The borrowed manager supplies both lease and cache capabilities from the SAME adapter/namespace. It performs no I/O and never falls back to an ordinary store. Close the lease manager before its adapter. Existing Cache methods remain typed.

func NewStore

func NewStore(backend Backend, config Config, options ...StoreOption) (*Store, error)

NewStore validates configuration and borrows the backend without performing I/O. The caller owns the backend lifecycle and must close the store before the backend.

func (*Store) Close

func (s *Store) Close(ctx context.Context) error

Close rejects new operations with fault.Closed, cancels the contexts of running fills (Remember loaders whose callers have gone, Flexible background refreshes and their publications) and waits for them to exit. ctx bounds only the wait: a callback that ignores cancellation keeps running, and Done closes when it has actually exited. Close never closes the borrowed backend; call it before the backend's owner does. Repeated calls wait again.

func (*Store) Done

func (s *Store) Done() <-chan struct{}

Done closes after Close once every running fill has exited.

func (*Store) Invalidate

func (s *Store) Invalidate(ctx context.Context) error

Invalidate logically invalidates every typed cache entry in this namespace, including tagged views and counters across Store instances. TaggedBackend stores (memory, Redis) rotate a reserved namespace generation; FlushBackend stores (file, PostgreSQL) physically remove the namespace's entries instead. Basic adapters without either capability return fault.Invalid.

Rotation is atomic. A stale operation cannot publish or delete a newer value; overlapping operations may return fault.Conflict. An in-flight Remember loader retains ownership until it exits, then fails publication if its snapshot changed (its callers still receive the loaded value; the failure is reported). New calls use independent fill identities. No loader or mutation is retried. A physical flush is not a fence: a fill that started earlier may publish after it.

This is not physical erasure for tagged stores: stale payloads expire or are reclaimed on access, replacement or adapter eviction, using stable addresses even for Forever data. Tag metadata, leases, rate limits, pub/sub and other namespaces are not cleared. Direct adapter writes bypass this typed-store contract. A remote error may hide an applied rotation; it does not prove that invalidation was rolled back.

func (*Store) InvalidateTags

func (s *Store) InvalidateTags(ctx context.Context, first Tag, rest ...Tag) error

InvalidateTags atomically invalidates entries containing any selected reference. A remote error may hide an applied invalidation; no implicit retry occurs.

func (*Store) Namespace

func (s *Store) Namespace() Namespace

Namespace returns the store's configured namespace.

func (*Store) Require

func (s *Store) Require(required Requirements) error

Require returns fault.Invalid if the store lacks any requested capability.

func (*Store) Stats

func (s *Store) Stats() Stats

Stats returns a snapshot of operation counters, or zero counters for a nil store.

type StoreName

type StoreName string

StoreName identifies one configured cache instance, distinct from other service families.

func (StoreName) Validate

func (n StoreName) Validate() error

Validate checks the configured service name.

type StoreOption

type StoreOption func(*Store) error

StoreOption configures optional Store collaborators at construction.

func WithLogger

func WithLogger(logger *slog.Logger) StoreOption

WithLogger borrows a structured logger for cache failures that do not fail the caller, such as a Remember whose loaded value could not be published. Records carry the cache family and a redacted errordiag diagnostic, never keys, values or arbitrary error text. Application assembly supplies its configured logger.

func WithObserver

func WithObserver(observer Observer) StoreOption

WithObserver installs one call observer for metrics or tracing. Observers are fixed for the Store's lifetime.

type Stores

type Stores struct {
	// contains filtered or unexported fields
}

Stores is immutable. Default and named access alias the same owned instance. Resolve dependencies during construction; pass concrete services to domain code.

func NewStores

func NewStores(selected StoreName, entries ...NamedStore) (*Stores, error)

NewStores validates the named stores and selected default without taking ownership of their lifecycles.

func (*Stores) Default

func (r *Stores) Default() (*Store, error)

Default returns the selected registered store or an error if no default is available.

func (*Stores) DefaultName

func (r *Stores) DefaultName() StoreName

DefaultName returns the selected name, or an empty name for a nil registry.

func (*Stores) Names

func (r *Stores) Names() []StoreName

Names returns an owned, sorted list of registered names, or nil for a nil registry.

func (*Stores) Store

func (r *Stores) Store(name StoreName) (*Store, error)

Store returns the store registered under name or an error if it is unknown.

type TTL

type TTL struct {
	// contains filtered or unexported fields
}

TTL explicitly selects finite expiry or persistence. Its zero value is invalid. A TTL takes effect when the backend accepts a write, not when its value is encoded.

func For

func For(duration time.Duration) TTL

For constructs a finite TTL whose duration must be positive when validated.

func Forever

func Forever() TTL

Forever returns an explicitly persistent TTL.

func (TTL) Duration

func (t TTL) Duration() time.Duration

Duration returns the finite duration, or zero for a persistent TTL.

func (TTL) IsForever

func (t TTL) IsForever() bool

IsForever reports whether persistence was explicitly selected.

func (TTL) Validate

func (t TTL) Validate() error

Validate returns fault.Invalid unless the TTL is positive or explicitly persistent.

type Tag

type Tag struct {
	// contains filtered or unexported fields
}

Tag is an opaque bound reference. The key is encoded during an operation, under its context and error boundary. Keep reference-backed key values immutable.

type TagDeclaration

type TagDeclaration[K any] struct {
	// contains filtered or unexported fields
}

TagDeclaration owns a tag family's concrete key type. It shares declaration ownership and limits with value/counter families within the same Store.

func DefineTag

func DefineTag[K any](name Name, keys KeyCodec[K]) TagDeclaration[K]

DefineTag creates a typed tag declaration using opaque tag versions.

func (TagDeclaration[K]) Bind

func (d TagDeclaration[K]) Bind(store *Store) (Tags[K], error)

Bind validates tag support and claims the declaration family in the store without I/O.

func (TagDeclaration[K]) Name

func (d TagDeclaration[K]) Name() Name

Name returns the tag family name, or an empty name for the zero declaration.

func (TagDeclaration[K]) Validate

func (d TagDeclaration[K]) Validate() error

Validate checks the tag declaration and its key codec.

type TagStamp

type TagStamp struct {
	Key     EntryKey
	Version TagVersion
}

TagStamp is the adapter boundary for a resolved tag identity and version.

type TagVersion

type TagVersion struct {
	// contains filtered or unexported fields
}

TagVersion is an opaque random identity. Missing metadata must get a fresh version; recreating a previous/default version could resurrect stale values.

func NewTagVersion

func NewTagVersion() (TagVersion, error)

NewTagVersion creates a nonzero random version, returning fault.Internal if entropy fails.

func ParseTagVersion

func ParseTagVersion(data []byte) (TagVersion, error)

ParseTagVersion copies a fixed-size nonzero version or returns fault.Invalid.

func (TagVersion) Bytes

func (v TagVersion) Bytes() []byte

Bytes returns an owned copy of the version bytes.

func (TagVersion) Validate

func (v TagVersion) Validate() error

Validate returns fault.Invalid for the zero version.

type TaggedBackend

type TaggedBackend interface {
	// ResolveTags returns versions in input order, creating fresh versions for missing metadata.
	ResolveTags(context.Context, []EntryKey) ([]TagVersion, error)
	// InvalidateTags atomically replaces all selected tag versions.
	InvalidateTags(context.Context, []EntryKey) error
	// GetTagged returns owned bytes under the current snapshot, treating obsolete stored values as absent.
	GetTagged(context.Context, TaggedKey) ([]byte, bool, error)
	// PutTagged replaces the entry only if the tag snapshot is current.
	PutTagged(context.Context, TaggedKey, []byte, TTL) error
	// AddTagged stores the entry only if the snapshot is current and no current value exists.
	AddTagged(context.Context, TaggedKey, []byte, TTL) (bool, error)
	// ForgetTagged removes the current value only under a current tag snapshot.
	ForgetTagged(context.Context, TaggedKey) (bool, error)
}

TaggedBackend adds atomic tag metadata and conditional tagged entry operations to a Backend. ResolveTags returns versions in input order, creating fresh random versions for missing metadata. Inputs are canonical and bounded; adapters must still validate their public boundary. InvalidateTags replaces all selected versions atomically. Missing metadata never reuses an earlier/default version.

Tagged operations must reject a changed/missing snapshot with fault.Conflict before altering data. Get/Add treat an older stored fingerprint as absent; a conditional cleanup cannot delete a newer replacement. Storage addresses stay stable across versions. Size, ownership and corruption failures preserve live entries. All methods must observe context and share Backend lifecycle/limits.

type TaggedBatchBackend

type TaggedBatchBackend interface {
	// ForgetManyTagged atomically removes the batch under its shared current snapshot.
	ForgetManyTagged(context.Context, []TaggedKey) (uint64, error)
}

TaggedBatchBackend additionally requires every entry to share one tag snapshot. Missing/changed metadata returns Conflict before deletion. Old stored generations are logically absent and do not increase the live removal count. Adapters share ValidateTaggedBatch; metadata and unrelated keys must remain untouched.

type TaggedBatchReadBackend

type TaggedBatchReadBackend interface {
	// GetManyTagged returns owned results in input order under one current tag snapshot.
	GetManyTagged(context.Context, []TaggedKey) ([]BatchValue, error)
}

TaggedBatchReadBackend reads a canonical batch sharing one tag snapshot (see ValidateTaggedBatch) atomically with its validation. A changed or missing snapshot returns Conflict; obsolete payloads are misses and may be reclaimed.

type TaggedCounterBackend

type TaggedCounterBackend interface {
	// IncrementTagged atomically increments the counter under a current tag snapshot.
	IncrementTagged(context.Context, TaggedKey, int64, TTL) (int64, error)
}

TaggedCounterBackend is the optional exact counter capability for tagged views. It combines snapshot validation and Increment semantics in one atomic operation.

type TaggedEntryBackend

type TaggedEntryBackend interface {
	// ExistsTagged checks entry existence under the supplied current tag snapshot.
	ExistsTagged(context.Context, TaggedKey) (bool, error)
	// ExpireTagged changes expiry only under the supplied current tag snapshot.
	ExpireTagged(context.Context, TaggedKey, TTL) (bool, error)
}

TaggedEntryBackend checks a current tag snapshot atomically with inspection or expiry mutation. Stale callers return Conflict; obsolete stored entries are logically absent and may be reclaimed, without decoding their old payloads.

type TaggedKey

type TaggedKey struct {
	// contains filtered or unexported fields
}

TaggedKey owns an immutable canonical snapshot. Its data address is stable across invalidations; its fill address includes versions for Remember isolation.

func NewTaggedKey

func NewTaggedKey(base EntryKey, stamps []TagStamp) (TaggedKey, error)

NewTaggedKey is for adapters. Applications construct typed tags and WithTags views. Duplicate keys with conflicting versions and cross-namespace tags fail.

func (TaggedKey) DataKey

func (k TaggedKey) DataKey() EntryKey

DataKey returns the stable payload address, or a zero address for an uninitialized key.

func (TaggedKey) FillKey

func (k TaggedKey) FillKey() EntryKey

FillKey returns the version-specific fill address, or a zero address for an uninitialized key.

func (TaggedKey) Fingerprint

func (k TaggedKey) Fingerprint() [sha256.Size]byte

Fingerprint returns the snapshot digest, or a zero digest for an uninitialized key.

func (TaggedKey) Stamps

func (k TaggedKey) Stamps() []TagStamp

Stamps returns an owned copy of the canonical tag stamps, or nil for an uninitialized key.

func (TaggedKey) Validate

func (k TaggedKey) Validate() error

Validate returns fault.Invalid for an uninitialized key.

func (TaggedKey) VersionFingerprint

func (k TaggedKey) VersionFingerprint() []byte

VersionFingerprint is the canonical concatenation of the snapshot's versions (TagVersionBytes each, in Stamps order). The data address already fixes the tag identities, so this identifies the snapshot exactly. Adapters whose authority cannot compute SHA-256 (such as Redis Lua) persist this instead of Fingerprint, which lets one atomic operation resolve metadata and compare.

type Tags

type Tags[K any] struct {
	// contains filtered or unexported fields
}

Tags retains the declared key type when creating references or invalidating.

func (Tags[K]) For

func (t Tags[K]) For(key K) Tag

For retains the key in a reference whose address is encoded during an operation.

func (Tags[K]) Invalidate

func (t Tags[K]) Invalidate(ctx context.Context, key K) error

Invalidate replaces the key's tag version through the bound store.

Directories

Path Synopsis
Package file implements a bounded persistent cache under an owned os.Root.
Package file implements a bounded persistent cache under an owned os.Root.
Package memory supplies a bounded, concurrency-safe cache backend for local development and deterministic tests.
Package memory supplies a bounded, concurrency-safe cache backend for local development and deterministic tests.
Package null supplies a cache backend that retains nothing, for disabling a cache store without changing its typed declarations.
Package null supplies a cache backend that retains nothing, for disabling a cache store without changing its typed declarations.
Package postgres implements persistent cache using an explicitly migrated PostgreSQL table and borrowed Foundry connection.
Package postgres implements persistent cache using an explicitly migrated PostgreSQL table and borrowed Foundry connection.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL