Documentation
¶
Overview ¶
Package cascache implements a provider-agnostic cache with compare-and-swap (CAS) safety via per-key generations. Single-key reads never return stale values. Bulk results are validated on read (per member) and rejected if any member is stale.
Components:
- Provider: byte store with TTL (e.g. Ristretto, BigCache, Redis).
- Codec[V]: (de)serializes V <-> []byte.
- GenStore: generation counter per logical key. Local (in-process) by default, optional Redis implementation for multi-replica / restart persistence. Custom implementations receive opaque genstore.CacheKey identities, not logical user keys.
Keys:
cas:v1:val:s:<nsLen>:<ns>:<key> - single entries cas:v1:val:b:<nsLen>:<ns>:<sha256-128> - set-shaped entries (128-bit SHA-256 over sorted keys) cas:v1:gen:s:<nsLen>:<ns>:<key> - generation counters (RedisGenStore)
CAS pattern:
obs, err := cache.TrySnapshotGen(ctx, k) // before DB read
v := readFromDB(k)
if err == nil {
_ = cache.SetWithGen(ctx, k, v, obs, 0) // write iff current gen == obs
}
Index ¶
- Variables
- type BulkReadGuardFunc
- type BulkRejectReason
- type BulkSeedMode
- type BulkWriteSeedMode
- type CAS
- type Cache
- type Hooks
- type InvalidateError
- type MissingObservedGensError
- type NopHooks
- func (NopHooks) BulkRejected(string, int, BulkRejectReason)
- func (NopHooks) GenBumpError(genstore.CacheKey, error)
- func (NopHooks) GenSnapshotError(int, error)
- func (NopHooks) InvalidateOutage(string, error, error)
- func (NopHooks) LocalGenWithBulk()
- func (NopHooks) ProviderSetRejected(string, bool)
- func (NopHooks) SelfHealSingle(string, SelfHealReason)
- type Op
- type OpError
- type Options
- type ReadGuardFunc
- type SelfHealReason
- type SetCostFunc
Constants ¶
This section is empty.
Variables ¶
var ErrBulkSeedNeedsAdder = errors.New("BulkSeedIfMissing requires Adder")
ErrBulkSeedNeedsAdder identifies an invalid configuration where BulkSeedIfMissing is requested with a provider that does not implement Adder.
var ErrMissingObservedGens = errors.New("missing observed generations")
ErrMissingObservedGens identifies a SetBulkWithGens caller error where at least one item key has no corresponding observed generation.
Functions ¶
This section is empty.
Types ¶
type BulkReadGuardFunc ¶ added in v1.4.1
type BulkReadGuardFunc[V any] func(ctx context.Context, values map[string]V) (rejected map[string]struct{}, err error)
BulkReadGuardFunc is the batch form of ReadGuardFunc for validated GetBulk hits. The input map contains only the requested logical keys that survived wire and generation checks. Return the keys that failed validation. Any non-empty result deletes the stored bulk entry because bulk values are stored as a single blob.
If ReadGuard is also configured, GetBulk rechecks the rejected keys through per-key fallback reads. Otherwise, rejected keys are treated as misses for that GetBulk call so they cannot be served back from seeded singles. Returning a key that was not present in values or returning an error is treated conservatively as a guard failure. Returning an error is treated conservatively as a bulk rejection.
type BulkRejectReason ¶ added in v0.3.1
type BulkRejectReason string
BulkRejectReason classifies why a bulk entry was rejected.
const ( // at least one bulk payload could not be decoded by the codec. BulkRejectReasonValueDecode BulkRejectReason = "value_decode" // bulk entry was missing members or contained stale generations. BulkRejectReasonInvalidOrStale BulkRejectReason = "invalid_or_stale" // bulk wire envelope was invalid. BulkRejectReasonDecodeError BulkRejectReason = "decode_error" // caller omitted at least one observed generation. BulkRejectReasonMissingObservedGen BulkRejectReason = "missing_observed_gen" // current generations could not be loaded. BulkRejectReasonGenSnapshotError BulkRejectReason = "gen_snapshot_error" // one observed generation no longer matched. BulkRejectReasonGenMismatch BulkRejectReason = "gen_mismatch" // an authoritative read guard rejected at least one requested member. BulkRejectReasonReadGuardReject BulkRejectReason = "read_guard_reject" // an authoritative read guard failed, so the bulk was conservatively dropped. BulkRejectReasonReadGuardError BulkRejectReason = "read_guard_error" )
type BulkSeedMode ¶ added in v1.1.0
type BulkSeedMode uint8
BulkSeedMode controls whether a successful bulk read validated members as individual single-key entries. The zero/default value is BulkSeedOff so bulk hits stay read-only unless the caller explicitly opts into warming singles.
const ( // BulkSeedOff returns the bulk hit "as-is" and does not write singles. BulkSeedOff BulkSeedMode = iota // BulkSeedAll seeds singles after the bulk has already passed // generation validation. No additional per-key generation lookup is done. BulkSeedAll // BulkSeedIfMissing seeds singles only when the provider supports // conditional add/set-if-absent with native backend support or // provider-level atomic coordination. Cache construction fails if the // provider does not implement Adder. BulkSeedIfMissing )
type BulkWriteSeedMode ¶ added in v1.4.1
type BulkWriteSeedMode uint8
BulkWriteSeedMode controls how a successful SetBulkWithGens write individual single-key entries.
The zero/default value is BulkWriteSeedStrict, which routes each single through SetWithGen again so the post-bulk seeding path preserves the same per-key CAS recheck as standalone writes. Higher-throughput systems can opt into BulkWriteSeedFast to reuse the validated bulk payloads directly, or BulkWriteSeedOff to skip success path single seeding entirely.
const ( // BulkWriteSeedStrict seeds singles through SetWithGen after the bulk write // has succeeded. Stricter CAS semantics of rechecking // each key's generation immediately before the single write lands. BulkWriteSeedStrict BulkWriteSeedMode = iota // BulkWriteSeedFast seeds singles directly from the validated bulk payloads // without a second generation lookup. This is faster but can allow stale // singles to land if a generation changes between batch validation and the // single writes. BulkWriteSeedFast // BulkWriteSeedOff skips single seeding after a successful bulk write. The // bulk entry is still written, and fallback paths that skip or reject the // bulk continue to seed singles best-effort through the checked CAS path. BulkWriteSeedOff )
type CAS ¶
type CAS[V any] interface { Enabled() bool Close(context.Context) error // Single Get(ctx context.Context, key string) (v V, ok bool, err error) SetWithGen(ctx context.Context, key string, value V, observedGen uint64, ttl time.Duration) error Invalidate(ctx context.Context, key string) error // Bulk (order-agnostic return. Use your own ordering by keys slice) GetBulk(ctx context.Context, keys []string) (values map[string]V, missing []string, err error) SetBulkWithGens(ctx context.Context, items map[string]V, observedGens map[string]uint64, ttl time.Duration) error // Error-aware generation snapshots for CAS writes. // These return an error so the caller can decide whether to proceed. TrySnapshotGen(ctx context.Context, key string) (uint64, error) TrySnapshotGens(ctx context.Context, keys []string) (map[string]uint64, error) // Best-effort generation snapshots. // Failures are reported through Hooks and the generation falls back to zero. SnapshotGen(ctx context.Context, key string) uint64 SnapshotGens(ctx context.Context, keys []string) map[string]uint64 }
CAS is the provider-agnostic cache interface with compare-and-swap safety via per-key generations. V is the caller's value type serialization is handled by the configured Codec[V].
type Hooks ¶ added in v0.0.6
type Hooks interface {
SelfHealSingle(storageKey string, reason SelfHealReason)
BulkRejected(namespace string, requested int, reason BulkRejectReason)
ProviderSetRejected(storageKey string, isBulk bool)
GenSnapshotError(count int, err error)
GenBumpError(cacheKey genstore.CacheKey, err error)
InvalidateOutage(key string, bumpErr, delErr error)
LocalGenWithBulk()
}
Hooks are lightweight callbacks for high-signal events. Implementations MUST be cheap and non-blocking; do not perform I/O. If work may block, buffer it and drop on backpressure (best effort).
Key-bearing callbacks intentionally expose different key kinds:
- SelfHealSingle / ProviderSetRejected: provider storage keys.
- GenBumpError: canonical genstore.CacheKey identity.
func Multi ¶ added in v0.0.9
Multi returns a Hooks implementation that fans out to all provided hooks in order. Nil entries are silently skipped. Panics from any hook propagate to the caller.
Example usage:
logH := sloghook.New(slog.Default(), sloghook.Options{SelfHealEvery: 10}) metH := promhook.New(...) // some kind of metrics adapter auditH := myAuditHook{...} // audit adapter
fan-out mh := cascache.MultiHooks{logH, metH, auditH}
Either: single async queue for the whole fan-out hooks := asynchook.New(mh, 1, 1000)
Or: give each hook its own queue (isolate backpressure)
hooks := cascache.MultiHooks{
asynchook.New(logH, 1, 1000),
asynchook.New(metH, 1, 1000),
asynchook.New(auditH, 1, 1000),
}
type InvalidateError ¶ added in v0.0.5
func (*InvalidateError) Error ¶ added in v0.0.5
func (e *InvalidateError) Error() string
func (*InvalidateError) Unwrap ¶ added in v0.0.5
func (e *InvalidateError) Unwrap() []error
type MissingObservedGensError ¶ added in v0.3.1
type MissingObservedGensError struct {
Missing []string
}
MissingObservedGensError reports which logical keys were missing observed generations.
func (*MissingObservedGensError) Error ¶ added in v0.3.1
func (e *MissingObservedGensError) Error() string
func (*MissingObservedGensError) Unwrap ¶ added in v0.3.2
func (e *MissingObservedGensError) Unwrap() error
type NopHooks ¶ added in v0.0.6
type NopHooks struct{}
NopHooks is a default no-op.
func (NopHooks) BulkRejected ¶ added in v0.0.6
func (NopHooks) BulkRejected(string, int, BulkRejectReason)
func (NopHooks) GenBumpError ¶ added in v0.0.6
func (NopHooks) GenSnapshotError ¶ added in v0.0.6
func (NopHooks) InvalidateOutage ¶ added in v0.0.6
func (NopHooks) LocalGenWithBulk ¶ added in v0.0.6
func (NopHooks) LocalGenWithBulk()
func (NopHooks) ProviderSetRejected ¶ added in v0.0.6
func (NopHooks) SelfHealSingle ¶ added in v0.0.6
func (NopHooks) SelfHealSingle(string, SelfHealReason)
type OpError ¶ added in v1.2.0
type OpError struct {
Op Op
Key string // empty for non-key specific failures such as bulk path failures
// Err is the underlying cause.
// Error panics if Err is nil.
Err error
}
OpError reports an operation failure and, when applicable, the logical key that triggered it.
type Options ¶
type Options[V any] struct { Namespace string // logical namespace to isolate the keyspace Provider pr.Provider Codec c.Codec[V] DefaultTTL time.Duration // singles; 0 => 10m BulkTTL time.Duration // bulks; 0 => 10m CleanupInterval time.Duration // 0 => 1h GenRetention time.Duration // 0 => 30d Disabled bool // default false (enabled) ComputeSetCost SetCostFunc // default 1 GenStore gen.GenStore // nil => LocalGenStore (in-process) DisableBulk bool // default false => bulk enabled ReadGuard ReadGuardFunc[V] BulkReadGuard BulkReadGuardFunc[V] // BulkSeed controls single-entry warming after successful GetBulk hits // only. It does not affect how successful SetBulkWithGens writes seed // singles; that behavior is controlled separately by BulkWriteSeed. // BulkSeedIfMissing requires a provider with native or provider-level // atomic add-if-missing support. BulkSeed BulkSeedMode // BulkWriteSeed controls single-entry materialization after a successful // SetBulkWithGens bulk write only. The default is BulkWriteSeedStrict, // which re-enters SetWithGen per key to preserve stricter CAS semantics. // It does not affect fallback single seeding when the bulk write is // skipped, rejected, or bulk mode is disabled. BulkWriteSeed BulkWriteSeedMode Hooks Hooks }
Options configures the CAS cache. Namespace, Provider, and Codec are required.
type ReadGuardFunc ¶ added in v1.4.1
ReadGuardFunc can veto serving a decoded cache hit for a single logical key. It is intended for critical paths that need an authoritative source check before a cached value may be returned.
Return allow=false to reject the entry as stale or unsafe. Any returned error is treated conservatively as a rejection and the caller receives a miss.
type SelfHealReason ¶ added in v0.3.1
type SelfHealReason string
SelfHealReason classifies why a single entry was deleted on read.
const ( // single-entry wire envelope was invalid. SelfHealReasonCorrupt SelfHealReason = "corrupt" // stored generation no longer matched the current generation. SelfHealReasonGenMismatch SelfHealReason = "gen_mismatch" // payload could not be decoded by the configured codec. SelfHealReasonValueDecode SelfHealReason = "value_decode" // an authoritative read guard rejected the entry. SelfHealReasonReadGuardReject SelfHealReason = "read_guard_reject" // an authoritative read guard errored, so the entry was conservatively dropped. SelfHealReasonReadGuardError SelfHealReason = "read_guard_error" )
Directories
¶
| Path | Synopsis |
|---|---|
|
hooks
|
|
|
async
usage:
|
usage: |
|
slog
Package sloghook provides a slog-based implementation of cascache.Hooks.
|
Package sloghook provides a slog-based implementation of cascache.Hooks. |
|
internal
|
|
|
wire
Package wire contains the compact, versioned on-the-wire format used by cascache to store values in the underlying Provider.
|
Package wire contains the compact, versioned on-the-wire format used by cascache to store values in the underlying Provider. |
|
Package provider defines the storage abstraction used by cascache.
|
Package provider defines the storage abstraction used by cascache. |