Documentation
¶
Overview ¶
Package cache provides a robust hybrid cache for distributed systems, inspired by ZiggyCreatures FusionCache.
Index ¶
- Variables
- func Get[T any](ctx context.Context, c Cache, key string, opts ...EntryOption) (T, bool, error)
- func GetOrSet[T any](ctx context.Context, c Cache, key string, ...) (T, error)
- type Cache
- type EntryOption
- func WithAllowStaleOnReadOnly() EntryOption
- func WithAutoClone() EntryOption
- func WithBackgroundL2Ops() EntryOption
- func WithDistributedCacheTimeouts(soft, hard time.Duration) EntryOption
- func WithDuration(d time.Duration) EntryOption
- func WithEagerRefresh(threshold float32) EntryOption
- func WithFactoryTimeouts(soft, hard time.Duration) EntryOption
- func WithFailSafe(maxDuration, throttleDuration time.Duration) EntryOption
- func WithJitter(max time.Duration) EntryOption
- func WithLockTimeout(d time.Duration) EntryOption
- func WithPriority(p EvictionPriority) EntryOption
- func WithSize(n int64) EntryOption
- func WithSkipL2() EntryOption
- func WithSkipL2ReadWhenStale() EntryOption
- func WithTags(tags ...string) EntryOption
- type EntryOptions
- type Event
- type EventBackplaneCircuitBreakerStateChange
- type EventBackplaneReceived
- type EventBackplaneSent
- type EventCacheHit
- type EventCacheMiss
- type EventEagerRefreshComplete
- type EventEagerRefreshStarted
- type EventEmitter
- type EventFactoryCall
- type EventFactoryError
- type EventFactorySuccess
- type EventFailSafeActivated
- type EventHandler
- type EventHardTimeoutActivated
- type EventL2CircuitBreakerStateChange
- type EventL2Error
- type EventL2Hit
- type EventL2Miss
- type EventSoftTimeoutActivated
- type EvictionPriority
- type FactoryFunc
- type Option
- func WithBackplane(bp backplane.Backplane) Option
- func WithBackplaneCircuitBreaker(threshold int, openDuration time.Duration) Option
- func WithCacheName(name string) Option
- func WithDefaultEntryOptions(eo EntryOptions) Option
- func WithKeyPrefix(prefix string) Option
- func WithL1(adapter l1.Adapter) Option
- func WithL2(adapter l2.Adapter) Option
- func WithL2CircuitBreaker(threshold int, openDuration time.Duration) Option
- func WithLogger(logger *slog.Logger) Option
- func WithNodeID(id string) Option
- func WithSerializer(s serializer.Serializer) Option
- type Options
Constants ¶
This section is empty.
Variables ¶
var ErrFactoryHardTimeout = fmt.Errorf("cache: factory hard timeout")
ErrFactoryHardTimeout is returned when the factory exceeds the hard timeout and no stale fail safe value is available.
var ErrLockTimeout = fmt.Errorf("cache: stampede lock timeout")
ErrLockTimeout is returned when LockTimeout elapses before the stampede lock is acquired. The caller may proceed without the lock (best-effort).
Functions ¶
Types ¶
type Cache ¶
type Cache interface {
// Get returns the cached value for key if present and not logically expired.
// Returns (nil, false, nil) on a clean cache miss.
Get(ctx context.Context, key string, opts ...EntryOption) (any, bool, error)
// Set stores value under key in all configured cache layers.
Set(ctx context.Context, key string, value any, opts ...EntryOption) error
// GetOrSet returns the cached value for key. On a cache miss it calls factory,
// stores the result, and returns it. This is the primary method.
GetOrSet(ctx context.Context, key string, factory FactoryFunc, opts ...EntryOption) (any, error)
// Delete removes the entry from all cache layers and notifies the backplane.
Delete(ctx context.Context, key string, opts ...EntryOption) error
// DeleteByTag removes all entries associated with tag from all layers.
DeleteByTag(ctx context.Context, tag string, opts ...EntryOption) error
// Expire marks the entry as logically expired without removing it.
// The value remains available as a fail-safe fallback until physical expiry.
// If fail-safe is not enabled, this is equivalent to Delete.
Expire(ctx context.Context, key string, opts ...EntryOption) error
// Clear removes all entries from L1. If clearL2 is true and an L2 adapter
// is configured, it calls Clear on the adapter as well.
Clear(ctx context.Context, clearL2 bool) error
// Name returns the configured cache name.
Name() string
// DefaultEntryOptions returns a copy of the cache-wide default entry options.
DefaultEntryOptions() EntryOptions
// Events returns the EventEmitter for subscribing to cache lifecycle events.
Events() *EventEmitter
// Close shuts down background goroutines and releases resources.
// It is safe to call Close multiple times.
Close() error
}
type EntryOption ¶
type EntryOption func(*EntryOptions)
EntryOption modifies an EntryOptions. Compose multiple options freely.
func WithAllowStaleOnReadOnly ¶
func WithAllowStaleOnReadOnly() EntryOption
func WithAutoClone ¶
func WithAutoClone() EntryOption
func WithBackgroundL2Ops ¶
func WithBackgroundL2Ops() EntryOption
func WithDistributedCacheTimeouts ¶
func WithDistributedCacheTimeouts(soft, hard time.Duration) EntryOption
func WithDuration ¶
func WithDuration(d time.Duration) EntryOption
func WithEagerRefresh ¶
func WithEagerRefresh(threshold float32) EntryOption
func WithFactoryTimeouts ¶
func WithFactoryTimeouts(soft, hard time.Duration) EntryOption
func WithFailSafe ¶
func WithFailSafe(maxDuration, throttleDuration time.Duration) EntryOption
func WithJitter ¶
func WithJitter(max time.Duration) EntryOption
func WithLockTimeout ¶
func WithLockTimeout(d time.Duration) EntryOption
func WithPriority ¶
func WithPriority(p EvictionPriority) EntryOption
func WithSize ¶
func WithSize(n int64) EntryOption
func WithSkipL2 ¶
func WithSkipL2() EntryOption
func WithSkipL2ReadWhenStale ¶
func WithSkipL2ReadWhenStale() EntryOption
func WithTags ¶
func WithTags(tags ...string) EntryOption
type EntryOptions ¶
type EntryOptions struct {
// Duration is how long the entry is considered fresh (logically valid).
// When fail-safe is enabled the physical TTL in backing stores is
// FailSafeMaxDuration; Duration marks the "stale after" boundary.
Duration time.Duration
// DistributedCacheDuration overrides Duration for the L2 layer only.
// Zero means "use Duration".
DistributedCacheDuration time.Duration
// JitterMaxDuration adds a random extra TTL in [0, JitterMaxDuration) to
// both L1 and L2 entries. Prevents thundering-herd expiry spikes across
// nodes in multi-instance deployments. Zero disables jitter.
JitterMaxDuration time.Duration
// IsFailSafeEnabled activates the fail-safe mechanism: if a factory call
// or L2 fetch fails and a stale entry exists, the stale value is returned
// rather than propagating the error.
IsFailSafeEnabled bool
// FailSafeMaxDuration is the total lifetime of an entry in the backing
// store when fail-safe is on. The entry will be physically present (but
// logically stale) for this duration after it was first written, enabling
// it to be used as a fallback.
// Must be >= Duration when fail-safe is enabled.
FailSafeMaxDuration time.Duration
// DistributedCacheFailSafeMaxDuration overrides FailSafeMaxDuration for
// the L2 physical TTL. Zero means "use FailSafeMaxDuration".
DistributedCacheFailSafeMaxDuration time.Duration
// FailSafeThrottleDuration is how long a fail-safe-activated stale value
// is temporarily promoted back to "fresh" in L1 to prevent the factory
// from being hammered again immediately after an error.
FailSafeThrottleDuration time.Duration
// AllowStaleOnReadOnly permits stale (logically expired) values to be
// returned from read-only operations (Get) without triggering a factory call.
AllowStaleOnReadOnly bool
// FactorySoftTimeout is the maximum time to wait for the factory before
// returning a stale fail-safe value to the caller. The factory continues
// running in the background and caches its result when done.
// Zero means no soft timeout.
FactorySoftTimeout time.Duration
// FactoryHardTimeout is the absolute maximum time to wait for the factory.
// After this the call returns an error (or stale value if fail-safe is on),
// regardless of whether a stale value is available.
// Zero means wait indefinitely.
FactoryHardTimeout time.Duration
// AllowTimedOutFactoryBackgroundCompletion: when true, a factory that
// triggered a soft or hard timeout (but eventually succeeds) will have its
// result stored in the cache. Default: true.
AllowTimedOutFactoryBackgroundCompletion bool
// DistributedCacheSoftTimeout is the max time to wait for an L2 read/write
// before falling back to a stale value (fail-safe must be on for a fallback
// to be available). Zero means no soft timeout.
DistributedCacheSoftTimeout time.Duration
// DistributedCacheHardTimeout is the absolute max time for any L2 operation.
// Zero means wait indefinitely.
DistributedCacheHardTimeout time.Duration
// AllowBackgroundDistributedCacheOperations: when true, L2 writes are
// fire-and-forget goroutines. This improves latency but means a write
// failure is logged rather than returned to the caller.
// Default: false (blocking, deterministic behaviour).
AllowBackgroundDistributedCacheOperations bool
// EagerRefreshThreshold: when a cache hit occurs after this fraction of
// Duration has elapsed, a background factory call is started to refresh
// the entry before it expires, so callers never observe a miss.
// Value must be in (0.0, 1.0); zero or values outside this range disable
// eager refresh.
// Example: 0.9 starts refreshing at 90% of Duration elapsed.
EagerRefreshThreshold float32
// SkipBackplaneNotifications: if true, mutations (Set/Delete/Expire) will
// not publish backplane messages for this operation.
SkipBackplaneNotifications bool
// AllowBackgroundBackplaneOperations: when true, backplane publishes are
// fire-and-forget goroutines. Default: true.
AllowBackgroundBackplaneOperations bool
// ReThrowBackplaneExceptions: when true and AllowBackgroundBackplaneOperations
// is false, backplane publish errors are returned to the caller.
ReThrowBackplaneExceptions bool
// ReThrowDistributedCacheExceptions: when true and AllowBackgroundDistributedCacheOperations
// is false, L2 errors are returned to the caller.
ReThrowDistributedCacheExceptions bool
// ReThrowSerializationExceptions: when true, serialization errors during L2
// reads/writes are returned to the caller. Default: true.
ReThrowSerializationExceptions bool
// Priority hints to the L1 eviction policy. Higher priority entries are
// evicted last under memory pressure. Default: PriorityNormal.
Priority EvictionPriority
// Size is an arbitrary weight unit used by L1 when a SizeLimit is configured
// on the cache. Typically represents relative byte size or item weight.
Size int64
// SkipL1Read: bypass reading from the in-process memory cache (L1).
// Use with care, removes stampede protection.
SkipL1Read bool
// SkipL1Write: bypass writing to L1 after a factory call or L2 hit.
SkipL1Write bool
// SkipL2Read: bypass reading from the distributed cache (L2).
SkipL2Read bool
// SkipL2Write: bypass writing to L2.
SkipL2Write bool
// SkipL2ReadWhenStale: when L1 has a stale entry, skip checking L2 for a
// newer version. Useful when L2 is local (not shared across nodes).
SkipL2ReadWhenStale bool
// Tags associates string labels with this entry for bulk invalidation via
// DeleteByTag. Tags are stored in both L1 (in-memory reverse index) and L2
// (implementation-defined, e.g., a Redis SET per tag).
Tags []string
// LockTimeout is the maximum time to wait to acquire the stampede protection
// lock for this key. After this, the caller proceeds without the lock
// (risks a mini-stampede but prevents indefinite starvation).
// Zero means wait indefinitely.
LockTimeout time.Duration
// EnableAutoClone: when true, values returned from L1 are deep-cloned
// (via the Serializer: marshal -> unmarshal) before being returned to the
// caller. Prevents callers from inadvertently mutating cached objects.
// Requires a Serializer to be configured.
EnableAutoClone bool
}
EntryOptions holds all per-entry settings. It is a plain value type; cache copies it on every call so mutations never affect the stored defaults.
type Event ¶
type Event interface {
// contains filtered or unexported methods
}
Event is the marker interface for all cache events.
type EventBackplaneCircuitBreakerStateChange ¶
type EventBackplaneCircuitBreakerStateChange struct{ Open bool }
type EventBackplaneReceived ¶
type EventBackplaneReceived struct {
Key string
Type backplane.MessageType
}
type EventBackplaneSent ¶
type EventBackplaneSent struct {
Key string
Type backplane.MessageType
}
type EventCacheHit ¶
type EventCacheMiss ¶
type EventCacheMiss struct{ Key string }
type EventEagerRefreshComplete ¶
type EventEagerRefreshComplete struct{ Key string }
type EventEagerRefreshStarted ¶
type EventEagerRefreshStarted struct{ Key string }
type EventEmitter ¶
type EventEmitter struct {
// contains filtered or unexported fields
}
EventEmitter is a simple in-process event bus. Safe for concurrent use.
func (*EventEmitter) On ¶
func (e *EventEmitter) On(handler EventHandler) (unsubscribe func())
type EventFactoryCall ¶
type EventFactoryCall struct{ Key string }
type EventFactoryError ¶
type EventFactorySuccess ¶
type EventFactorySuccess struct {
Key string
}
type EventFailSafeActivated ¶
type EventHandler ¶
type EventHandler func(Event)
EventHandler is a callback invoked for each cache event. Handlers are called synchronously on the goroutine that produced the event.
type EventHardTimeoutActivated ¶
type EventHardTimeoutActivated struct{ Key string }
type EventL2CircuitBreakerStateChange ¶
type EventL2CircuitBreakerStateChange struct{ Open bool }
type EventL2Error ¶
type EventL2Hit ¶
type EventL2Hit struct{ Key string }
type EventL2Miss ¶
type EventL2Miss struct{ Key string }
type EventSoftTimeoutActivated ¶
type EventSoftTimeoutActivated struct{ Key string }
type EvictionPriority ¶
type EvictionPriority int
EvictionPriority hints to the L1 eviction policy.
const ( PriorityLow EvictionPriority = -1 PriorityNormal EvictionPriority = 0 // default PriorityHigh EvictionPriority = 1 PriorityNeverRemove EvictionPriority = 2 )
type FactoryFunc ¶
FactoryFunc is the function called on a cache miss to produce a fresh value. It receives the request context and should respect cancellation.
type Option ¶
type Option func(*Options)
Option configures an Options at construction time.
func WithBackplane ¶
func WithCacheName ¶
func WithDefaultEntryOptions ¶
func WithDefaultEntryOptions(eo EntryOptions) Option
func WithKeyPrefix ¶
func WithL2CircuitBreaker ¶
func WithLogger ¶
func WithNodeID ¶
func WithSerializer ¶
func WithSerializer(s serializer.Serializer) Option
type Options ¶
type Options struct {
// CacheName identifies this cache instance in logs, events, and backplane messages.
// Default: "default"
CacheName string
// KeyPrefix is prepended to every key before it is passed to L1, L2, and the
// backplane. Enables namespace isolation when multiple caches share an L2 backend.
// Example: "myapp:products:"
KeyPrefix string
// DefaultEntryOptions is the baseline EntryOptions for every cache operation.
// Per-call EntryOption funcs are applied on top of a copy of this value.
DefaultEntryOptions EntryOptions
// L1 is the in-process memory cache adapter.
// If nil, a default sync.Map-backed adapter is used.
L1 l1.Adapter
// L2 is the distributed cache adapter.
// If nil, cache operates as a pure in-process memory cache.
L2 l2.Adapter
// Serializer is required when L2 is non-nil.
// It encodes/decodes Go values to/from []byte for L2 storage.
Serializer serializer.Serializer
// Backplane enables inter-node invalidation.
// If nil, no cross-node notifications are sent or received.
Backplane backplane.Backplane
// Logger is the structured logger for internal diagnostics.
// If nil, all logging is silently discarded.
Logger *slog.Logger
// NodeID uniquely identifies this cache node in backplane messages.
// Used to suppress processing of self-sent notifications.
// If empty, a random UUID is generated at construction time.
NodeID string
// DistributedCacheCircuitBreakerThreshold is the number of consecutive L2
// errors that cause the circuit breaker to open (reject further L2 calls).
// 0 disables the L2 circuit breaker entirely.
DistributedCacheCircuitBreakerThreshold int
// DistributedCacheCircuitBreakerDuration is how long the L2 circuit
// breaker stays open before attempting recovery.
DistributedCacheCircuitBreakerDuration time.Duration
// BackplaneCircuitBreakerThreshold is the consecutive-failure threshold
// for the backplane circuit breaker. 0 disables it.
BackplaneCircuitBreakerThreshold int
// BackplaneCircuitBreakerDuration is how long the backplane circuit
// breaker stays open. Only relevant when threshold > 0.
BackplaneCircuitBreakerDuration time.Duration
// BackplaneAutoRecovery enables automatic L1 re-sync when the backplane
// reconnects after an outage. Default: true.
BackplaneAutoRecovery bool
// AutoRecoveryMaxRetries is the maximum number of recovery attempts.
// Default: 10.
AutoRecoveryMaxRetries int
// AutoRecoveryDelay is the pause between recovery attempts.
// Default: 2s.
AutoRecoveryDelay time.Duration
// IgnoreIncomingBackplaneNotifications disables acting on received backplane
// messages. Useful for read-only replicas or during testing.
IgnoreIncomingBackplaneNotifications bool
// SkipL2OnError: if true (default), L2 errors are logged and swallowed.
// cache continues with L1 only. If false, L2 errors propagate to callers
// unless overridden by the per-entry ReThrowDistributedCacheExceptions flag.
SkipL2OnError bool
}
Options configures the entire cache instance. Set once via Option funcs passed to New(). Never mutate after construction.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
adapters
|
|
|
backplane/memory
Package memory provides an in-process backplane using Go channels.
|
Package memory provides an in-process backplane using Go channels. |
|
backplane/noop
Package noop provides a backplane that silently discards all messages.
|
Package noop provides a backplane that silently discards all messages. |
|
l2/memory
Package memory is an implementation of the L2 cache using an in-memory L2 cache, backed by sync.Map.
|
Package memory is an implementation of the L2 cache using an in-memory L2 cache, backed by sync.Map. |
|
serializer/json
Package json is an implementation of the cache Serializer using encoding/json.
|
Package json is an implementation of the cache Serializer using encoding/json. |
|
backplane/redis
module
|
|
|
l2/redis
module
|
|
|
Package backplane provides the interfaces for implementing a backplane in the core cache package.
|
Package backplane provides the interfaces for implementing a backplane in the core cache package. |
|
internal
|
|
|
clock
Package clock provides a time abstraction so cache internals can be tested without sleeping or time.Sleep-based races.
|
Package clock provides a time abstraction so cache internals can be tested without sleeping or time.Sleep-based races. |
|
Package l2 provides the adapter interface for the core cache package.
|
Package l2 provides the adapter interface for the core cache package. |
|
Package serializer provides the Serializer interface for the core cache package.
|
Package serializer provides the Serializer interface for the core cache package. |