Documentation
¶
Overview ¶
Package crema provides a probabilistic cache with revalidation and loaders.
The cache can deduplicate concurrent loads via singleflight. Use WithMaxLoadTimeout to cap the execution time of singleflight loaders and their synchronous cache writebacks. When WithDirectLoader is used, the max load timeout is ignored and loaders run with the caller context.
The default singleflight load path runs in an internal goroutine and crema does not recover panics. Loaders, callbacks, metrics providers, and cache providers supplied by the application must therefore avoid panics or recover them at their own boundary. WithDirectLoader runs the loader in the caller's goroutine.
By default a still-valid cached value is served when a revalidation load fails, instead of propagating the loader error. Use WithRevalidationFallback(false) to always propagate loader errors.
Load results are not cached by default. WithNegativeCacheProvider caches results that mean a value is absent, while WithLoadErrorCacheProvider caches selected errors that do not mean absence. Both use an independent TTL.
Index ¶
- Constants
- Variables
- func WithDirectLoader() func(*cacheConfig)
- func WithLoadErrorCacheProvider(provider CacheProvider[error], ttl time.Duration, ...) func(*cacheConfig)
- func WithLogger(logger *slog.Logger) func(*cacheConfig)
- func WithMaxLoadTimeout(duration time.Duration) func(*cacheConfig)
- func WithMetricsProvider(metrics MetricsProvider) func(*cacheConfig)
- func WithRevalidationFallback(enabled bool) func(*cacheConfig)
- func WithRevalidationWindow(duration time.Duration) func(*cacheConfig)
- type BaseMetricsProvider
- func (BaseMetricsProvider) RecordCacheDelete(context.Context)
- func (BaseMetricsProvider) RecordCacheGet(context.Context)
- func (BaseMetricsProvider) RecordCacheHit(context.Context)
- func (BaseMetricsProvider) RecordCacheSet(context.Context)
- func (BaseMetricsProvider) RecordLoad(context.Context)
- func (BaseMetricsProvider) RecordLoadConcurrency(context.Context, int)
- func (BaseMetricsProvider) RecordLoadError(context.Context)
- func (BaseMetricsProvider) RecordLoadReason(context.Context, LoadReason)
- type BinaryCompressionOption
- type BufferEncoder
- type BufferReleasePolicy
- type Cache
- type CacheLoadFunc
- type CacheLoadResult
- type CacheObject
- type CacheOption
- type CacheProvider
- type CacheStorageCodec
- type JSONByteStringCodec
- func (j JSONByteStringCodec[V]) CanReleaseBufferOnDecode() bool
- func (j JSONByteStringCodec[V]) Decode(data []byte) (CacheObject[V], error)
- func (j JSONByteStringCodec[V]) Encode(value CacheObject[V]) ([]byte, error)
- func (j JSONByteStringCodec[V]) EncodeTo(buf *bytes.Buffer, value CacheObject[V]) error
- type LoadErrorCacheErrorPredicate
- type LoadReason
- type MetricsProvider
- type NegativeCacheMetricsProvider
- type NegativeCachePredicate
- type NoopCacheProvider
- type NoopCacheStorageCodec
- type NoopMetricsProvider
Examples ¶
Constants ¶
const ( // DefaultCompressThresholdBytes is the default threshold size // above which values are compressed in BinaryCompressionCodec. DefaultCompressThresholdBytes = 1024 * 2 // 2 KiB CompressionTypeIDNone byte = 0x00 CompressionTypeIDZlib byte = 0x01 )
Variables ¶
Functions ¶
func WithDirectLoader ¶
func WithDirectLoader() func(*cacheConfig)
WithDirectLoader disables singleflight and calls loaders directly.
func WithLoadErrorCacheProvider ¶ added in v1.2.0
func WithLoadErrorCacheProvider( provider CacheProvider[error], ttl time.Duration, shouldCache LoadErrorCacheErrorPredicate, ) func(*cacheConfig)
WithLoadErrorCacheProvider caches errors selected by shouldCache through provider.
Example ¶
provider := &testMemoryProvider[int]{items: make(map[string]CacheObject[int])}
negativeProvider := &testLoadErrorProvider{entries: make(map[string]error)}
cache := NewCache(
provider,
NoopCacheStorageCodec[int]{},
WithLoadErrorCacheProvider(negativeProvider, time.Second, func(error) bool { return true }),
)
backendDown := errors.New("backend down")
calls := 0
loader := func(context.Context) (int, error) {
calls++
return 0, backendDown
}
for range 3 {
_, err := cache.GetOrLoad(context.Background(), "answer", time.Minute, loader)
fmt.Println(errors.Is(err, backendDown))
}
fmt.Println("loader calls:", calls)
Output: true true true loader calls: 1
func WithLogger ¶
WithLogger overrides the default logger used for cache warnings.
func WithMaxLoadTimeout ¶
WithMaxLoadTimeout sets the maximum duration allowed for singleflight loader execution, including its synchronous cache writeback. A non-positive duration disables the timeout.
func WithMetricsProvider ¶
func WithMetricsProvider(metrics MetricsProvider) func(*cacheConfig)
WithMetricsProvider overrides the default metrics provider.
func WithRevalidationFallback ¶ added in v1.2.0
func WithRevalidationFallback(enabled bool) func(*cacheConfig)
WithRevalidationFallback controls whether GetOrLoad returns a still-valid cached value when revalidation fails. It is enabled by default.
func WithRevalidationWindow ¶
WithRevalidationWindow sets how long before expiry probabilistic revalidation starts. A zero duration disables probabilistic revalidation.
Types ¶
type BaseMetricsProvider ¶
type BaseMetricsProvider struct{}
BaseMetricsProvider is a no-op metrics implementation.
func (BaseMetricsProvider) RecordCacheDelete ¶
func (BaseMetricsProvider) RecordCacheDelete(context.Context)
func (BaseMetricsProvider) RecordCacheGet ¶
func (BaseMetricsProvider) RecordCacheGet(context.Context)
func (BaseMetricsProvider) RecordCacheHit ¶
func (BaseMetricsProvider) RecordCacheHit(context.Context)
func (BaseMetricsProvider) RecordCacheSet ¶
func (BaseMetricsProvider) RecordCacheSet(context.Context)
func (BaseMetricsProvider) RecordLoad ¶
func (BaseMetricsProvider) RecordLoad(context.Context)
func (BaseMetricsProvider) RecordLoadConcurrency ¶
func (BaseMetricsProvider) RecordLoadConcurrency(context.Context, int)
func (BaseMetricsProvider) RecordLoadError ¶ added in v1.2.0
func (BaseMetricsProvider) RecordLoadError(context.Context)
func (BaseMetricsProvider) RecordLoadReason ¶ added in v1.2.0
func (BaseMetricsProvider) RecordLoadReason(context.Context, LoadReason)
type BinaryCompressionOption ¶ added in v1.2.0
type BinaryCompressionOption func(*binaryCompressionConfig)
BinaryCompressionOption configures a BinaryCompressionCodec.
func WithMaxDecompressedBytes ¶ added in v1.2.0
func WithMaxDecompressedBytes(maxBytes int64) BinaryCompressionOption
WithMaxDecompressedBytes limits the size of zlib data after decompression. A non-positive value disables the limit. The default is disabled.
type BufferEncoder ¶ added in v1.2.0
type BufferEncoder[V any] interface { // EncodeTo appends the encoded cache object to buf. EncodeTo(buf *bytes.Buffer, value CacheObject[V]) error }
BufferEncoder encodes a cache object into a caller-provided buffer. On error, EncodeTo must restore buf's original length.
type BufferReleasePolicy ¶
type BufferReleasePolicy interface {
CanReleaseBufferOnDecode() bool
}
BufferReleasePolicy declares whether Decode can safely release buffer-backed input.
type Cache ¶
type Cache[V any] interface { // Get returns the cached entry for key. Get(ctx context.Context, key string) (CacheObject[V], bool, error) // Set stores a cached entry for key. Set(ctx context.Context, key string, value CacheObject[V]) error // Delete removes a cached entry for key. Delete(ctx context.Context, key string) error // GetOrLoad returns a cached value or uses loader when missing or revalidating. GetOrLoad(ctx context.Context, key string, ttl time.Duration, loader CacheLoadFunc[V]) (V, error) }
Cache coordinates probabilistic revalidation with optional singleflight loading. Implementations are safe for concurrent use as long as CacheProvider and CacheStorageCodec implementations are goroutine-safe. Use NewCache to construct an implementation.
Example ¶
provider := &testMemoryProvider[int]{items: make(map[string]CacheObject[int])}
codec := NoopCacheStorageCodec[int]{}
cache := NewCache(provider, codec)
value, err := cache.GetOrLoad(context.Background(), "answer", time.Minute, func(ctx context.Context) (int, error) {
// Database or computation logic here
return 42, nil
})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(value)
Output: 42
func NewCache ¶
func NewCache[V any, S any](provider CacheProvider[S], codec CacheStorageCodec[V, S], opts ...CacheOption[V]) Cache[V]
NewCache constructs a Cache with defaults and optional overrides.
type CacheLoadFunc ¶
CacheLoadFunc loads a value when it is missing or needs revalidation.
type CacheLoadResult ¶ added in v1.2.0
CacheLoadResult is the value and error returned by a cache loader.
type CacheObject ¶
type CacheObject[V any] struct { // Value is the cached value. Value V // ExpireAtMillis is the absolute expiration time in milliseconds since epoch. ExpireAtMillis int64 }
CacheObject wraps a cached value with its absolute expiration time.
type CacheOption ¶
type CacheOption[V any] func(*cacheConfig)
CacheOption[V] configures a Cache for values of type V.
func WithNegativeCacheProvider ¶ added in v1.2.0
func WithNegativeCacheProvider[V any]( provider CacheProvider[CacheLoadResult[V]], ttl time.Duration, isNegative NegativeCachePredicate[V], ) CacheOption[V]
WithNegativeCacheProvider caches loader results selected by isNegative as absent values. The provider value type V must match the value type used by NewCache.
type CacheProvider ¶
type CacheProvider[S any] interface { // Get retrieves a value from the cache by key. Get(ctx context.Context, key string) (S, bool, error) // Set stores a value in the cache with the specified key. Set(ctx context.Context, key string, value S, ttl time.Duration) error // Delete removes a value from the cache by key. Delete(ctx context.Context, key string) error }
CacheProvider abstracts storage for encoded cache entries. Implementations must be safe for concurrent use by multiple goroutines.
type CacheStorageCodec ¶
type CacheStorageCodec[V any, S any] interface { // Encode returns the cache object encoded into storage value. Encode(value CacheObject[V]) (S, error) // Decode reads the storage value into a cache object. Decode(data S) (CacheObject[V], error) }
CacheStorageCodec encodes and decodes cache objects to storage values. Implementations must be safe for concurrent use by multiple goroutines.
func NewBinaryCompressionCodec ¶
func NewBinaryCompressionCodec[V any]( inner CacheStorageCodec[V, []byte], compressThresholdBytes int, opts ...BinaryCompressionOption, ) CacheStorageCodec[V, []byte]
NewBinaryCompressionCodec returns a codec that conditionally compresses encoded values with zlib when they reach the threshold. A threshold of 0 always compresses, and a negative threshold disables compression.
type JSONByteStringCodec ¶
type JSONByteStringCodec[V any] struct{}
JSONByteStringCodec marshals cache objects as JSON bytes.
func (JSONByteStringCodec[V]) CanReleaseBufferOnDecode ¶
func (j JSONByteStringCodec[V]) CanReleaseBufferOnDecode() bool
func (JSONByteStringCodec[V]) Decode ¶
func (j JSONByteStringCodec[V]) Decode(data []byte) (CacheObject[V], error)
Decode unmarshals JSON bytes into a cache object.
func (JSONByteStringCodec[V]) Encode ¶
func (j JSONByteStringCodec[V]) Encode(value CacheObject[V]) ([]byte, error)
Encode marshals the cache object into JSON bytes without a trailing newline.
func (JSONByteStringCodec[V]) EncodeTo ¶ added in v1.2.0
func (j JSONByteStringCodec[V]) EncodeTo(buf *bytes.Buffer, value CacheObject[V]) error
EncodeTo appends the cache object as JSON bytes to buf without a trailing newline.
type LoadErrorCacheErrorPredicate ¶ added in v1.2.0
LoadErrorCacheErrorPredicate reports whether a load error should be cached. Implementations must be safe for concurrent use.
type LoadReason ¶ added in v1.2.0
type LoadReason int
LoadReason describes what triggered a load.
const ( // LoadReasonMiss indicates that no usable cache entry was found. LoadReasonMiss LoadReason = iota // LoadReasonExpired indicates that the cached entry was already expired. LoadReasonExpired // LoadReasonRevalidation indicates that the cached entry was still valid but // was picked up by probabilistic revalidation. LoadReasonRevalidation // LoadReasonGetError indicates that reading or decoding the cached entry failed. LoadReasonGetError )
func (LoadReason) String ¶ added in v1.2.0
func (r LoadReason) String() string
String returns a stable, metric-friendly name for the reason.
type MetricsProvider ¶
type MetricsProvider interface {
// RecordCacheHit is called when a cached value is successfully returned.
RecordCacheHit(ctx context.Context)
// RecordCacheGet is called when a cache lookup is attempted.
RecordCacheGet(ctx context.Context)
// RecordCacheSet is called when a cache write is attempted.
RecordCacheSet(ctx context.Context)
// RecordCacheDelete is called when a cache delete is attempted.
RecordCacheDelete(ctx context.Context)
// RecordLoad is called when a load is started by the leader.
RecordLoad(ctx context.Context)
// RecordLoadReason is called with the reason that triggered the load.
RecordLoadReason(ctx context.Context, reason LoadReason)
// RecordLoadError is called once when a loader execution fails.
RecordLoadError(ctx context.Context)
// RecordLoadConcurrency is called when a load finishes with the number of
// callers sharing that loader execution.
RecordLoadConcurrency(ctx context.Context, concurrency int)
}
MetricsProvider receives cache and loader events for instrumentation. Implementations must be safe for concurrent use and should avoid blocking.
Load latency is intentionally not reported here; wrap your CacheLoadFunc to measure it with your APM.
Embed BaseMetricsProvider to implement only the callbacks you need.
type NegativeCacheMetricsProvider ¶ added in v1.2.0
type NegativeCacheMetricsProvider interface {
MetricsProvider
RecordNegativeCacheHit(ctx context.Context)
RecordNegativeCacheSet(ctx context.Context)
}
NegativeCacheMetricsProvider optionally receives negative-cache events.
type NegativeCachePredicate ¶ added in v1.2.0
NegativeCachePredicate reports whether a loader result represents an absent value. Implementations must be safe for concurrent use.
type NoopCacheProvider ¶ added in v0.1.9
type NoopCacheProvider[S any] struct{}
NoopCacheProvider is a cache provider that does nothing. All Get calls return a cache miss, and Set/Delete calls are no-ops. Useful for tests or when caching should be explicitly disabled.
func NewNoopCacheProvider ¶ added in v0.1.9
func NewNoopCacheProvider[S any]() *NoopCacheProvider[S]
NewNoopCacheProvider constructs a NoopCacheProvider.
func (*NoopCacheProvider[S]) Delete ¶ added in v0.1.9
func (n *NoopCacheProvider[S]) Delete(_ context.Context, _ string) error
Delete does nothing and returns nil.
type NoopCacheStorageCodec ¶
type NoopCacheStorageCodec[V any] struct{}
NoopCacheStorageCodec passes CacheObject values through without encoding.
func (NoopCacheStorageCodec[V]) Decode ¶
func (n NoopCacheStorageCodec[V]) Decode(data CacheObject[V]) (CacheObject[V], error)
Decode copies the cache object.
func (NoopCacheStorageCodec[V]) Encode ¶
func (n NoopCacheStorageCodec[V]) Encode(value CacheObject[V]) (CacheObject[V], error)
Encode copies the cache object.
type NoopMetricsProvider ¶
type NoopMetricsProvider struct {
BaseMetricsProvider
}
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
plot-revalidation
command
Command plot-revalidation generates an SVG plot of the revalidation probability curves.
|
Command plot-revalidation generates an SVG plot of the revalidation probability curves. |
|
ext
|
|
|
go-json
module
|
|
|
golang-lru
module
|
|
|
gomemcache
module
|
|
|
madvfree
module
|
|
|
protobuf
module
|
|
|
ristretto
module
|
|
|
rueidis
module
|
|
|
valkey-go
module
|