cache

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: May 26, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package cache is the kit's transport-agnostic key/value cache abstraction.

Implementations:

  • NewMemory[V]() — in-process LRU-ish cache with TTL eviction
  • (future) NewRedis[V](client, prefix) — Redis-backed adapter

Consumers code against Cache[V], pick an implementation at wiring time, and can swap memory ↔ Redis without touching call sites.

Index

Constants

This section is empty.

Variables

View Source
var ErrMiss = errors.New("cache: miss")

ErrMiss is returned by Get when the key is absent or has expired.

Functions

This section is empty.

Types

type Cache

type Cache[V any] interface {
	Get(ctx context.Context, key string) (V, error)
	Set(ctx context.Context, key string, value V, ttl time.Duration) error
	Delete(ctx context.Context, key string) error

	// GetOrLoad fetches the cached value or calls loader on miss, storing the
	// result with ttl before returning. Concurrent loaders for the same key
	// are coalesced: only one loader runs, the rest wait for the result.
	GetOrLoad(ctx context.Context, key string, ttl time.Duration, loader func(ctx context.Context) (V, error)) (V, error)

	// Clear flushes the entire namespace.
	Clear(ctx context.Context) error
}

Cache is the common interface backed by every implementation.

type Codec added in v0.3.0

type Codec[V any] interface {
	Encode(V) ([]byte, error)
	Decode([]byte) (V, error)
}

Codec converts V to/from bytes for storage.

type JSONCodec added in v0.3.0

type JSONCodec[V any] struct{}

JSONCodec is the default Codec — encodes V via encoding/json.

func (JSONCodec[V]) Decode added in v0.3.0

func (JSONCodec[V]) Decode(b []byte) (V, error)

func (JSONCodec[V]) Encode added in v0.3.0

func (JSONCodec[V]) Encode(v V) ([]byte, error)

type Memory

type Memory[V any] struct {
	// contains filtered or unexported fields
}

Memory is an in-process Cache[V] with TTL eviction and single-flight loading. Suitable for low-cardinality / latency-sensitive caches that don't need cross-instance sharing. Thread-safe.

func NewMemory

func NewMemory[V any]() *Memory[V]

func (*Memory[V]) Clear

func (m *Memory[V]) Clear(_ context.Context) error

func (*Memory[V]) Delete

func (m *Memory[V]) Delete(_ context.Context, key string) error

func (*Memory[V]) Get

func (m *Memory[V]) Get(ctx context.Context, key string) (V, error)

func (*Memory[V]) GetOrLoad

func (m *Memory[V]) GetOrLoad(
	ctx context.Context,
	key string,
	ttl time.Duration,
	loader func(ctx context.Context) (V, error),
) (V, error)

func (*Memory[V]) Len

func (m *Memory[V]) Len() int

Len returns the current entry count (live + expired). Useful for tests.

func (*Memory[V]) Set

func (m *Memory[V]) Set(ctx context.Context, key string, value V, ttl time.Duration) error

func (*Memory[V]) SetClock

func (m *Memory[V]) SetClock(now func() time.Time)

SetClock replaces the time source. Use in tests to drive expiry without real sleeps.

type Namespaced added in v0.3.0

type Namespaced[V any] struct {
	// contains filtered or unexported fields
}

Namespaced wraps any Cache[V] with a key prefix so multiple services (or multiple subsystems within one service) can share a physical Redis without colliding on keys.

The wrapped cache's Clear is delegated as-is — implementations are responsible for honouring the prefix internally. The Redis backend already does the right thing (SCAN under prefix); Memory's Clear flushes everything, which is fine for tests but means a Namespaced Memory shares its keyspace across namespaces.

func NewNamespaced added in v0.3.0

func NewNamespaced[V any](inner Cache[V], prefix string) *Namespaced[V]

NewNamespaced returns a wrapper that prepends prefix (followed by ":") to every key before delegating.

func (*Namespaced[V]) Clear added in v0.3.0

func (n *Namespaced[V]) Clear(ctx context.Context) error

func (*Namespaced[V]) Delete added in v0.3.0

func (n *Namespaced[V]) Delete(ctx context.Context, key string) error

func (*Namespaced[V]) Get added in v0.3.0

func (n *Namespaced[V]) Get(ctx context.Context, key string) (V, error)

func (*Namespaced[V]) GetOrLoad added in v0.3.0

