High-performance cache policies and supporting data structures.
Status: design rationale for the current
serdefeature and the boundaries around future cache-state persistence. Companion tometrics.md,ttl.md, andbuilder-and-dyn-dispatch.md.
cachekit has a narrow serialization surface today. The serde feature derives
Serialize / Deserialize for metrics snapshots and StoreMetrics; it does
not serialize cache contents, policy metadata, hash-map state, locks, or
builder dispatchers.
That boundary is intentional. Metrics are stable observations. Cache state is live data with policy invariants, hash seeds, pointer-like handles, and optional time semantics.
With features = ["serde"], these public value types derive serde:
StoreMetrics in src/store/traits.rs.src/metrics/snapshot.rs.Properties:
u64, usize, optional nested stats).#[non_exhaustive], so new fields are SemVer-compatible at the Rust
API level but still require schema discipline for serialized consumers.No policy type implements serde today. No store type serializes entries today.
Metrics snapshots are point-in-time copies:
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LruMetricsSnapshot {
pub get_calls: u64,
pub get_hits: u64,
// ...
}
Serializing a snapshot cannot corrupt a cache on restore because there is no restore into a running policy. At most, a downstream dashboard sees old or partial counters. That matches the metrics contract: best-effort observability.
Serializing a cache is not just “serialize a map.” A policy may contain:
Arc<V> sharing state.Restoring only keys and values discards policy warm state. Restoring every internal field exposes private representation and risks accepting corrupted state from disk.
The default position: do not serialize policy internals until there is a specific restore contract for that policy.
If cache-state serialization lands later, it should choose one of two modes per type.
Serialize only entries (K, V) plus capacity/config. On restore, rebuild the
policy as if entries were inserted in serialized order.
Pros:
Cons:
Serialize policy metadata too: list order, frequency counters, clock hand, ghost lists, ARC target, etc.
Pros:
Cons:
Warm-state restore should be opt-in per policy, not a blanket derive.
TTL is the hardest serialization case because monotonic ticks are not portable
across process restarts. The TTL design doc recommends serializing relative
remaining duration, not raw Instant-derived ticks.
Rules for future TTL serialization:
Tick as if it were wall time.This keeps Clock pluggable and avoids replaying meaningless old monotonic
values.
Do not serialize:
RandomState seeds.ShardSelector::randomized key material.FxHashMap iteration order.Serialize semantic data only: keys, values, capacity, policy config, and, if warm restore is explicitly chosen, policy metadata in a stable schema.
ShardSelector::new(shards, seed) is the exception because deterministic
routing is its public contract. If a type exposes deterministic sharding as
part of serialized config, the seed is config data and must be treated as
secret if keys are attacker-controlled.
Arc<V> and SharingSeveral policies and stores use Arc<V>. Serialization should treat Arc<V>
as V, not as identity-preserving shared ownership:
Arc::ptr_eq relationships.If multiple keys point at the same Arc<V>, data-only serialization will
duplicate the value unless the caller provides a higher-level interning scheme.
That is acceptable; cachekit should not infer value identity.
For serialized artifacts controlled by cachekit (benchmark JSON, metrics snapshots), use explicit schema rules:
For serde-derived Rust structs, #[non_exhaustive] is not enough for external
JSON compatibility. A downstream JSON consumer still sees fields. If stable
wire compatibility matters, introduce an explicit versioned artifact type
rather than serializing internal structs directly.
Do not add #[derive(Serialize, Deserialize)] to a policy type just because it
compiles. Check:
If the answer is not clear, add a separate DTO (SerializableLruCache) and a
fallible try_from restore path.
Resultsrc/metrics/snapshot.rsbench-support/src/json_results.rs