pacecache

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Sep 19, 2026 License: MIT Imports: 14 Imported by: 0

README

pacecache

pacecache

Fast and concurrent in-memory cache for Go

Go Reference Test codecov

pacecache is a bounded, generic in-process cache for Go, built for highly concurrent workloads. It combines segmented LRU storage, flexible expiration, cache-aside loading, and optional observability while keeping resident hot paths allocation-free.

The library provides an intuitive API with predictable behavior under high concurrency and contention.

Features

  • Concurrency: Segmented storage scales across concurrent workloads.
  • Generic API: Type-safe caching with comparable keys and arbitrary value types.
  • Bounded LRU: Exact per-segment LRU within a fixed total capacity.
  • Expiration: Default and per-entry TTLs, jitter, sliding expiration, refresh, and no-expiration entries.
  • Cleanup: Lazy expiration, explicit cleanup, and optional background cleanup.
  • Cache-Aside: Coalesces concurrent misses for the same key into a single load.
  • Safe Updates: Publication barriers prevent stale loads from overwriting newer cache state.
  • Observability: Built-in statistics with optional OpenTelemetry metrics.

Installation

This repository contains the core pacecache module. The core package is released from the repository root:

go get github.com/mkbeh/pacecache

Optional OpenTelemetry metrics are available through paceotel:

go get github.com/mkbeh/pacecache/extra/paceotel

Usage

Create a cache with pacecache.New:

cache, err := pacecache.New[string, string]()
if err != nil {
    panic(err)
}

Caches use a single storage segment by default. For highly concurrent workloads, use WithSegmentCount and benchmark segment counts against the application's actual access pattern.

Entries do not expire by default. Use WithTTL to set the default expiration and WithJitter to spread expiration deadlines and reduce synchronized expiration bursts. Individual entries can use the default TTL, a custom TTL, or NoExpiration.

cache, _ := pacecache.New[string, string](
    pacecache.WithTTL(5*time.Minute),
    pacecache.WithJitter(30*time.Second),
)

Values can be stored, retrieved, checked, conditionally inserted, and removed:

// Store values with different expiration strategies.
cache.Set("key1", "value1", pacecache.DefaultExpiration) // cache-level TTL
cache.Set("key2", "value2", pacecache.NoExpiration)      // no expiration
cache.Set("key3", "value3", 30*time.Second)              // custom TTL

// Read a live value.
value, found := cache.Get("key1")

// Read a value together with its expiration metadata.
entry, found := cache.GetEntry("key1")
fmt.Println(entry.Value(), entry.ExpiresAt())

// Check existence without updating LRU or TTL.
exists := cache.Exists("key2")

// Atomically return an existing value or store a new one.
value, found = cache.GetOrSet("key4", "value4", pacecache.DefaultExpiration)
entry, found = cache.GetOrSetEntry("key5", "value5", 30*time.Second)

// Remove cached values.
value, found = cache.GetAndDelete("key3") // read and delete atomically
cache.Delete("key1")                      // delete one key
cache.Delete("key2", "key4")              // delete multiple keys
cache.DeleteExpired()                     // remove expired entries
cache.Clear()                             // remove all entries

Use GetOrLoadFunc to load a value on a cache miss with a per-call loader. The loader runs only when no live entry exists; successful results are cached using the cache's default expiration:

// Define a loader that fetches the value from an upstream source.
loader := pacecache.Loader[string, string](func(ctx context.Context, key string) (string, bool, error) {
    // Fetch data from a database, file, or remote service.
    return "loaded value", true, nil
})

// Return the cached value or invoke the loader on a miss.
value, found, err := cache.GetOrLoadFunc(ctx, "key", loader)
if err != nil {
    panic(err)
}

if found {
    fmt.Println("retrieved value:", value) // loaded key
}

If the same loader is reused across calls, configure it once with NewWithDefaultLoader and use GetOrLoad:

cache, _ := pacecache.NewWithDefaultLoader[string, string](
    func(ctx context.Context, key string) (string, bool, error) {
        // Fetch data from a database, file, or remote service.
        return "loaded value", true, nil
    },
    pacecache.WithTTL(5*time.Minute),
)

// Return the cached value or invoke the configured loader on a miss.
value, found, err := cache.GetOrLoad(ctx, "key")
if err != nil {
    panic(err)
}

if found {
    fmt.Println("retrieved value:", value) // loaded key
}

