CacheKit Docs

High-performance cache policies and supporting data structures.

View the Project on GitHub OxidizeLabs/cachekit

Serialization

Status: design rationale for the current serde feature and the boundaries around future cache-state persistence. Companion to metrics.md, ttl.md, and builder-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.

Current Surface

With features = ["serde"], these public value types derive serde:

Properties:

No policy type implements serde today. No store type serializes entries today.

Why Metrics Are Safe To Serialize

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.

Why Cache State Is Not Serialized

Serializing a cache is not just “serialize a map.” A policy may contain:

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.

Two Possible Future Modes

If cache-state serialization lands later, it should choose one of two modes per type.

Data-only restore

Serialize only entries (K, V) plus capacity/config. On restore, rebuild the policy as if entries were inserted in serialized order.

Pros:

Cons:

Warm-state restore

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 and Time

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:

This keeps Clock pluggable and avoids replaying meaningless old monotonic values.

Hash Seeds and Map Order

Do not serialize:

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 Sharing

Several policies and stores use Arc<V>. Serialization should treat Arc<V> as V, not as identity-preserving shared ownership:

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.

Schema Discipline

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.

What Not To Derive

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.

See Also