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
- func FillLeaseKey(fill EntryKey) (lease.Key, error)
- func ValidateBatchKeys(keys []EntryKey) error
- func ValidateFillProof(ctx context.Context, fill EntryKey, proof lease.Proof) error
- func ValidateTagKeys(keys []EntryKey) error
- func ValidateTaggedBatch(keys []TaggedKey) error
- func WithMemo(ctx context.Context) context.Context
- type Backend
- type BatchBackend
- type BatchReadBackend
- type BatchValue
- type Cache
- func (c Cache[K, V]) Add(ctx context.Context, key K, value V, ttl TTL) (bool, error)
- func (c Cache[K, V]) Exists(ctx context.Context, key K) (bool, error)
- func (c Cache[K, V]) Expire(ctx context.Context, key K, ttl TTL) (bool, error)
- func (c Cache[K, V]) Flexible(ctx context.Context, key K, fresh, stale time.Duration, ...) (V, error)
- func (c Cache[K, V]) Forget(ctx context.Context, key K) (bool, error)
- func (c Cache[K, V]) ForgetMany(ctx context.Context, keys ...K) (uint64, error)
- func (c Cache[K, V]) Get(ctx context.Context, key K) (V, bool, error)
- func (c Cache[K, V]) GetMany(ctx context.Context, keys ...K) ([]Lookup[V], error)
- func (c Cache[K, V]) Pull(ctx context.Context, key K) (V, bool, error)
- func (c Cache[K, V]) Put(ctx context.Context, key K, value V, ttl TTL) error
- func (c Cache[K, V]) PutMany(ctx context.Context, ttl TTL, entries ...Entry[K, V]) error
- func (c Cache[K, V]) Remember(ctx context.Context, key K, ttl TTL, loader func(context.Context) (V, error), ...) (V, error)
- func (c Cache[K, V]) WithTags(first Tag, rest ...Tag) (Cache[K, V], error)
- type Codec
- type Config
- type CoordinatedBackend
- type CoordinatedTaggedBackend
- type CoordinationConfig
- type Counter
- func (c Counter[K]) Add(ctx context.Context, key K, value int64, ttl TTL) (bool, error)
- func (c Counter[K]) Decrement(ctx context.Context, key K, amount int64, initialTTL TTL) (int64, error)
- func (c Counter[K]) Exists(ctx context.Context, key K) (bool, error)
- func (c Counter[K]) Expire(ctx context.Context, key K, ttl TTL) (bool, error)
- func (c Counter[K]) Forget(ctx context.Context, key K) (bool, error)
- func (c Counter[K]) ForgetMany(ctx context.Context, keys ...K) (uint64, error)
- func (c Counter[K]) Get(ctx context.Context, key K) (int64, bool, error)
- func (c Counter[K]) Increment(ctx context.Context, key K, delta int64, initialTTL TTL) (int64, error)
- func (c Counter[K]) Put(ctx context.Context, key K, value int64, ttl TTL) error
- func (c Counter[K]) WithTags(first Tag, rest ...Tag) (Counter[K], error)
- type CounterBackend
- type CounterDeclaration
- type Declaration
- type Entry
- type EntryBackend
- type EntryKey
- type Event
- type FlushBackend
- type KeyCodec
- func NewKeyCodec[K any](encode func(K) (string, error)) KeyCodec[K]
- func SignedKeys[K ~int | ~int8 | ~int16 | ~int32 | ~int64]() KeyCodec[K]
- func StringKeys[K ~string]() KeyCodec[K]
- func TextKeys[K encoding.TextMarshaler]() KeyCodec[K]
- func UnsignedKeys[K ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr]() KeyCodec[K]
- type Lookup
- type Name
- type NamedStore
- type Namespace
- type Observer
- type Operation
- type RememberOption
- type Requirements
- type SnapshotReadBackend
- type SnapshotWriteBackend
- type Stats
- type Store
- func (s *Store) Close(ctx context.Context) error
- func (s *Store) Done() <-chan struct{}
- func (s *Store) Invalidate(ctx context.Context) error
- func (s *Store) InvalidateTags(ctx context.Context, first Tag, rest ...Tag) error
- func (s *Store) Namespace() Namespace
- func (s *Store) Require(required Requirements) error
- func (s *Store) Stats() Stats
- type StoreName
- type StoreOption
- type Stores
- type TTL
- type Tag
- type TagDeclaration
- type TagStamp
- type TagVersion
- type TaggedBackend
- type TaggedBatchBackend
- type TaggedBatchReadBackend
- type TaggedCounterBackend
- type TaggedEntryBackend
- type TaggedKey
- type Tags
Constants ¶
const ( MaxMemoEntries = 256 MaxMemoBytes = 1 << 20 )
Memo bounds: a full memo stops recording; reads then use the Store as usual.
const ( KeyFeature keyspace.Feature = "cache" NamespaceKeyFeature keyspace.Feature = "cache-namespace" )
Physical address families include the namespace generation metadata used by tags.
const MaxBatchEntries = 256
MaxBatchEntries bounds input keys before canonicalization. Config may lower it.
const MaxKeyBytes = keyspace.MaxKeyBytes
const MaxLoadTimeout = 24 * time.Hour
MaxLoadTimeout bounds how long a detached Remember loader can own its fill.
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.
const TagVersionBytes = 16
Variables ¶
This section is empty.
Functions ¶
func FillLeaseKey ¶
FillLeaseKey is the adapter address for a fill identity. Tagged callers use TaggedKey.FillKey, so a new tag generation has independent ownership.
func ValidateBatchKeys ¶
ValidateBatchKeys validates an adapter batch's namespace, order and uniqueness. Empty input is valid; the adapter still validates its lifecycle and context.
func ValidateFillProof ¶
ValidateFillProof checks cancellation, local validity and the exact cache-fill binding. Adapters must additionally compare ownership atomically at publication.
func ValidateTagKeys ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
Put replaces one entry with the supplied TTL. No implicit command retry occurs.
func (Cache[K, V]) PutMany ¶
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.
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 ¶
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.
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 ¶
DefaultConfig returns bounded cache defaults for the supplied namespace.
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 ¶
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 ¶
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]) Expire ¶
Expire changes the counter's expiry, preserving the existing exact integer.
func (Counter[K]) ForgetMany ¶
ForgetMany atomically removes a bounded batch of distinct counters in this view.
func (Counter[K]) Get ¶
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.
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 ¶
NewEntryKey is intended for adapter authors. Codecs must distinguish semantic keys.
func NewNamespaceTagKey ¶
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 ¶
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.
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 ¶
KeyCodec preserves the declared key type using the shared keyspace codec.
func NewKeyCodec ¶
NewKeyCodec wraps the supplied typed key encoder.
func SignedKeys ¶
SignedKeys encodes signed integer keys as decimal text.
func StringKeys ¶
StringKeys encodes string keys without changing their text.
func TextKeys ¶
func TextKeys[K encoding.TextMarshaler]() KeyCodec[K]
TextKeys encodes keys through their MarshalText method.
type Lookup ¶
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 ¶
NamedStore binds a typed name to an existing borrowed instance.
type Observer ¶
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.
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 ¶
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 ¶
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 ¶
InvalidateTags atomically invalidates entries containing any selected reference. A remote error may hide an applied invalidation; no implicit retry occurs.
func (*Store) Require ¶
func (s *Store) Require(required Requirements) error
Require returns fault.Invalid if the store lacks any requested capability.
type StoreName ¶
type StoreName string
StoreName identifies one configured cache instance, distinct from other service families.
type StoreOption ¶
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 ¶
Default returns the selected registered store or an error if no default is available.
func (*Stores) DefaultName ¶
DefaultName returns the selected name, or an empty name for a nil registry.
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.
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 ¶
NewTaggedKey is for adapters. Applications construct typed tags and WithTags views. Duplicate keys with conflicting versions and cross-namespace tags fail.
func (TaggedKey) DataKey ¶
DataKey returns the stable payload address, or a zero address for an uninitialized key.
func (TaggedKey) FillKey ¶
FillKey returns the version-specific fill address, or a zero address for an uninitialized key.
func (TaggedKey) Fingerprint ¶
Fingerprint returns the snapshot digest, or a zero digest for an uninitialized key.
func (TaggedKey) Stamps ¶
Stamps returns an owned copy of the canonical tag stamps, or nil for an uninitialized key.
func (TaggedKey) VersionFingerprint ¶
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.
Source Files
¶
- backend.go
- batch_keys.go
- batch_read.go
- batch_write.go
- cache.go
- capabilities.go
- codec.go
- config.go
- coordinated_backend.go
- coordination.go
- counter.go
- counter_backend.go
- counter_declaration.go
- declaration.go
- doc.go
- entry.go
- entry_backend.go
- fill.go
- flexible.go
- invalidate.go
- memo.go
- named.go
- namespace.go
- observer.go
- remember.go
- store.go
- tag.go
- tag_backend.go
- tag_keys.go
- tag_view.go
- tagged_key.go
- ttl.go
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. |