Documentation
¶
Overview ¶
Package tiered composes two cache.Cache backends into a single two-level (L1 + L2) read-through / write-through cache.
The canonical shape is a fast in-process L1 (drops/cache/memory) in front of a shared, network L2 (drops/cache/redis or drops/cache/memcached):
l1 := memory.New(memory.Options{MaxEntries: 10_000})
l2, _ := redis.New(redis.Options{Addr: "redis:6379"})
c := tiered.New(tiered.Options{L1: l1, L2: l2, L1TTL: 30 * time.Second})
Reads check L1 first; on a miss they fall through to L2 and, on an L2 hit, backfill L1 so the next read is local. Writes and deletes fan out to both tiers. GetOrLoad adds read-through population from an origin function with singleflight stampede protection, so a burst of concurrent misses for the same key triggers exactly one load.
The zero value is not usable; construct with New. Both tiers must be supplied. Tiered is itself a cache.Cache (and a cache.MultiCache), so it nests: an L2 can itself be another tiered cache.
Index ¶
- type Cache
- func (c *Cache) Close() error
- func (c *Cache) Delete(ctx context.Context, keys ...string) (n int, err error)
- func (c *Cache) Exists(ctx context.Context, key string) (_ bool, err error)
- func (c *Cache) Get(ctx context.Context, key string) (_ []byte, err error)
- func (c *Cache) GetMulti(ctx context.Context, keys ...string) (_ map[string][]byte, err error)
- func (c *Cache) GetOrLoad(ctx context.Context, key string, ttl time.Duration, load Loader) (_ []byte, err error)
- func (c *Cache) Ping(ctx context.Context) (err error)
- func (c *Cache) Set(ctx context.Context, key string, value []byte, ttl time.Duration) (err error)
- func (c *Cache) SetMulti(ctx context.Context, items map[string][]byte, ttl time.Duration) (err error)
- func (c *Cache) TTL(ctx context.Context, key string) (_ time.Duration, err error)
- type Loader
- type Options
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Cache ¶
type Cache struct {
// contains filtered or unexported fields
}
Cache is the two-level cache. It satisfies cache.Cache and cache.MultiCache.
func New ¶
New builds a tiered cache. It panics if either tier is nil — a misconfiguration that can only be a programming error at wiring time.
func (*Cache) Delete ¶
Delete removes the keys from both tiers and returns the count reported by L2 (the source of truth). L1 is always cleared even if L2 delete fails, so a stale local copy is never left behind.
func (*Cache) Get ¶
Get returns the value for key, checking L1 then L2. On an L2 hit the entry is backfilled into L1. Returns cache.ErrNotFound when neither tier has it.
func (*Cache) GetMulti ¶
GetMulti fetches keys from L1, then fills the gaps from L2 in a single batched call, backfilling every L2 hit into L1. Missing keys are simply absent from the result.
func (*Cache) GetOrLoad ¶
func (c *Cache) GetOrLoad(ctx context.Context, key string, ttl time.Duration, load Loader) (_ []byte, err error)
GetOrLoad returns key's cached value, or — on a full miss — calls load exactly once (even under concurrent callers for the same key, via singleflight), stores the result in both tiers with ttl, and returns it. This is the read-through / stampede-protection entry point: a thundering herd of requests for a cold key results in a single origin load, not one per request.
load is not called for keys already present in L1 or L2. A load error is propagated and nothing is cached.
func (*Cache) Set ¶
Set writes value into both tiers. L2 gets the full ttl; L1 gets the smaller of ttl and L1TTL (when L1TTL is set) so a local copy can't outlive the shared one. An L2 write failure is returned without touching L1, so the tiers can't diverge on a failed write.
type Options ¶
type Options struct {
// L1 is the fast, near tier (typically in-process memory). Required.
L1 cache.Cache
// L2 is the slow, shared tier (typically network Redis/Memcached),
// and the source of truth for TTL. Required.
L2 cache.Cache
// L1TTL caps how long a value backfilled into L1 (on an L2 hit) or
// written into L1 (on Set) may live, bounding how stale a local copy
// can get relative to L2. 0 means "mirror the L2 TTL" — the value
// lives in L1 for L2's remaining lifetime (which requires an extra
// L2.TTL round-trip on backfill). A small non-zero L1TTL avoids that
// round-trip and is the recommended production setting.
L1TTL time.Duration
// Hook fires after every tiered operation with kind = "cache.get",
// "cache.set", "cache.del", "cache.exists", "cache.ttl",
// "cache.ping", "cache.load". It observes the tiered call as a whole;
// the underlying L1/L2 caches fire their own hooks independently.
Hook drops.Hook
// Clock is injectable for tests; defaults to time.Now.
Clock func() time.Time
}
Options configures a tiered cache.