Missing results and loader errors are returned without being cached. Concurrent misses for the same key share a single loader execution, avoiding duplicate requests to the upstream source.

Expired entries are removed lazily when encountered. Background cleanup can be started with StartCleanup. Since StartCleanup blocks until StopCleanup is called, it is usually launched in a separate goroutine:

cache, _ := pacecache.New[string, string](
    pacecache.WithTTL(5*time.Minute),
)

// Start automatic deletion of expired items.
go cache.StartCleanup()

// Stop automatic deletion of expired items.
cache.StopCleanup()

Background cleanup is optional. Expired entries can also be reclaimed explicitly with DeleteExpired.

Concurrency semantics

The cache coordinates concurrent loads and mutations to prevent duplicate upstream work and stale values from overwriting newer cache state. The flow below shows how a shared in-flight load is handled when the cache is mutated before the loader completes:

flowchart LR
    Request["Concurrent same-key GetOrLoad calls"]
    Miss["Cache miss"]
    Load["Single shared loader"]
    Check{"Cache changed<br/>while loading?"}
    Store["Store loaded value"]
    Reject["Reject stale result"]
    Request --> Miss
    Miss --> Load
    Load --> Check
    Check -->|No| Store
    Check -->|Yes| Reject

Concurrent misses for the same key share a single loader execution, while different keys are loaded independently. If the cache is mutated while a load is in flight, the newer mutation takes precedence. The stale loaded value is discarded instead of overwriting the newer cache state, and the loading call returns an error.

Callers waiting for a shared load can stop waiting through their own context.Context without blocking other waiting callers.

Observability

The cache provides built-in runtime statistics and optional OpenTelemetry metrics for monitoring cache behavior.

Stats returns a snapshot of the current cache state and cumulative activity:

// Retrieve a snapshot of the current cache state and activity.
stats := cache.Stats()

// Selected statistics available in the snapshot.
_ = stats.EntryCount        // Current resident entries
_ = stats.MaxEntries        // Configured maximum capacity
_ = stats.HitCount          // Cache hits
_ = stats.MissCount         // Cache misses
_ = stats.EvictionCount     // LRU evictions
_ = stats.ExpirationCount   // Expired entries removed
_ = stats.LoadErrorCount    // Loader errors
_ = stats.DeletedEntryCount // Explicitly deleted entries
_ = stats.ClearedEntryCount // Entries removed by full cache clears

Statistics also include load outcomes, shared and superseded loads, deleted and cleared entry counts, cleanup activity, and segment count.

Optional OpenTelemetry metrics are available through paceotel. OpenTelemetry configuration and exporter selection remain application concerns, so Prometheus, OTLP, and other exporters can be used without changing the cache integration.

For a complete setup, see the example.

Performance

The benchmark suite evaluates concurrent throughput, cache hit ratio, and memory consumption under representative cache workloads.

Benchmarks were run on an Intel Core i7-12700H (14 cores, 20 threads).

Throughput

Measures concurrent read/write throughput using a pre-generated Scrambled Zipfian access pattern to create skewed key access and hot-key contention.

  • Concurrency: 8 parallel workers
  • Segments: 512
  • Maximum entries: 10K, 100K, and 1M
  • Write ratios: 0%, 25%, 50%, 75%, and 100%
  • Expiration: Disabled

Throughput

Hit Ratio

Measures how cache capacity affects hit ratio under a Zipfian access pattern.

  • Requests: 1,000,000
  • Segments: 1
  • Capacity: 500 to 80K entries
  • Expiration: Disabled to isolate capacity and eviction behavior

Hit Ratio

Memory

Measures live heap consumption after populating the cache with fixed-size keys and values.

  • Data: Fixed 32-byte keys and 32-byte values
  • Segments: 1
  • Capacity: 1K to 1M entries
  • Expiration: 1-hour TTL

Memory Consumption

For the complete methodology, source code, and execution instructions, see the performance benchmarks.

Examples

See the examples directory for runnable examples demonstrating how to use pacecache.

License

This project is licensed under the MIT License.

Documentation

Overview

Package pacecache provides a bounded, concurrent in-process cache.

Cache keys may use any comparable Go type. Entries support TTL expiration, optional sliding expiration and TTL jitter, LRU eviction, cache-aside loading with configurable default or per-call loaders, duplicate load suppression, and explicit removal.

