Documentation
¶
Overview ¶
Package cache provides generic, thread-safe in-memory caches with first-class support for negative (not-found) entries and per-key TTLs.
Two cache types are exposed:
Memory[K, V] is a single-key cache that maps a comparable key K to a value V. It keeps positive (value) and negative (not-found) entries in separate tables, so callers can distinguish "we never asked" from "we asked and the source had nothing".
MemoryMultiCache[V] stores a single value V that can be looked up via multiple independent indexes (for example, by ID and by Code). Indexes are kept consistent automatically: updating a value rewrites all index entries it touches, deleting via one index removes the value from every index it occupies, and each index has its own negative table.
Both caches run an optional background janitor that removes expired entries. Call Close when done to stop it.
Lookup states ¶
Lookups return one of three states:
StatusHit value is cached and valid; use it StatusNotFound source was checked previously and had no such key StatusMiss cache has no information; query the source
The classic two-value Get method collapses Miss and NotFound to (zero, false). Use Lookup if you need to tell them apart, e.g. to skip a database round-trip.
Value semantics ¶
V is stored as-is. Pointer types are shared; struct values are copied at Set and at Get time. Concurrent mutation of pointer values is the caller's responsibility — the cache's mutex protects only its internal maps.
Index ¶
- Variables
- type IndexFunc
- type Memory
- func (c *Memory[K, V]) Clear()
- func (c *Memory[K, V]) ClearNotFound()
- func (c *Memory[K, V]) Close()
- func (c *Memory[K, V]) Delete(key K)
- func (c *Memory[K, V]) Get(key K) (V, bool)
- func (c *Memory[K, V]) Items() map[K]V
- func (c *Memory[K, V]) Keys() []K
- func (c *Memory[K, V]) Len() int
- func (c *Memory[K, V]) Lookup(key K) (V, Status)
- func (c *Memory[K, V]) MarkNotFound(key K)
- func (c *Memory[K, V]) MarkNotFoundWithTTL(key K, ttl time.Duration)
- func (c *Memory[K, V]) NotFoundKeys() []K
- func (c *Memory[K, V]) NotFoundLen() int
- func (c *Memory[K, V]) Set(key K, value V)
- func (c *Memory[K, V]) SetWithTTL(key K, value V, ttl time.Duration)
- func (c *Memory[K, V]) Values() []V
- type MemoryMultiCache
- func (mc *MemoryMultiCache[V]) Clear()
- func (mc *MemoryMultiCache[V]) ClearNotFound(indexName string) error
- func (mc *MemoryMultiCache[V]) Close()
- func (mc *MemoryMultiCache[V]) Delete(indexName string, key any) (bool, error)
- func (mc *MemoryMultiCache[V]) DeleteValue(value V) bool
- func (mc *MemoryMultiCache[V]) Get(indexName string, key any) (V, bool)
- func (mc *MemoryMultiCache[V]) IndexNames() []string
- func (mc *MemoryMultiCache[V]) Keys(indexName string) ([]any, error)
- func (mc *MemoryMultiCache[V]) Len() int
- func (mc *MemoryMultiCache[V]) Lookup(indexName string, key any) (V, Status, error)
- func (mc *MemoryMultiCache[V]) MarkNotFound(indexName string, key any) error
- func (mc *MemoryMultiCache[V]) MarkNotFoundWithTTL(indexName string, key any, ttl time.Duration) error
- func (mc *MemoryMultiCache[V]) NotFoundLen(indexName string) (int, error)
- func (mc *MemoryMultiCache[V]) Set(value V)
- func (mc *MemoryMultiCache[V]) SetWithTTL(value V, ttl time.Duration)
- func (mc *MemoryMultiCache[V]) Values() []V
- type Status
Constants ¶
This section is empty.
Variables ¶
var ErrUnknownIndex = errors.New("cache: unknown index")
ErrUnknownIndex is returned when an index name is used that was not registered at construction time.
Functions ¶
This section is empty.
Types ¶
type IndexFunc ¶
IndexFunc derives an index key from a value. Return ok=false to skip this index for the given value (e.g. an optional field is empty); the value is still stored, just not reachable through this index.
type Memory ¶
type Memory[K comparable, V any] struct { // contains filtered or unexported fields }
Memory is a generic, thread-safe in-memory cache that keeps positive (value) and negative (not-found) entries in separate tables.
func NewMemory ¶
func NewMemory[K comparable, V any](ttl, negTTL time.Duration) *Memory[K, V]
NewMemory creates a new cache with the given TTLs.
- ttl: lifetime of positive entries. 0 disables expiry.
- negTTL: lifetime of negative entries. 0 disables expiry.
A background janitor goroutine runs if at least one TTL is > 0. Call Close() when done.
func (*Memory[K, V]) Clear ¶
func (c *Memory[K, V]) Clear()
Clear removes all entries (positive and negative).
func (*Memory[K, V]) ClearNotFound ¶
func (c *Memory[K, V]) ClearNotFound()
ClearNotFound removes only the negative entries. Useful after a bulk insert into the underlying source, when negative entries should be re-evaluated.
func (*Memory[K, V]) Close ¶
func (c *Memory[K, V]) Close()
Close stops the janitor goroutine. Safe to call multiple times.
func (*Memory[K, V]) Delete ¶
func (c *Memory[K, V]) Delete(key K)
Delete removes the key from both positive and negative tables.
func (*Memory[K, V]) Get ¶
Get is a classic two-value cache API. It returns (value, true) only on StatusHit; both Miss and NotFound collapse to (zero, false). Use Lookup when you need to distinguish those two cases.
func (*Memory[K, V]) Items ¶
func (c *Memory[K, V]) Items() map[K]V
Items returns a snapshot map of all valid positive entries.
func (*Memory[K, V]) Keys ¶
func (c *Memory[K, V]) Keys() []K
Keys returns the keys of all valid positive entries.
func (*Memory[K, V]) Lookup ¶
Lookup returns the cached state for the key. Three outcomes:
- StatusHit: value is returned.
- StatusNotFound: key is known to be missing in the source.
- StatusMiss: nothing is known; query the source.
For non-Hit outcomes, value is the zero value.
func (*Memory[K, V]) MarkNotFound ¶
func (c *Memory[K, V]) MarkNotFound(key K)
MarkNotFound records the key as confirmed-missing using the default negative TTL. Any positive entry for the same key is removed.
func (*Memory[K, V]) MarkNotFoundWithTTL ¶
MarkNotFoundWithTTL records a not-found entry with the given TTL. ttl=0 means no expiry.
func (*Memory[K, V]) NotFoundKeys ¶
func (c *Memory[K, V]) NotFoundKeys() []K
NotFoundKeys returns the keys of all valid negative entries.
func (*Memory[K, V]) NotFoundLen ¶
NotFoundLen returns the number of valid negative entries.
func (*Memory[K, V]) Set ¶
func (c *Memory[K, V]) Set(key K, value V)
Set stores the value with the default TTL. Any existing not-found entry for the same key is removed.
func (*Memory[K, V]) SetWithTTL ¶
SetWithTTL stores the value with the given TTL. ttl=0 means no expiry.
type MemoryMultiCache ¶
type MemoryMultiCache[V any] struct { // contains filtered or unexported fields }
MemoryMultiCache stores values that can be looked up through multiple independent indexes (for example: by ID and by Code). It is generic over the value type V; index keys flow through `any` because each index may use a different key type.
Consistency guarantees:
- Set updates all indexes atomically. If a new value collides with an existing entry on any index, the old entry's other index keys are removed too — there are no stale entries.
- Delete removes the value from every index it is registered under.
- Each index maintains its own not-found set.
func NewMemoryMultiCache ¶
func NewMemoryMultiCache[V any]( ttl, negTTL time.Duration, extractors map[string]IndexFunc[V], ) *MemoryMultiCache[V]
NewMemoryMultiCache creates a new multi-index cache.
- ttl: default TTL for positive entries (0 = no expiry)
- negTTL: default TTL for negative entries (0 = no expiry)
- extractors: map of index name to key extractor; at least one required.
func (*MemoryMultiCache[V]) Clear ¶
func (mc *MemoryMultiCache[V]) Clear()
Clear removes everything (positive and negative across all indexes).
func (*MemoryMultiCache[V]) ClearNotFound ¶
func (mc *MemoryMultiCache[V]) ClearNotFound(indexName string) error
ClearNotFound removes negative entries on the given index. Pass an empty string to clear negatives across all indexes.
func (*MemoryMultiCache[V]) Close ¶
func (mc *MemoryMultiCache[V]) Close()
Close stops the janitor goroutine. Safe to call multiple times.
func (*MemoryMultiCache[V]) Delete ¶
func (mc *MemoryMultiCache[V]) Delete(indexName string, key any) (bool, error)
Delete removes the value reachable via (indexName, key) from every index it occupies. Returns true if a value was removed.
func (*MemoryMultiCache[V]) DeleteValue ¶
func (mc *MemoryMultiCache[V]) DeleteValue(value V) bool
DeleteValue removes the value from every index it occupies, by recomputing its index keys with the registered extractors. Useful when the caller has the value object directly.
func (*MemoryMultiCache[V]) Get ¶
func (mc *MemoryMultiCache[V]) Get(indexName string, key any) (V, bool)
Get is the two-value convenience form of Lookup. Returns (zero, false) for both Miss and NotFound. Returns (zero, false) and silently ignores unknown index names — use Lookup if you need to detect that.
func (*MemoryMultiCache[V]) IndexNames ¶
func (mc *MemoryMultiCache[V]) IndexNames() []string
IndexNames returns the registered index names.
func (*MemoryMultiCache[V]) Keys ¶
func (mc *MemoryMultiCache[V]) Keys(indexName string) ([]any, error)
Keys returns the keys present on the given index, for valid entries only.
func (*MemoryMultiCache[V]) Len ¶
func (mc *MemoryMultiCache[V]) Len() int
Len returns the number of valid (non-expired) values stored.
func (*MemoryMultiCache[V]) Lookup ¶
func (mc *MemoryMultiCache[V]) Lookup(indexName string, key any) (V, Status, error)
Lookup returns the cached state for (indexName, key).
func (*MemoryMultiCache[V]) MarkNotFound ¶
func (mc *MemoryMultiCache[V]) MarkNotFound(indexName string, key any) error
MarkNotFound records a not-found entry on the given index using the default negative TTL. If a positive entry exists at that index key, it is removed (along with its other index entries).
func (*MemoryMultiCache[V]) MarkNotFoundWithTTL ¶
func (mc *MemoryMultiCache[V]) MarkNotFoundWithTTL(indexName string, key any, ttl time.Duration) error
MarkNotFoundWithTTL is like MarkNotFound with a per-call TTL override.
func (*MemoryMultiCache[V]) NotFoundLen ¶
func (mc *MemoryMultiCache[V]) NotFoundLen(indexName string) (int, error)
NotFoundLen returns the number of valid negative entries on the given index.
func (*MemoryMultiCache[V]) Set ¶
func (mc *MemoryMultiCache[V]) Set(value V)
Set stores the value, registering it under every index whose extractor returns ok=true. Existing entries colliding on any index are fully removed (from all their indexes) before the new value is inserted.
func (*MemoryMultiCache[V]) SetWithTTL ¶
func (mc *MemoryMultiCache[V]) SetWithTTL(value V, ttl time.Duration)
SetWithTTL is like Set but with a per-call TTL override. ttl=0 = no expiry.
func (*MemoryMultiCache[V]) Values ¶
func (mc *MemoryMultiCache[V]) Values() []V
Values returns a snapshot of all valid values.
type Status ¶
type Status int
Status represents the result of a cache lookup.
const ( // StatusMiss: the cache has no information about the key. The caller // should query the underlying source (e.g. database). StatusMiss Status = iota // StatusHit: the key exists and a valid value is returned. StatusHit // StatusNotFound: a previous lookup confirmed the key does not exist // in the underlying source. Caller should not query again until the // negative entry expires. StatusNotFound )