Documentation
¶
Overview ¶
Package cascache implements a provider-agnostic cache with compare-and-swap (CAS) safety via per-key version fences. Single-key reads never return stale values. Batch 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.
- VersionStore: authoritative version state per logical key. Local (in-process) by default, optional Redis implementation for multi-replica / restart persistence. Custom implementations receive opaque version.CacheKey identities, not logical user keys.
- KeyWriter / KeyInvalidator: optional backend-native fast paths for single-key compare-and-write and invalidate. KeyMutator is the alias for implementations that provide both. See the redis subpackage for the built-in Redis implementation.
Keys:
cas:v3:val:{<slot>}:s:<nsLen>:<ns>:<key> - single entries
cas:v3:val:b:<nsLen>:<ns>:<sha256-128> - set-shaped entries (128-bit SHA-256 over sorted keys)
cas:v3:ver:{<slot>}:s:<nsLen>:<ns>:<key> - authoritative version state
CAS pattern:
obs, err := cache.SnapshotVersion(ctx, k) // before DB read
v := readFromDB(k)
if err == nil {
_, _ = cache.SetIfVersion(ctx, k, v, obs) // write iff current version == obs
}
Index ¶
- Variables
- type BatchReadGuardFunc
- type BatchReadSeedMode
- type BatchRejectReason
- type BatchWriteResult
- type BatchWriteSeedMode
- type CAS
- type Cache
- type Hooks
- type InvalidateError
- type KeyInvalidator
- type KeyMutator
- type KeyWriter
- type NopHooks
- func (NopHooks) BatchRejected(string, int, BatchRejectReason)
- func (NopHooks) InvalidateOutage(string, error, error)
- func (NopHooks) LocalVersionStoreWithBatch()
- func (NopHooks) ProviderSetRejected(string, bool)
- func (NopHooks) SelfHealSingle(string, SelfHealReason)
- func (NopHooks) VersionAdvanceError(version.CacheKey, error)
- func (NopHooks) VersionCreateError(version.CacheKey, error)
- func (NopHooks) VersionSnapshotError(int, error)
- type Op
- type OpError
- type Options
- type ReadGuardFunc
- type SelfHealReason
- type SetCostFunc
- type Version
- type VersionedValue
- type WriteOutcome
- type WriteResult
Constants ¶
This section is empty.
Variables ¶
var ErrBatchReadSeedNeedsAdder = errors.New("BatchReadSeedIfMissing requires Adder")
ErrBatchReadSeedNeedsAdder identifies an invalid configuration where BatchReadSeedIfMissing is requested with a provider that does not implement Adder.
Functions ¶
This section is empty.
Types ¶
type BatchReadGuardFunc ¶
type BatchReadGuardFunc[V any] func(ctx context.Context, values map[string]V) (rejected map[string]struct{}, err error)
BatchReadGuardFunc is the batch form of ReadGuardFunc for validated GetMany hits. The input map contains only the requested logical keys that survived wire and fence checks. Return the keys that failed validation. Any non-empty result deletes the stored batch entry because batch values are stored as a single blob.
If ReadGuard is also configured, GetMany rechecks the rejected keys through per-key fallback reads. Otherwise, rejected keys are treated as misses for that GetMany call so they cannot be served back from seeded singles.
type BatchReadSeedMode ¶
type BatchReadSeedMode uint8
BatchReadSeedMode controls whether a successful batch read validated members as individual single-key entries. The zero/default value is BatchReadSeedOff so batch hits stay read-only unless the caller explicitly opts into warming singles.
const ( BatchReadSeedOff BatchReadSeedMode = iota BatchReadSeedAll BatchReadSeedIfMissing )
type BatchRejectReason ¶
type BatchRejectReason string
BatchRejectReason classifies why a batch entry was rejected.
const ( // at least one batch payload could not be decoded by the codec. BatchRejectReasonValueDecode BatchRejectReason = "value_decode" // at least one requested member was absent from the stored batch entry. BatchRejectReasonIncompleteBatch BatchRejectReason = "incomplete_batch" // authoritative version state was missing for at least one requested member. BatchRejectReasonVersionMissing BatchRejectReason = "version_missing" // at least one requested member no longer matched authoritative version state. BatchRejectReasonVersionMismatch BatchRejectReason = "version_mismatch" // batch wire envelope was invalid. BatchRejectReasonDecodeError BatchRejectReason = "decode_error" // an authoritative read guard rejected at least one requested member. BatchRejectReasonReadGuardReject BatchRejectReason = "read_guard_reject" // an authoritative read guard failed, so the batch was conservatively dropped. BatchRejectReasonReadGuardError BatchRejectReason = "read_guard_error" )
type BatchWriteResult ¶
type BatchWriteResult struct {
Outcome WriteOutcome
SeededSingles bool
}
BatchWriteResult describes the result of a versioned write through the batch entry path.
func (BatchWriteResult) Stored ¶
func (r BatchWriteResult) Stored() bool
Stored reports whether the batch entry landed in the provider.
type BatchWriteSeedMode ¶
type BatchWriteSeedMode uint8
BatchWriteSeedMode controls how a successful SetIfVersions write individual single-key entries.
The zero/default value is BatchWriteSeedStrict, which routes each single through SetIfVersion again so the post-batch seeding path preserves the same per-key CAS recheck as standalone writes. Higher-throughput systems can opt into BatchWriteSeedFast to reuse the validated batch payloads directly, or BatchWriteSeedOff to skip success path single seeding entirely.
const ( BatchWriteSeedStrict BatchWriteSeedMode = iota BatchWriteSeedFast BatchWriteSeedOff )
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) SnapshotVersion(ctx context.Context, key string) (Version, error) SetIfVersion(ctx context.Context, key string, value V, version Version) (WriteResult, error) SetIfVersionWithTTL(ctx context.Context, key string, value V, version Version, ttl time.Duration) (WriteResult, error) Invalidate(ctx context.Context, key string) error // Batch (order-agnostic return. Use your own ordering by keys slice) GetMany(ctx context.Context, keys []string) (values map[string]V, missing []string, err error) SnapshotVersions(ctx context.Context, keys []string) (map[string]Version, error) SetIfVersions(ctx context.Context, items []VersionedValue[V]) (BatchWriteResult, error) SetIfVersionsWithTTL(ctx context.Context, items []VersionedValue[V], ttl time.Duration) (BatchWriteResult, error) }
CAS is the provider-agnostic cache interface with compare-and-swap safety via per-key version fences. V is the caller's value type serialization is handled by the configured Codec[V].
type Hooks ¶
type Hooks interface {
SelfHealSingle(storageKey string, reason SelfHealReason)
BatchRejected(namespace string, requested int, reason BatchRejectReason)
ProviderSetRejected(storageKey string, isBatch bool)
VersionSnapshotError(count int, err error)
VersionCreateError(cacheKey version.CacheKey, err error)
VersionAdvanceError(cacheKey version.CacheKey, err error)
InvalidateOutage(key string, bumpErr, delErr error)
LocalVersionStoreWithBatch()
}
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.
- VersionCreateError / VersionAdvanceError: canonical version.CacheKey identity.
func Multi ¶
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 ¶
func (*InvalidateError) Error ¶
func (e *InvalidateError) Error() string
func (*InvalidateError) Unwrap ¶
func (e *InvalidateError) Unwrap() []error
type KeyInvalidator ¶
type KeyInvalidator interface {
Invalidate(ctx context.Context, versionKey version.CacheKey, valueKey string) error
}
KeyInvalidator is an optional backend-native fast path for single-key invalidation. versionKey identifies the canonical authoritative version state tracked by the configured VersionStore. valueKey identifies the provider storage key for the encoded single value entry. Implementations are responsible for coordinating those two keys so Invalidate preserves the same contract as the generic cache path.
type KeyMutator ¶
type KeyMutator interface {
KeyWriter
KeyInvalidator
}
type KeyWriter ¶
type KeyWriter interface {
// payload is the codec-encoded caller value, not the final wire envelope.
// Implementations are responsible for writing a value stamped with the
// authoritative fence that actually won the compare/init step.
SetIfVersion(ctx context.Context, versionKey version.CacheKey, valueKey string, expected version.Snapshot, payload []byte, ttl time.Duration) (stored bool, err error)
}
KeyWriter is an optional backend-native fast path for single-key compare-and-write operations. versionKey identifies the canonical authoritative version state tracked by the configured VersionStore. valueKey identifies the provider storage key for the encoded single value entry. Implementations are responsible for coordinating those two keys so SetIfVersion preserves the same freshness contract as the generic cache path.
type NopHooks ¶
type NopHooks struct{}
NopHooks is a default no-op.
func (NopHooks) BatchRejected ¶
func (NopHooks) BatchRejected(string, int, BatchRejectReason)
func (NopHooks) LocalVersionStoreWithBatch ¶
func (NopHooks) LocalVersionStoreWithBatch()
func (NopHooks) ProviderSetRejected ¶
func (NopHooks) SelfHealSingle ¶
func (NopHooks) SelfHealSingle(string, SelfHealReason)
func (NopHooks) VersionSnapshotError ¶
type OpError ¶
type OpError struct {
Op Op
Key string // empty for non-key specific failures such as batch 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 BatchTTL time.Duration // batches; 0 => 10m Disabled bool // default false (enabled) ComputeSetCost SetCostFunc // default 1 VersionStore version.Store // nil => LocalStore (in-process) KeyWriter KeyWriter KeyInvalidator KeyInvalidator DisableBatch bool // default false => batch enabled ReadGuard ReadGuardFunc[V] BatchReadGuard BatchReadGuardFunc[V] BatchReadSeed BatchReadSeedMode BatchWriteSeed BatchWriteSeedMode Hooks Hooks }
Options configures the CAS cache. Namespace, Provider, and Codec are required.
type ReadGuardFunc ¶
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 ¶
type SelfHealReason string
SelfHealReason classifies why a single entry was deleted on read.
const ( // single-entry wire envelope was invalid. SelfHealReasonCorrupt SelfHealReason = "corrupt" // no authoritative version state existed for the key. SelfHealReasonVersionMissing SelfHealReason = "version_missing" // stored version fence no longer matched the current authoritative fence. SelfHealReasonVersionMismatch SelfHealReason = "version_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" )
type SetCostFunc ¶
SetCostFunc computes the provider cost used for a cache write. The returned value is passed through to Provider.Set (and Adder.Add when applicable). Providers that use admission or weighted eviction can interpret it as entry weight; providers that ignore cost may discard it. key is the provider storage key cascache is writing. raw is the fully encoded wire value that will be stored. isBatch reports whether the write targets the grouped batch-entry path rather than a single-key entry. memberCount is 1 for single writes and the number of logical keys contained in a batch entry.
type Version ¶
type Version struct {
// contains filtered or unexported fields
}
Version is the per-key freshness token returned by the cache. Callers should treat it as an compare-only value and pass it back unchanged to versioned write APIs.
The zero value is the missing-version token.
type VersionedValue ¶
VersionedValue is the caller-facing unit for versioned multi-key writes.
type WriteOutcome ¶
type WriteOutcome string
WriteOutcome describes what happened during a versioned write attempt.
const ( WriteOutcomeStored WriteOutcome = "stored" WriteOutcomeVersionMismatch WriteOutcome = "version_mismatch" WriteOutcomeSnapshotError WriteOutcome = "snapshot_error" WriteOutcomeProviderRejected WriteOutcome = "provider_rejected" WriteOutcomeDisabled WriteOutcome = "disabled" )
type WriteResult ¶
type WriteResult struct {
Outcome WriteOutcome
}
func (WriteResult) Stored ¶
func (r WriteResult) Stored() bool
Stored reports whether the write landed in the provider.
Source Files
¶
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. |
|
Package redis contains the Redis backend for cascache.
|
Package redis contains the Redis backend for cascache. |
|
Package version contains the lower-level primitives behind cascache's authoritative version state.
|
Package version contains the lower-level primitives behind cascache's authoritative version state. |