Background expiration cleanup is optional and is not started automatically. StartCleanup runs the cleanup loop and blocks until StopCleanup is called. Callers that want background cleanup should start it in a goroutine. WithCleanupInterval configures the regular cleanup interval.

Cache mutations act as publication barriers for concurrent loads, preventing superseded loader results from overwriting newer cache state.

Cache statistics are collected locally and exposed through Cache.Stats. Optional metrics integrations register when a cache is created and observe those snapshots without adding telemetry calls to the cache request path.

The cache is local to one application process. It does not provide distributed cache coherence between application instances.

Index

Constants

View Source
const (
	// DefaultExpiration uses the cache's configured TTL.
	DefaultExpiration time.Duration = 0

	// NoExpiration disables time-based expiration for the entry.
	NoExpiration time.Duration = -1
)

Variables

View Source
var (
	// ErrNotInitialized indicates that an operation requires an initialized
	// cache but the cache is nil or has not been initialized.
	ErrNotInitialized = errors.New("pacecache: cache is not initialized")

	// ErrNoLoader indicates that an operation requires a loader but none is
	// available. NewWithDefaultLoader also returns ErrNoLoader when passed a nil loader.
	ErrNoLoader = errors.New("pacecache: loader is not configured")

	// ErrLoadSuperseded indicates that a successful loader result was made stale
	// by a newer cache mutation, such as Set, a GetOrSet or GetOrSetEntry insertion,
	// Delete, or Clear, before it could be published to the cache.
	ErrLoadSuperseded = errors.New("pacecache: load superseded by cache mutation")
)

Functions

This section is empty.

Types

type Cache

type Cache[K comparable, V any] struct {
	// contains filtered or unexported fields
}

Cache is a bounded in-process cache for keys of type K and values of type V.

Cache uses exact LRU eviction within each storage segment and TTL expiration. Entries may optionally use sliding expiration. GetOrLoad, GetOrLoadFunc, GetOrLoadEntry, and GetOrLoadEntryFunc provide cache-aside loading and coalesce concurrent loads for the same key.

Cache is safe for concurrent use. A Cache must not be copied after creation.

func New

func New[K comparable, V any](
	options ...Option,
) (*Cache[K, V], error)

New creates a Cache.

Unless overridden by options, New uses the default cache capacity, a single storage segment, and no time-based expiration. No default loader is configured. Metrics are disabled by default, and background cleanup is not started automatically.

func NewWithDefaultLoader added in v1.2.0

func NewWithDefaultLoader[K comparable, V any](
	loader Loader[K, V],
	options ...Option,
) (*Cache[K, V], error)

NewWithDefaultLoader creates a Cache with the given default loader.

The loader is used by GetOrLoad and GetOrLoadEntry when no live cache entry exists. Per-call loaders may be supplied through GetOrLoadFunc and GetOrLoadEntryFunc. Loader must not be nil.

Options, metrics, and background cleanup have the same semantics as New.

func (*Cache[K, V]) Clear added in v1.2.0

func (cache *Cache[K, V]) Clear()

Clear removes all entries from the cache.

Clear acts as a cache-wide publication barrier. Successful loader results already in flight are discarded with ErrLoadSuperseded if Clear wins before publication, and cannot repopulate the cache afterward.

The removed entries may include physically resident entries whose TTL has already expired but which have not yet been removed.

Clear is a no-op on an uninitialized Cache.

func (*Cache[K, V]) Delete added in v1.2.0

func (cache *Cache[K, V]) Delete(keys ...K)

Delete removes the specified keys from the cache.

Delete acts as a publication barrier for each key. A successful loader result already in flight for a deleted key is discarded with ErrLoadSuperseded if Delete wins before publication, and cannot repopulate that key afterward.

Missing keys are ignored. Duplicate keys are allowed.

Delete is a no-op when called without keys or on an uninitialized Cache.

func (*Cache[K, V]) DeleteExpired added in v1.2.0

func (cache *Cache[K, V]) DeleteExpired() int64

DeleteExpired physically removes expired entries using the cache expiration index and returns the number of entries removed.

DeleteExpired is always available; background cleanup does not need to be running. Logical expiration is independent of physical cleanup: an expired entry is never returned even if it has not yet been reclaimed. Nearby expiration deadlines are grouped internally. Bucket eligibility may trail the exact TTL deadline by up to the internal bucket resolution; actual physical reclamation also depends on when cleanup runs. Manual cleanup uses the configured cleanup batch size and entry budget, yielding cooperatively between quanta until all entries due at the start of the call are drained.

