cache

package
v0.22.0 Latest Latest
Warning

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

Go to latest
Published: Jun 24, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package cache provides a key-value cache abstraction with a built-in in-memory store. Modelled on Laravel's Cache facade.

The Store interface is small enough to back with Redis, Memcached, or a database — those drivers live in separate sub-packages so the core module has zero extra dependencies.

Usage:

c := cache.NewMemory()
c.Put(ctx, "user:1", []byte("Ada"), 5*time.Minute)
v, ok, _ := c.Get(ctx, "user:1")
c.Forget(ctx, "user:1")

Helper:

v, err := cache.Remember(ctx, c, "users:list", time.Minute, func() ([]byte, error) {
    return fetchAndSerializeUsers()
})

Index

Constants

This section is empty.

Variables

View Source
var ErrMiss = errors.New("cache: key not found")

ErrMiss is returned by Get-shaped APIs that signal absence with an error. The basic Store interface uses (value, ok, error) instead, so ErrMiss is mostly informational for higher layers.

View Source
var ErrUnsupported = errors.New("cache: operation not supported by store")

ErrUnsupported is returned by free helpers whose semantics cannot be emulated safely on a plain Store. For example, Increment needs an atomic read-modify-write that a basic Get+Put cannot provide without racing, so on stores that do not implement the incrementer optional interface the helper reports ErrUnsupported rather than silently corrupting the counter under concurrency.

Functions

func Add added in v0.21.0

func Add(ctx context.Context, s Store, key string, value []byte, ttl time.Duration) (bool, error)

Add stores value under key only if it is absent. If the store implements adder (e.g. *Memory) the check-and-set is atomic, making this a reliable lock primitive. Otherwise it falls back to a non-atomic Has followed by Put, which may race with a concurrent Add/Put of the same key — acceptable for best-effort caching, not for locking. Returns true when the value was stored.

func Decrement added in v0.21.0

func Decrement(ctx context.Context, s Store, key string, delta int64) (int64, error)

Decrement atomically subtracts delta from the counter at key. See Increment for store requirements and the ErrUnsupported fallback.

func Increment added in v0.21.0

func Increment(ctx context.Context, s Store, key string, delta int64) (int64, error)

Increment atomically adds delta to the integer counter at key and returns the new value. It requires a store that implements incrementer (e.g. *Memory); on any other store it returns ErrUnsupported, because a Get+Put emulation cannot guarantee the atomic read-modify-write a counter needs and would silently lose updates under concurrency.

func Many added in v0.21.0

func Many(ctx context.Context, s Store, keys ...string) (map[string][]byte, error)

Many returns the values for the present keys. If the store implements manyer (e.g. *Memory) the reads happen in one critical section; otherwise it falls back to a per-key Get loop, which is not a consistent snapshot but is otherwise correct. Missing/expired keys are omitted.

func Pull

func Pull(ctx context.Context, s Store, key string) ([]byte, bool, error)

Pull reads a key and removes it. If the underlying store implements puller (e.g. *Memory) the read-and-delete is atomic; otherwise it falls back to a non-atomic Get followed by Forget, which may race with a concurrent Pull/Put of the same key. Returns (nil, false, nil) on miss.

func PutMany added in v0.21.0

func PutMany(ctx context.Context, s Store, items map[string][]byte, ttl time.Duration) error

PutMany stores every item under the same TTL. If the store implements manyer (e.g. *Memory) the writes happen in one critical section; otherwise it falls back to a per-key Put loop and stops at the first error.

func Remember

func Remember(ctx context.Context, s Store, key string, ttl time.Duration, fn func() ([]byte, error)) ([]byte, error)

Remember returns the value for key if present, otherwise calls fn, stores the result with ttl, and returns it. The classic "cache-aside" pattern.

users, err := cache.Remember(ctx, store, "users:list", time.Minute,
    func() ([]byte, error) { return json.Marshal(listUsers()) })

Types

type Memory

type Memory struct {
	// contains filtered or unexported fields
}

Memory is a process-local Store backed by a sync.RWMutex map. Safe for concurrent use; expiry is lazy (checked on Get/Has) with a background sweeper to keep the map from growing unbounded.

func NewMemory

func NewMemory() *Memory

NewMemory returns a fresh in-memory cache. A goroutine periodically purges expired keys; call Close() when discarding the cache to stop it.

