lru

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 10, 2026 License: AGPL-3.0 Imports: 6 Imported by: 0

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

func NewWithTTL[K comparable, V any](capacity int, ttl time.Duration) *Cache[K, V]

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

func (c *Cache[K, V]) Get(key K) (V, bool)

Get returns the value for key + true on hit, zero value + false on miss (including TTL-expired entries).

func (*Cache[K, V]) Len

func (c *Cache[K, V]) Len() int

Len reports the live entry count (does NOT scan for TTL expiry — that happens lazily on Get).

func (*Cache[K, V]) Set

func (c *Cache[K, V]) Set(key K, val V)

Set stores key→val, evicting the least-recently-used entry when at capacity. Replacing an existing key resets its TTL.

func (*Cache[K, V]) Stats

func (c *Cache[K, V]) Stats() Stats

Stats returns a snapshot of the hit / miss / eviction counters.

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.

func (*Group[K, V]) Stats

func (g *Group[K, V]) Stats() Stats

Stats reports the underlying cache's counters.

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]) Len

func (c *SizedCache[K]) Len() int

Len reports the entry count.

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.

type Stats

type Stats struct {
	Hits      uint64
	Misses    uint64
	Evictions uint64
}

Stats is the per-cache hit / miss / eviction counter set. Counters are atomic so /metrics can read without locking the cache.

Jump to

Keyboard shortcuts

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