func (*Cache[K, V]) Exists

func (cache *Cache[K, V]) Exists(key K) bool

Exists reports whether a live entry exists for key.

Expired and missing entries return false. Exists does not update LRU recency, refresh sliding expiration, or affect lookup statistics.

An expired entry observed by Exists is removed from storage and contributes to expiration statistics.

func (*Cache[K, V]) Get

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

Get returns the cached value for key.

The returned bool reports whether a live entry exists. A live hit updates LRU recency and contributes to lookup statistics. Expired entries are always treated as misses and are removed when observed, by DeleteExpired, or by background cleanup when it is running. When sliding expiration is enabled, a live hit refreshes the entry using the TTL with which it was stored.

func (*Cache[K, V]) GetAndDelete added in v1.2.0

func (cache *Cache[K, V]) GetAndDelete(key K) (V, bool)

GetAndDelete atomically returns the live value for key and removes it from the cache. Missing and expired entries return the zero value with found=false. Expired entries are reclaimed as expirations.

GetAndDelete acts as a publication barrier for the same key. An in-flight successful loader result is discarded with ErrLoadSuperseded if this method wins before publication, even when no resident entry exists.

The operation does not refresh sliding expiration, update LRU recency, or affect lookup hit/miss statistics. A successfully removed live entry is counted as a deleted entry.

func (*Cache[K, V]) GetEntry

func (cache *Cache[K, V]) GetEntry(key K) (Entry[V], bool)

GetEntry returns an immutable snapshot of the cached entry for key.

GetEntry has the same lookup semantics as Get: it updates LRU recency, contributes to lookup statistics, removes an observed expired entry, and refreshes a live entry when sliding expiration is enabled.

For an entry stored with NoExpiration, Entry.ExpiresAt returns the zero time. When sliding expiration refreshes an entry, ExpiresAt reflects the refreshed deadline.

func (*Cache[K, V]) GetOrLoad

func (cache *Cache[K, V]) GetOrLoad(
	ctx context.Context,
	key K,
) (V, bool, error)

GetOrLoad returns the cached value for key or obtains it from the configured default loader.

On a cache miss, GetOrLoad returns ErrNoLoader when the cache was created without a default loader. Use NewWithDefaultLoader to configure one, or GetOrLoadFunc to supply a loader for a specific operation.

A loader result with found=true is cached using the configured TTL. A result with found=false and a nil error is returned to callers but is not cached. Loader errors are never cached.

Concurrent misses for the same key are coalesced. The caller that starts the shared load executes the loader synchronously with its context. Other callers may stop waiting independently when their own contexts are canceled.

Set, GetOrSet and GetOrSetEntry insertions, Delete, and Clear act as publication barriers for the affected key or keys. If a successful loader result is superseded by a newer cache mutation before publication, GetOrLoad discards the loader result and returns ErrLoadSuperseded. Loader errors take precedence over ErrLoadSuperseded. Mutations of other keys do not affect the load, even when those keys share the same segment.

A loader must not call GetOrLoad, GetOrLoadFunc, GetOrLoadEntry, or GetOrLoadEntryFunc recursively for the same key, because the nested call would wait for the load already in progress.

When err is non-nil, the returned value is the zero value of V and found is false.

func (*Cache[K, V]) GetOrLoadEntry

func (cache *Cache[K, V]) GetOrLoadEntry(
	ctx context.Context,
	key K,
) (Entry[V], bool, error)

GetOrLoadEntry returns a read-only cache entry snapshot for key or obtains the value from the configured default loader and publishes it before returning the snapshot.

On a cache miss, GetOrLoadEntry returns ErrNoLoader when the cache was created without a default loader. Use NewWithDefaultLoader to configure one, or GetOrLoadEntryFunc to supply a loader for a specific operation.

A loader result with found=false is not cached and returns the zero Entry with found=false. GetOrLoadEntry otherwise has the same lookup, singleflight, publication-barrier, LRU, sliding-expiration, and statistics semantics as GetOrLoad.

ExpiresAt reflects the exact deadline of the cached entry observed or published by the operation, including TTL jitter. As with GetEntry, the returned Entry is a snapshot and the cache may change immediately after the operation completes.

func (*Cache[K, V]) GetOrLoadEntryFunc added in v1.2.1

