cache

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Aug 1, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

README

go-cache

A native, in-memory, generic cache for Go backend services. Think of it as a miniaturized, single-process Redis: fast reads, TTL expiry, pluggable eviction, and a small interface anyone can extend or mock.

No dependencies outside the standard library.

Install

go get github.com/vikash-paf/go-cache

Quick start

package main

import (
	"fmt"
	"time"

	cache "github.com/vikash-paf/go-cache"
)

func main() {
	c := cache.New[string, int](
		cache.WithDefaultTTL[string, int](time.Minute),
	)
	defer c.Close()

	c.Set("visits", 1)
	if v, ok := c.Get("visits"); ok {
		fmt.Println(v) // 1
	}
}

Design

  • Generic: Cache[K comparable, V any]. Keys and values are whatever your code already uses, no interface{} boxing.
  • Sharded: the keyspace is split across N independent shards (default 32), each with its own lock, so concurrent goroutines rarely contend. Reads on an unbounded cache take a read lock only; capacity-bounded caches promote to a write lock on Get because recency/frequency bookkeeping needs to mutate shared state. See shard.go for the full tradeoff.
  • TTL: set a cache-wide default with WithDefaultTTL, or override per entry with cache.WithTTL(d) on a single Set call. Expired entries are treated as absent on read even before a sweep removes them.
  • Extensible eviction: capacity limits are enforced through the eviction.Policy[K] interface. Built-in policies are eviction.NewLRU, eviction.NewLFU, and eviction.NewFIFO (all O(1) per operation). Implement eviction.Policy[K] yourself for anything else (2-random, size-weighted, TinyLFU, ...) and pass it via WithEvictionPolicy.
  • Background janitor: a goroutine per cache periodically sweeps expired entries so memory isn't held by keys nobody reads again. Configurable via WithJanitorInterval, or disable it entirely by passing 0.
  • Observability: Stats() returns hit/miss/eviction counters. WithOnEvict registers a callback for capacity evictions and TTL expiries, useful for logging or cascading invalidation.

Extending eviction

type myPolicy[K comparable] struct{ /* ... */ }

func (p *myPolicy[K]) Add(key K)          { /* ... */ }
func (p *myPolicy[K]) Hit(key K)          { /* ... */ }
func (p *myPolicy[K]) Remove(key K)       { /* ... */ }
func (p *myPolicy[K]) Evict() (K, bool)   { /* ... */ }
func (p *myPolicy[K]) Len() int           { /* ... */ }

c := cache.New[string, User](
	cache.WithCapacity[string, User](10_000),
	cache.WithEvictionPolicy[string, User](func() eviction.Policy[string] {
		return &myPolicy[string]{}
	}),
)

Because each shard gets its own Policy instance (built by the Factory you pass in), implementations never need to worry about concurrency inside Policy itself: the shard's lock already serializes every call into it.

Options reference

Option Effect Default
WithShards(n) number of internal shards 32
WithCapacity(n) max entries per shard before eviction 0 (unbounded)
WithEvictionPolicy(f) eviction strategy factory LRU, if capacity is set
WithDefaultTTL(d) TTL applied to Set unless overridden 0 (no expiry)
WithJanitorInterval(d) background sweep frequency 1s
WithOnEvict(fn) callback on eviction/expiry none

Per-Set option: WithTTL(d) overrides the cache's default TTL for that one entry.

Benchmarks

go test -bench . -benchmem:

BenchmarkGetUnbounded-10     16.4M ops    67 ns/op    16 B/op   1 allocs/op
BenchmarkGetLRUBounded-10    15.5M ops    77 ns/op    16 B/op   1 allocs/op
BenchmarkSet-10              27.3M ops    45 ns/op    32 B/op   2 allocs/op

Numbers will vary by hardware and key type (string keys hash through maphash; integer keys take an allocation-free fast path).

Status

Core cache, TTL, sharding, and LRU/LFU/FIFO eviction are implemented and tested (including -race). No external dependencies.

Documentation

Overview

Package cache provides a fast, in-memory, generic key-value cache for Go backend services. It shards its keyspace across independent locks for concurrent throughput, supports per-entry TTLs, and evicts via a pluggable eviction.Policy (LRU, LFU, FIFO, or a custom implementation) once a capacity is set.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cache