func (*Memory) Add added in v0.21.0

func (m *Memory) Add(_ context.Context, key string, value []byte, ttl time.Duration) (bool, error)

Add stores value under key only if the key is absent (or expired), returning true when it stored. A return of false means a live value already occupied the key and was left untouched. The check and the write happen in one critical section, so Add is a safe lock primitive: of N concurrent Adds for the same key exactly one wins.

TTL semantics match Put: ttl==0 stores forever, ttl<0 is treated as an already-expired write and stores nothing (Add returns false only when a live value blocks it, so a ttl<0 Add on an absent key returns true without storing).

func (*Memory) Close

func (m *Memory) Close()

Close stops the background sweeper. Subsequent operations remain safe but expired entries will only be evicted on access.

func (*Memory) Decrement added in v0.21.0

func (m *Memory) Decrement(ctx context.Context, key string, delta int64) (int64, error)

Decrement atomically subtracts delta from the counter at key. It is Increment with a negated delta; see Increment for value encoding and error behaviour.

func (*Memory) Flush

func (m *Memory) Flush(_ context.Context) error

func (*Memory) Forever added in v0.21.0

func (m *Memory) Forever(ctx context.Context, key string, value []byte) error

Forever stores value under key with no expiry. It is shorthand for Put(ctx, key, value, 0).

func (*Memory) Forget

func (m *Memory) Forget(_ context.Context, key string) error

func (*Memory) Get

func (m *Memory) Get(_ context.Context, key string) ([]byte, bool, error)

func (*Memory) Has

func (m *Memory) Has(ctx context.Context, key string) (bool, error)

func (*Memory) Increment added in v0.21.0

func (m *Memory) Increment(_ context.Context, key string, delta int64) (int64, error)

Increment atomically adds delta to the integer counter stored at key and returns the new value. A missing or expired key starts from 0. The counter is persisted as its decimal ASCII representation so a plain Get reads it back consistently (e.g. Get after Increment by 5 returns []byte("5")). Incrementing a key whose value is not a base-10 integer returns an error and leaves the value unchanged.

Counters are stored without expiry (forever); use Put/Forget to reset or bound their lifetime.

func (*Memory) Many added in v0.21.0

func (m *Memory) Many(_ context.Context, keys ...string) (map[string][]byte, error)

Many returns the values for the present, non-expired keys in a single critical section. Missing or expired keys are simply omitted from the result map, so len(result) may be < len(keys). Returned slices are copies; callers may mutate them freely.

func (*Memory) Pull added in v0.20.0

func (m *Memory) Pull(_ context.Context, key string) ([]byte, bool, error)

Pull returns the value for key and removes it in a single critical section, so a concurrent Pull of the same key can succeed at most once. Returns (nil, false, nil) on miss or expiry.

func (*Memory) Put

func (m *Memory) Put(_ context.Context, key string, value []byte, ttl time.Duration) error

func (*Memory) PutMany added in v0.21.0

func (m *Memory) PutMany(_ context.Context, items map[string][]byte, ttl time.Duration) error

PutMany stores every item under the same TTL in a single critical section. TTL semantics match Put: ttl==0 stores forever, ttl<0 deletes each key (treated as an already-expired write).

type Store

type Store interface {
	// Get returns the value, whether the key existed, and any driver
	// error. An expired entry behaves like a miss (ok=false, err=nil).
	Get(ctx context.Context, key string) ([]byte, bool, error)
	// Put stores value under key with the given TTL. ttl==0 means store
	// without expiry (until explicit Forget / Flush); ttl<0 means the
	// entry is already expired and is treated as an immediate miss (the
	// key is removed rather than stored).
	Put(ctx context.Context, key string, value []byte, ttl time.Duration) error
	// Forget removes the key (no error if missing).
	Forget(ctx context.Context, key string) error
	// Flush wipes everything.
	Flush(ctx context.Context) error
	// Has reports whether the key exists and is not expired.
	Has(ctx context.Context, key string) (bool, error)
}

Store is the minimum surface a cache driver must implement.

All methods take a context so callers can cancel slow remote stores. The in-memory implementation ignores the context value but still respects the deadline via the (rare) blocking codepaths.

Jump to

Keyboard shortcuts

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