func (cache *Cache[K, V]) GetOrLoadEntryFunc(
	ctx context.Context,
	key K,
	loader Loader[K, V],
) (Entry[V], bool, error)

GetOrLoadEntryFunc returns a read-only cache entry snapshot for key or obtains the value from loader and publishes it before returning the snapshot.

The supplied loader is used instead of the cache's configured default loader when this caller starts the shared load. If another caller already owns a same-key load, GetOrLoadEntryFunc joins that wave and the supplied loader is not invoked. Loader is only required when no live cache entry exists; a nil loader therefore returns ErrNoLoader on a miss.

GetOrLoadEntryFunc otherwise has the same semantics as GetOrLoadEntry.

func (*Cache[K, V]) GetOrLoadFunc added in v1.2.1

func (cache *Cache[K, V]) GetOrLoadFunc(
	ctx context.Context,
	key K,
	loader Loader[K, V],
) (V, bool, error)

GetOrLoadFunc returns the cached value for key or obtains it from loader.

The supplied loader is used instead of the cache's configured default loader when this caller starts the shared load. If another caller already owns a same-key load, GetOrLoadFunc joins that wave and the supplied loader is not invoked. Loader is only required when no live cache entry exists; a nil loader therefore returns ErrNoLoader on a miss.

GetOrLoadFunc otherwise has the same cache, singleflight, publication, and statistics semantics as GetOrLoad.

func (*Cache[K, V]) GetOrSet

func (cache *Cache[K, V]) GetOrSet(
	key K,
	value V,
	expiration time.Duration,
) (V, bool)

GetOrSet returns the live value for key or atomically stores the provided value when no live entry exists.

The returned bool reports whether an existing value was found. On a live hit, the provided value and expiration are ignored and the operation has the same LRU, sliding-expiration, and lookup-statistics semantics as Get. On a miss or expired entry, the provided value is stored using the same expiration semantics as Set and found=false is returned.

When GetOrSet stores the provided value, it acts as a publication barrier for the same key. An in-flight successful loader result cannot overwrite the value inserted by GetOrSet afterward.

func (*Cache[K, V]) GetOrSetEntry

func (cache *Cache[K, V]) GetOrSetEntry(
	key K,
	value V,
	expiration time.Duration,
) (Entry[V], bool)

GetOrSetEntry returns the live entry for key or atomically stores the provided value when no live entry exists.

The returned bool reports whether an existing entry was found. On a live hit, the provided value and expiration are ignored and the operation has the same LRU, sliding-expiration, and lookup-statistics semantics as GetEntry. On a miss or expired entry, the provided value is stored using the same expiration semantics as Set and found=false is returned. The returned Entry always describes the resident value selected by the operation, including the actual expiration deadline assigned to a newly inserted entry.

When GetOrSetEntry stores the provided value, it acts as a publication barrier for the same key. An in-flight successful loader result cannot overwrite the value inserted by GetOrSetEntry afterward.

func (*Cache[K, V]) RefreshTTL

func (cache *Cache[K, V]) RefreshTTL(key K) bool

RefreshTTL renews the expiration deadline of a live entry.

The entry is refreshed using the effective TTL with which it was stored. RefreshTTL works independently of sliding expiration. An entry without time-based expiration is considered live and returns true without changing its state.

Expired and missing entries return false. An expired entry observed by RefreshTTL is removed from storage and contributes to expiration statistics.

RefreshTTL does not update LRU recency or lookup statistics.

func (*Cache[K, V]) Set

func (cache *Cache[K, V]) Set(
	key K,
	value V,
	expiration time.Duration,
)

Set stores a value in the cache.

DefaultExpiration uses the cache's configured TTL. A positive expiration overrides the configured TTL for this entry. NoExpiration disables time-based expiration.

Set acts as a publication barrier. A successful loader result that was already in flight for the same key is discarded with ErrLoadSuperseded if Set wins before publication, and cannot overwrite the explicitly stored value afterward.

func (*Cache[K, V]) StartCleanup added in v1.3.0

func (cache *Cache[K, V]) StartCleanup()

StartCleanup runs periodic expiration cleanup until StopCleanup is called. StartCleanup blocks for the lifetime of the cleanup loop; callers that want background cleanup should start it in a goroutine. If cleanup is already running, StartCleanup returns immediately.

func (*Cache[K, V]) Stats

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

