cache

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Oct 21, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package cache is a small, explicit cache for expensive reads that are safe to recompute. A Store holds bytes under a key for a bounded time; the code that uses it receives one and calls it where it wants caching. There is no ambient configuration, no automatic caching of database queries and no invalidation from writes: a cache only ever makes a read cheaper, and the database stays the source of truth.

A miss is not an error: Get reports it as ok == false with a nil error, and an error always means the backend failed, so a hit, a miss and a failure are three different outcomes. The Store, the stores in cache/local, cache/redis and cache/memcache, and the helpers here never log; they return errors and the host decides what is worth recording.

Keys follow one rule every backend can store (see ValidKey), and a TTL has one portable range (see CheckTTL). Values are bytes, whose format is the host's. The conformance suite in package cachetest holds every Store to this contract.

Index

Constants

View Source
const (
	// OpGet is a Store.Get failure.
	OpGet = "get"
	// OpDecode is a stored value that does not decode (ErrCorrupt).
	OpDecode = "decode"
	// OpEncode is a loaded value that cannot be encoded as JSON.
	OpEncode = "encode"
	// OpSet is a Store.Set failure.
	OpSet = "set"
)

The operations a FetchJSON cache failure is reported under.

View Source
const (
	// MaxKeyLength is the longest key, in bytes. It is below Memcached's 250
	// byte limit so a Prefix has room to grow.
	MaxKeyLength = 200
	// MaxTTL is the longest portable TTL. A longer retention belongs in your
	// database or object storage, not in a cache.
	MaxTTL = 30 * 24 * time.Hour
)

Variables

View Source
var (
	// ErrInvalidKey is returned for a key that is not 1 to 200 bytes of
	// printable ASCII without spaces, before any request is made.
	ErrInvalidKey = errors.New("cache: invalid key")
	// ErrInvalidTTL is returned for a TTL that is not greater than zero and
	// at most MaxTTL, before any request is made.
	ErrInvalidTTL = errors.New("cache: invalid ttl")
	// ErrTooLarge is returned when a Store refuses a value for its size.
	ErrTooLarge = errors.New("cache: value is too large")
)
View Source
var ErrCorrupt = errors.New("cache: corrupt value")

ErrCorrupt is returned by GetJSON for a stored value that cannot be decoded into the requested type. Match it with errors.Is; errors.As with *CorruptError gives the key and the decode error.

Functions

func CheckTTL

func CheckTTL(ttl time.Duration) error

CheckTTL returns ErrInvalidTTL unless 0 < ttl <= MaxTTL.

func FetchJSON

func FetchJSON[T any](ctx context.Context, store Store, key string, ttl time.Duration, load func(context.Context) (T, error), opts ...FetchOption) (T, error)

FetchJSON returns the value cached under key, or calls load, caches what it returns for ttl and returns that. It is deliberately fail-open: the cache is an optimization, so

  • if reading or decoding fails, FetchJSON reports it, calls load, and returns the loaded value if loading succeeds;
  • if writing the loaded value fails, FetchJSON reports it and still returns the value.

A key or ttl that is not valid (ErrInvalidKey, ErrInvalidTTL) is a programming error, not a cache failure: FetchJSON returns it before calling load, and never works around it. That includes a key the store itself refuses, such as one a Prefix made too long.

A load failure is returned as is (it is not a cache error). Cache failures go to the OnError hook when one is supplied. Without a hook they are not returned when loading succeeds, since observability is the hook's job, but when both the cache and load fail the errors are returned joined.

FetchJSON does not coalesce concurrent calls for the same key: when a hot key expires, every caller loads. Wrap load with singleflight to avoid it.

func GetJSON

func GetJSON[T any](ctx context.Context, store Store, key string) (T, bool, error)

GetJSON reads the value under key and decodes it as JSON into a T. A miss is ok == false with a nil error. Bytes that do not decode into T are an error matching ErrCorrupt, never a zero-value hit. Any other error is the Store's own.

func SetJSON

func SetJSON[T any](ctx context.Context, store Store, key string, value T, ttl time.Duration) error

SetJSON encodes value as JSON and stores it under key for ttl. The encoding is plain encoding/json: no codec is configurable, and the stored bytes are yours to read with any other tool.

func ValidKey

func ValidKey(key string) bool

ValidKey reports whether key is 1 to MaxKeyLength bytes of printable ASCII with no spaces, which every backend can store.

Types

type CorruptError

type CorruptError struct {
	// Key is the key whose value could not be decoded.
	Key string
	// Err is the underlying decode error.
	Err error
}

CorruptError is the typed form of ErrCorrupt.

func (*CorruptError) Error

func (e *CorruptError) Error() string

func (*CorruptError) Is

func (e *CorruptError) Is(target error) bool

Is matches ErrCorrupt.

func (*CorruptError) Unwrap

func (e *CorruptError) Unwrap() error

Unwrap returns the underlying decode error.

type FetchOption

type FetchOption func(*fetchConfig)

FetchOption configures FetchJSON.

func OnError

func OnError(fn func(op string, err error)) FetchOption

OnError reports each cache failure FetchJSON works around. fn is called synchronously, once per failed cache operation, with the operation (OpGet, OpDecode, OpEncode or OpSet) and the error. It cannot change what FetchJSON does, and a loader failure is never reported to it. A panic in fn is the host's and propagates.

type Store

type Store interface {
	// Get returns the value stored under key. A miss, including an expired
	// value, is ok == false with a nil error; an error means the backend
	// failed. The returned slice belongs to the caller.
	Get(ctx context.Context, key string) (value []byte, ok bool, err error)
	// Set stores value under key for ttl, replacing any existing value. The
	// Store does not retain value after Set returns. It fails with
	// ErrInvalidKey, or ErrInvalidTTL unless 0 < ttl <= MaxTTL, before any
	// request, and with ErrTooLarge for a value the backend refuses for size.
	// ttl is the longest the value may be served: a Store never treats it as
	// elapsed sooner than asked (a backend with coarser time units rounds
	// up). It is not a promise of retention. A bounded or remote cache may
	// evict the value, lose it in a restart, or have it deleted earlier, so
	// a later Get can miss; store only what can be recomputed.
	Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
	// Delete removes key. Deleting a missing key is not an error.
	Delete(ctx context.Context, key string) error
}

Store is a cache. Implementations are safe for concurrent use and pass the suite in package cachetest.

func Prefix

func Prefix(store Store, prefix string) Store

Prefix returns a Store that puts prefix in front of every key before delegating to store. It validates the final prefixed key, so an unusual prefix (even an empty one) is accepted as long as every resulting key is valid, and a combination that is too long or has a space is refused with ErrInvalidKey.

Versioning is yours: change "profile:v2:" to "profile:v3:" and every older entry becomes unreachable and expires by its TTL. That is the way to invalidate in bulk; there is no Clear.

Directories

Path Synopsis
Package local is a bounded in-process cache.Store, for development, tests and single-process deployments.
Package local is a bounded in-process cache.Store, for development, tests and single-process deployments.
memcache module
redis module

Jump to

Keyboard shortcuts

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