cache

package
v0.55.2 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package cache provides a generic, capacity-bounded cache with singleflight deduplication and per-hit liveness validation.

Index

Constants

This section is empty.

Variables

View Source
var ErrExpired = errors.New("cache entry expired")

ErrExpired is returned by the check function passed to New to signal that a cached entry has definitively expired and should be evicted.

Functions

This section is empty.

Types

type ValidatingCache

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

ValidatingCache is a node-local write-through cache backed by a capacity-bounded LRU map, with singleflight-deduplicated Get operations and lazy liveness validation on cache hit.

Type parameter K is the key type (must be comparable). Type parameter V is the cached value type.

The entire Get operation — cache hit validation and miss load — runs under a singleflight group so at most one operation executes concurrently per key. Concurrent callers for the same key share the result, coalescing both liveness checks and storage round-trips into a single operation per key.

onEvict runs without cache mutexes held by its invoking goroutine

The user onEvict may perform slow teardown (e.g. closing network connections). The LRU's own eviction callback only *buffers* each evicted entry (bufferEvicted). drainEvictions snapshots the buffer under evictMu, releases it, then invokes onEvict; direct callbacks on discarded miss-load values likewise run without either cache mutex held by their invoking goroutine. A drain may claim entries buffered by another operation, so a slow callback can delay that drainer's return, including one for a different key. Conversely, an operation can return before its own eviction callbacks finish if another drainer claimed them. The invoking goroutine holds neither cache mutex during callbacks, though other goroutines may hold them.

func New

func New[K comparable, V any](
	capacity int,
	load func(context.Context, K) (V, error),
	check func(context.Context, K, V) error,
	onEvict func(K, V),
) *ValidatingCache[K, V]

New creates a ValidatingCache with the given capacity and callbacks.

capacity is the maximum number of entries; it must be >= 1. When the cache is full and a new entry must be stored, the least-recently-used entry is evicted first. Values less than 1 panic.

load is called on a cache miss to restore the value; it must not be nil. check is called on every cache hit to confirm liveness. It receives both the key and the cached value so callers can inspect the value without a separate read. Returning ErrExpired evicts the entry; any other error is transient (cached value returned unchanged). It must not be nil. onEvict is called after any eviction (LRU or expiry); it may be nil. Its invoking goroutine holds neither cache mutex while it runs; see the type doc for drain and return-time semantics.

func (*ValidatingCache[K, V]) Get

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

Get returns the value for key, loading it on a cache miss. The entire operation — cache hit validation and miss load — runs under a singleflight group so at most one operation executes concurrently per key. Concurrent callers for the same key share the result.

ctx is forwarded to the load and check callbacks. When concurrent calls for the same key are coalesced by singleflight, only the first caller's context is forwarded; later callers' contexts are not used.

On a cache hit the entry's liveness is validated via the check function provided to New: ErrExpired evicts the entry and falls through to load; transient errors return the cached value unchanged. On a cache miss, load is called to restore the value.

The returned bool is false whenever the value is unavailable — either because load returned an error or because the key does not exist in the backing store. Callers cannot distinguish these two cases.

func (*ValidatingCache[K, V]) Len

func (c *ValidatingCache[K, V]) Len() int

Len returns the number of entries currently in the cache.

func (*ValidatingCache[K, V]) Remove added in v0.55.0

func (c *ValidatingCache[K, V]) Remove(key K) bool

Remove deletes the entry for key and reports whether one was present.

It mirrors RemoveMatching's lock discipline: removal runs under mu and buffers the eviction; drainEvictions runs after this goroutine releases mu. The callback and return-time semantics are described in the type doc.

Removing an absent key is a no-op returning false, so callers on a termination path may invoke it unconditionally without first probing the cache.

func (*ValidatingCache[K, V]) RemoveMatching added in v0.48.0

func (c *ValidatingCache[K, V]) RemoveMatching(pred func(K, V) bool) int

RemoveMatching evicts every entry for which pred reports true, invoking the user onEvict for each removed entry, and returns the number removed.

pred runs under the cache lock (the same lock Set contends for) and must not call back into the cache. This operation calls drainEvictions after removals complete and this goroutine releases mu. Another drainer may invoke callbacks earlier. See the type doc for callback and return-time semantics. This is intended for infrequent bulk eviction (e.g. reconciling sessions after a backend is removed from the registry), not a hot path.

func (*ValidatingCache[K, V]) Set

func (c *ValidatingCache[K, V]) Set(key K, value V)

Set stores value under key, moving the entry to the MRU position. If the cache is at capacity, the least-recently-used entry is evicted first and its callback is drained without a cache mutex held by this goroutine; see the type doc for return-time semantics.

Jump to

Keyboard shortcuts

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