func (n *Namespaced[V]) GetOrLoad(
	ctx context.Context,
	key string,
	ttl time.Duration,
	loader func(ctx context.Context) (V, error),
) (V, error)

func (*Namespaced[V]) Set added in v0.3.0

func (n *Namespaced[V]) Set(ctx context.Context, key string, value V, ttl time.Duration) error

type Redis added in v0.3.0

type Redis[V any] struct {
	// contains filtered or unexported fields
}

Redis is a Cache[V] backed by go-redis. Values are JSON-encoded by default; supply WithCodec to pick a different format.

Concurrent loaders for the same key are coalesced in-process via a per-instance singleflight map. Cross-instance coalescing would need a Redis lock (SETNX) — out of scope for the kit-level abstraction; layer it on top when you need it.

func NewRedis added in v0.3.0

func NewRedis[V any](client redis.UniversalClient, opts ...RedisOption[V]) *Redis[V]

NewRedis returns a Cache[V] backed by client. The client is expected to be already configured (URL, auth, pool size); the cache doesn't own its lifecycle.

func (*Redis[V]) Clear added in v0.3.0

func (r *Redis[V]) Clear(ctx context.Context) error

Clear removes every key under the configured prefix. Implemented via SCAN + DEL in batches; safe to call on a live cache but O(n) in keyspace size. No prefix configured means it's a no-op (we refuse to FLUSHDB a shared Redis).

func (*Redis[V]) Delete added in v0.3.0

func (r *Redis[V]) Delete(ctx context.Context, key string) error

func (*Redis[V]) Get added in v0.3.0

func (r *Redis[V]) Get(ctx context.Context, key string) (V, error)

func (*Redis[V]) GetOrLoad added in v0.3.0

func (r *Redis[V]) GetOrLoad(
	ctx context.Context,
	key string,
	ttl time.Duration,
	loader func(ctx context.Context) (V, error),
) (V, error)

func (*Redis[V]) Set added in v0.3.0

func (r *Redis[V]) Set(ctx context.Context, key string, value V, ttl time.Duration) error

type RedisOption added in v0.3.0

type RedisOption[V any] func(*Redis[V])

RedisOption configures a Redis cache.

func WithCodec added in v0.3.0

func WithCodec[V any](c Codec[V]) RedisOption[V]

WithCodec swaps the default JSON codec for a custom one (msgpack, protobuf, …).

func WithKeyPrefix added in v0.3.0

func WithKeyPrefix[V any](prefix string) RedisOption[V]

WithKeyPrefix prepends prefix to every key. The trailing colon is added if missing.

type Tiered added in v0.3.0

type Tiered[V any] struct {

	// OnL1Error is called when an L1 operation fails. Defaults to a
	// no-op; wire to a logger to capture serialization or eviction
	// errors without aborting the call.
	OnL1Error func(op string, err error)

	// L1TTL clamps how long a warmed entry stays in L1. Defaults to
	// the L2 TTL passed to Set/GetOrLoad. Useful when L1 should
	// expire faster than L2 to bound staleness.
	L1TTL time.Duration
	// contains filtered or unexported fields
}

Tiered chains two caches: L1 (fast, local) → L2 (shared, remote). Reads probe L1 first; on miss they probe L2 and warm L1. Writes fan out to both. Failures in L2 surface; failures in L1 are non-fatal and logged via the optional OnL1Error hook.

Typical setup: inmem L1 + redis L2 — sub-µs reads when warm, shared invalidation across pods when cold.

func NewTiered added in v0.3.0

func NewTiered[V any](l1, l2 Cache[V]) *Tiered[V]

NewTiered composes L1 over L2.

func (*Tiered[V]) Clear added in v0.3.0

func (t *Tiered[V]) Clear(ctx context.Context) error

func (*Tiered[V]) Delete added in v0.3.0

func (t *Tiered[V]) Delete(ctx context.Context, key string) error

func (*Tiered[V]) Get added in v0.3.0

func (t *Tiered[V]) Get(ctx context.Context, key string) (V, error)

func (*Tiered[V]) GetOrLoad added in v0.3.0

func (t *Tiered[V]) GetOrLoad(
	ctx context.Context,
	key string,
	ttl time.Duration,
	loader func(ctx context.Context) (V, error),
) (V, error)

func (*Tiered[V]) Set added in v0.3.0

func (t *Tiered[V]) Set(ctx context.Context, key string, value V, ttl time.Duration) error

Jump to

Keyboard shortcuts

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