type Cache[K comparable, V any] interface {
	// Get returns the value stored under key and true, or the zero value
	// and false if the key is absent or has expired.
	Get(key K) (V, bool)
	// Set stores value under key, applying the cache's default TTL unless
	// a SetOption overrides it. Set never fails: on a full, capacity-bounded
	// cache it evicts via the configured Policy to make room.
	Set(key K, value V, opts ...SetOption)
	// Delete removes key and reports whether it was present.
	Delete(key K) bool
	// Has reports whether key is present and unexpired, without affecting
	// eviction order the way Get does.
	Has(key K) bool
	// Len returns the number of entries currently stored, including any
	// not-yet-swept expired entries.
	Len() int
	// Keys returns a snapshot of all live (unexpired at call time) keys.
	// It allocates and copies, so avoid it on hot paths.
	Keys() []K
	// Clear removes every entry from the cache.
	Clear()
	// Close stops the cache's background janitor goroutine. Safe to call
	// once; the cache remains usable afterwards, it just stops sweeping
	// expired entries proactively (Get still treats them as absent).
	Close() error
	// Stats returns a snapshot of hit/miss/eviction counters accumulated
	// since the cache was created.
	Stats() Stats
}

Cache is the public interface implemented by *Cache. It exists so consumers can depend on an interface (and swap in a mock, or an alternative implementation) instead of a concrete type.

func New

func New[K comparable, V any](opts ...Option[K, V]) Cache[K, V]

New builds a Cache configured by the given options. With no options it returns an unbounded cache with 32 shards and no TTL: entries live until explicitly deleted.

Example
package main

import (
	"fmt"
	"time"

	cache "github.com/vikash-paf/go-cache"
)

func main() {
	c := cache.New[string, int](cache.WithDefaultTTL[string, int](time.Minute))
	defer c.Close()

	c.Set("visits", 1)
	v, ok := c.Get("visits")
	fmt.Println(v, ok)
}
Output:
1 true

type Option

type Option[K comparable, V any] func(*config[K, V])

Option configures a Cache at construction time.

func WithCapacity

func WithCapacity[K comparable, V any](perShard int) Option[K, V]

WithCapacity bounds each shard to at most n entries, evicting via the configured eviction Policy once full. Capacity is enforced per shard rather than globally so a hot shard can never block on a global counter; with N shards, total capacity is approximately n*N. A value of 0 (the default) means unbounded.

func WithDefaultTTL

func WithDefaultTTL[K comparable, V any](ttl time.Duration) Option[K, V]

WithDefaultTTL sets the time-to-live applied to entries written with Set when no per-item TTL is given via WithTTL. Zero (the default) means entries never expire unless given an explicit TTL.

func WithEvictionPolicy

func WithEvictionPolicy[K comparable, V any](f eviction.Factory[K]) Option[K, V]

WithEvictionPolicy sets the eviction.Factory used to build a fresh eviction.Policy for every shard. Only meaningful together with WithCapacity. Defaults to eviction.NewLRU when a capacity is set but no policy is chosen.

func WithJanitorInterval

func WithJanitorInterval[K comparable, V any](d time.Duration) Option[K, V]

WithJanitorInterval sets how often each shard sweeps for expired entries in the background. Expired entries are also skipped lazily on Get regardless of this setting, so the janitor only affects how quickly memory for expired-but-unread entries is reclaimed. A value <= 0 disables the background sweep entirely. Default: 1 second.

func WithOnEvict

func WithOnEvict[K comparable, V any](fn func(key K, value V)) Option[K, V]

WithOnEvict registers a callback invoked whenever an entry is removed due to capacity eviction or TTL expiry (not on explicit Delete). The callback runs synchronously on the goroutine that triggered the eviction, so it must be fast and must not call back into the same Cache.

func WithShards

func WithShards[K comparable, V any](n int) Option[K, V]

WithShards sets the number of internal shards used to partition the keyspace. More shards reduce lock contention under concurrent access at the cost of slightly higher memory overhead and less precise global capacity accounting. Must be a positive number; non-positive values are ignored. Default: 32.

type SetOption

type SetOption func(*setConfig)

SetOption configures a single Set call.

func WithTTL

func WithTTL(ttl time.Duration) SetOption

WithTTL overrides the cache's default TTL for a single Set call. A TTL of 0 means the entry never expires.

type Stats

type Stats struct {
	Hits      uint64
	Misses    uint64
	Evictions uint64 // capacity evictions and TTL expiries combined
}

Stats is a point-in-time snapshot of cache activity counters.

Directories

Path Synopsis
Package eviction defines the pluggable eviction strategy used by a cache shard once it reaches capacity, plus a set of ready-to-use policies (LRU, LFU, FIFO).
Package eviction defines the pluggable eviction strategy used by a cache shard once it reaches capacity, plus a set of ready-to-use policies (LRU, LFU, FIFO).

Jump to

Keyboard shortcuts

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