Stats returns a detached snapshot of the current cache statistics.

func (*Cache[K, V]) StopCleanup added in v1.3.0

func (cache *Cache[K, V]) StopCleanup()

StopCleanup stops a running cleanup loop. It blocks until the cleanup loop accepts the stop signal. Repeated calls are safe. StopCleanup is a no-op when cleanup is not running or Cache is nil.

type Entry

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

Entry is a read-only snapshot returned by GetEntry, GetOrLoadEntry, GetOrLoadEntryFunc, or GetOrSetEntry.

Entry captures the value and expiration metadata observed by the operation. The value is returned using normal Go value semantics and is not deep-copied.

ExpiresAt returns the entry expiration time. It returns the zero time for an entry stored with NoExpiration.

func (Entry[V]) ExpiresAt

func (entry Entry[V]) ExpiresAt() time.Time

ExpiresAt returns the expiration time captured by the operation that produced Entry.

The zero time means the entry has no time-based expiration.

func (Entry[V]) Value

func (entry Entry[V]) Value() V

Value returns the cached value captured by the operation that produced Entry.

type Loader

type Loader[K comparable, V any] func(ctx context.Context, key K) (value V, found bool, err error)

Loader obtains one value for key from the underlying data source.

found=true means the value exists and may be cached. found=false with a nil error means the value does not exist; not-found results are returned to the caller but are not stored by pacecache.

Loader errors are never cached.

type Metrics

type Metrics interface {
	Register(source MetricsSource) error
}

Metrics registers cache statistics with a metrics implementation.

Implementations must be safe to reuse across multiple caches. Register may be called concurrently.

If Register returns an error, the implementation must release any resources created during the registration attempt.

type MetricsSource added in v1.3.0

type MetricsSource interface {
	Name() string
	Stats() Stats
}

MetricsSource exposes cache identity and statistics to a metrics implementation.

type Option

type Option func(*settings) error

Option configures a Cache created by New or NewWithDefaultLoader.

func WithCleanupBatchSize

func WithCleanupBatchSize(size int) Option

WithCleanupBatchSize configures the maximum number of expired entries removed from one storage segment in a single cleanup batch.

The setting applies to both manual and background cleanup. Larger batches can increase cleanup throughput but may hold a segment lock for longer. Values larger than a segment or the remaining cleanup budget are safe and are naturally limited by the available work. The default is 256.

func WithCleanupEntryBudget

func WithCleanupEntryBudget(entries int) Option

WithCleanupEntryBudget configures the maximum number of expired entries removed during one cooperative cleanup quantum.

The setting applies to both manual and background cleanup. Larger budgets allow large expiration backlogs to be drained more aggressively. Background cleanup is additionally bounded by an internal time budget. Manual cleanup yields cooperatively after exhausting the entry budget and continues until all entries due at the start of the call are drained. Values larger than the cache size are safe. The default is 16384.

func WithCleanupInterval

func WithCleanupInterval(interval time.Duration) Option

WithCleanupInterval configures the interval between regular cleanup wakeups.

The default is one minute. While expired backlog remains, the cleaner may schedule bounded continuation work sooner. The interval does not affect logical TTL precision or the internal expiration bucket resolution.

Background cleanup must be started explicitly with StartCleanup. Manual cleanup through Cache.DeleteExpired is always available.

func WithJitter

func WithJitter(jitter time.Duration) Option

WithJitter configures random TTL spread.

Jitter adds a random duration smaller than the configured value when an expiring entry is stored, reducing synchronized expiration. With sliding expiration, the resulting effective TTL is reused on every refresh instead of selecting another jitter value. Zero disables jitter.

func WithMaxEntries

func WithMaxEntries(maxEntries int) Option

WithMaxEntries configures the total cache entry budget.

With one segment, the full budget is shared by the cache. When multiple segments are configured, the budget is distributed across them and effective capacity utilization may be slightly lower because each segment enforces its own local budget.

func WithMetrics

func WithMetrics(metrics Metrics) Option

WithMetrics configures optional cache metrics.

The Metrics implementation may be reused by multiple caches. The cache does not manage the lifecycle of metrics registrations.

func WithName added in v1.3.0

func WithName(name string) Option

WithName configures an optional logical cache name.

Metrics implementations may use the name to distinguish cache instances. An empty name leaves the cache unnamed.

func WithSegmentCount

func WithSegmentCount(count int) Option

