Documentation
¶
Overview ¶
Package lru is an in-process least-recently-used cache with optional TTL + singleflight wrapping. The S36 perf-pass standardizes on this package for every cross-request cache (refs, tree, ahead/ behind, rendered markdown, etc.) so callers don't roll their own eviction story.
Two core types:
Cache[K,V] — count-bounded LRU with optional per-entry TTL. The dumb-and-fast variant for value types whose in-memory cost is uniform.
SizedCache[K] — byte-bounded LRU. Each entry contributes a caller-supplied size; eviction runs while the total exceeds the cap. For values whose size varies per key (rendered HTML, diff blobs).
Both expose Get / Set / Delete / Len + a Stats accessor for the /metrics surface (S36 baseline asserts hit-rate). Hot-key dogpile prevention lives one layer up in `Group` (singleflight wrapper).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Cache ¶
type Cache[K comparable, V any] struct { // contains filtered or unexported fields }
Cache is a count-bounded LRU. Construct with New[K,V](capacity).
func New ¶
func New[K comparable, V any](capacity int) *Cache[K, V]
New constructs a count-bounded LRU. capacity must be positive. The TTL defaults to "no expiry"; use NewWithTTL to set one.
func NewWithTTL ¶
NewWithTTL is like New plus a per-entry TTL. Entries past their TTL are treated as misses on Get and dropped on access.
func (*Cache[K, V]) Delete ¶
func (c *Cache[K, V]) Delete(key K)
Delete removes key. No-op when absent.
func (*Cache[K, V]) Get ¶
Get returns the value for key + true on hit, zero value + false on miss (including TTL-expired entries).
func (*Cache[K, V]) Len ¶
Len reports the live entry count (does NOT scan for TTL expiry — that happens lazily on Get).
type Group ¶
type Group[K comparable, V any] struct { // contains filtered or unexported fields }
Group wraps a Cache with single-flight semantics so a hot-key miss doesn't spawn N concurrent upstream calls. The fetch function is invoked at most once per (key, in-flight wave) — concurrent callers wait on the same goroutine and receive its result.
Use this whenever the upstream is non-trivial: a `git rev-list` subprocess, an FS walk, a multi-row DB read.
func NewGroup ¶
func NewGroup[K comparable, V any](cache *Cache[K, V], keyer func(K) string) *Group[K, V]
NewGroup wraps cache with singleflight. keyer must produce a stable, unique string for every distinct K (default: `fmt.Sprint`- equivalent for the type). For composite keys (struct), the caller is on the hook for serialization.
func (*Group[K, V]) Do ¶
func (g *Group[K, V]) Do(ctx context.Context, key K, fetch func(ctx context.Context) (V, error)) (V, error)
Do returns the cached value when present, otherwise invokes fetch (single-flighted) and caches the result before returning.
Errors from fetch are NOT cached — a transient failure on key K shouldn't poison subsequent reads. Callers that want negative- caching add their own sentinel value.
func (*Group[K, V]) Invalidate ¶
func (g *Group[K, V]) Invalidate(key K)
Invalidate drops key from the cache. Safe to call from anywhere (push handlers, settings updates) without coordinating with in-flight singleflight callers — the next Do re-fetches.
type SizedCache ¶
type SizedCache[K comparable] struct { // contains filtered or unexported fields }
SizedCache is a byte-bounded LRU. Each entry contributes a caller- supplied size; eviction runs while the running total exceeds the cap. Use this for caches whose values vary widely in memory cost (rendered HTML, diff blobs, response bodies).
func NewSized ¶
func NewSized[K comparable](maxBytes int64) *SizedCache[K]
NewSized constructs a byte-bounded LRU. maxBytes must be positive.
func (*SizedCache[K]) Bytes ¶
func (c *SizedCache[K]) Bytes() int64
Bytes reports the current total payload size.
func (*SizedCache[K]) Delete ¶
func (c *SizedCache[K]) Delete(key K)
Delete removes key. No-op when absent.
func (*SizedCache[K]) Get ¶
func (c *SizedCache[K]) Get(key K) ([]byte, bool)
Get returns the cached bytes + true on hit. The returned slice is the cached buffer (zero-copy) — callers MUST NOT mutate it. Use `append([]byte(nil), v...)` if mutation is needed.
func (*SizedCache[K]) Set ¶
func (c *SizedCache[K]) Set(key K, val []byte)
Set stores key→val. Replacing an existing key updates its size. Eviction runs after insertion until total bytes ≤ maxBytes.
func (*SizedCache[K]) Stats ¶
func (c *SizedCache[K]) Stats() Stats
Stats returns a snapshot of the hit / miss / eviction counters.