WithSegmentCount configures the number of independent cache segments.

The default is one segment. More segments can reduce lock contention under concurrent access but may reduce effective capacity utilization because each segment has its own entry budget. Benchmark segment counts against the application's actual workload.

func WithSlidingExpiration

func WithSlidingExpiration() Option

WithSlidingExpiration refreshes the expiration deadline of live expiring entries whenever they are successfully read.

Each entry is refreshed using the effective TTL selected when it was stored. Entries using DefaultExpiration derive that TTL from the cache configuration, while entries with an explicit positive TTL retain their own TTL. Configured jitter is selected once when the entry is stored and reused by subsequent refreshes. Entries using NoExpiration are not refreshed.

func WithTTL

func WithTTL(ttl time.Duration) Option

WithTTL configures the default lifetime of cache entries.

A positive TTL enables time-based expiration. NoExpiration disables time-based expiration for entries using the default expiration.

type Stats

type Stats struct {

	// EntryCount is the number of entries currently resident in storage.
	// Expired entries may remain resident until they are observed, evicted,
	// deleted, cleared, or reclaimed by manual or background cleanup.
	EntryCount int64

	// MaxEntries is the configured total entry budget after applying defaults.
	MaxEntries int64

	// SegmentCount is the number of independent storage and coordination
	// segments.
	SegmentCount int64

	// HitCount is the cumulative number of cache hits.
	HitCount int64

	// MissCount is the cumulative number of cache misses. An expired entry
	// observed by a caller is counted as a miss.
	MissCount int64

	// LoadFoundCount is the cumulative number of actual loader invocations that
	// returned found=true.
	LoadFoundCount int64

	// LoadNotFoundCount is the cumulative number of actual loader invocations
	// that returned found=false without an error.
	LoadNotFoundCount int64

	// LoadErrorCount is the cumulative number of actual loader invocations that
	// returned an error.
	LoadErrorCount int64

	// LoadSupersededCount is the cumulative number of successful actual loader
	// invocations whose result was discarded because a newer cache mutation,
	// such as Set, GetOrSet and GetOrSetEntry insertions, Delete, or
	// Clear, won before publication.
	//
	// Superseded loads are still included in LoadFoundCount or LoadNotFoundCount,
	// according to the loader result, and are not included in LoadErrorCount.
	LoadSupersededCount int64

	// LoadDuration is the cumulative duration of actual loader invocations,
	// including found, not-found, and failed loads.
	LoadDuration time.Duration

	// SharedCount is the cumulative number of callers that received a
	// singleflight result shared with at least one other caller.
	SharedCount int64

	// DeletedEntryCount is the cumulative number of resident entries removed by
	// key-scoped deletion operations such as Delete and GetAndDelete.
	// Missing keys and duplicate keys that were already removed do not increase
	// the count.
	DeletedEntryCount int64

	// ClearedEntryCount is the cumulative number of resident entries removed by
	// Clear.
	//
	// This may include physically resident entries whose TTL had already expired
	// but which had not yet been removed.
	ClearedEntryCount int64

	// CleanupCount is the cumulative number of completed explicit
	// DeleteExpired calls.
	CleanupCount int64

	// CleanupWorkerRunCount is the cumulative number of cleanup quanta
	// completed by the background cleanup worker.
	CleanupWorkerRunCount int64

	// CleanupWorkerPendingCount is the cumulative number of background cleanup
	// quanta that completed with more expired entries still pending.
	CleanupWorkerPendingCount int64

	// CleanupWorkerDuration is the cumulative time spent executing background
	// cleanup quanta. Waiting between cleanup runs is not included.
	CleanupWorkerDuration time.Duration

	// EvictionCount is the cumulative number of entries evicted because a
	// storage segment reached capacity.
	EvictionCount int64

	// ExpirationCount is the cumulative number of entries removed because their
	// TTL expired, either lazily during lookup or by manual or background cleanup.
	ExpirationCount int64
}

Stats is a detached snapshot of cache statistics.

The snapshot is assembled from independent cache segments. Concurrent cache activity may continue while Stats is being collected, so individual fields are not guaranteed to represent one globally atomic instant.

Directories

Path Synopsis
extra
paceotel module
internal
singleflight
Package singleflight provides a duplicate function call suppression mechanism.
Package singleflight provides a duplicate function call suppression mechanism.

Jump to

Keyboard shortcuts

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