Problem. Cache keys are long heap strings built with format! and accumulating prefixes (e.g. {dataset_uri}{manifest_e_tag}{index_uuid}{sub_index_id}, ~80–150 B). Each get/insert costs two allocations and re-hashes the full key. Key memory rivals value size for small entries.
Proposal. Collapse each logical key into a 128-bit digest via a KeyBuilder that streams bytes into a hasher — zero allocation, hashing ~16 B instead of ~150 B. Serialization must be injective (type tags + length-prefixed variable segments + fixed-endian ints) so distinct logical keys never collide pre-hash.
fn write_key(&self, w: &mut KeyBuilder) {
w.tag(PAGE); // type tag, no allocation
w.u32(self.column_index);
w.u32(self.page_index); // fixed-endian ints hash faster than decimal text
}
Hash function: BLAKE3, truncated to 128 bits. Cryptographic (required — see below), native keyed/derive_key modes, native short output, no length-extension, mature Rust crate. SHA-256 is the FIPS fallback; xxh3 is faster but ruled out — it can't give keyed per-tenant partitioning.
Stability. Fixed, public function → identical digests across restarts and nodes (needed for CacheBackends that are backed by persistent storage). Version the key layout so function/schema changes miss-and-age-out rather than return wrong data. Fix endianness in write_key.
Multi-tenant. Derive a per-tenant key once via blake3::derive_key, then keyed_hash the tail per access. Tenants draw from independent keyspaces — cross-tenant collisions can't be engineered (2^128) or occur accidentally (<2^-128).
Security note. The cache key is not the security barrier; access control upstream is. Old string prefixes gave correctness namespacing, never security (they're guessable). Per-tenant derivation preserves isolation by construction, not by collision improbability.
What this loses:
invalidate_prefix() method is gone.
- Keys can not be introspected.
Problem. Cache keys are long heap strings built with
format!and accumulating prefixes (e.g.{dataset_uri}{manifest_e_tag}{index_uuid}{sub_index_id}, ~80–150 B). Each get/insert costs two allocations and re-hashes the full key. Key memory rivals value size for small entries.Proposal. Collapse each logical key into a 128-bit digest via a
KeyBuilderthat streams bytes into a hasher — zero allocation, hashing ~16 B instead of ~150 B. Serialization must be injective (type tags + length-prefixed variable segments + fixed-endian ints) so distinct logical keys never collide pre-hash.Hash function: BLAKE3, truncated to 128 bits. Cryptographic (required — see below), native keyed/
derive_keymodes, native short output, no length-extension, mature Rust crate. SHA-256 is the FIPS fallback; xxh3 is faster but ruled out — it can't give keyed per-tenant partitioning.Stability. Fixed, public function → identical digests across restarts and nodes (needed for CacheBackends that are backed by persistent storage). Version the key layout so function/schema changes miss-and-age-out rather than return wrong data. Fix endianness in
write_key.Multi-tenant. Derive a per-tenant key once via
blake3::derive_key, thenkeyed_hashthe tail per access. Tenants draw from independent keyspaces — cross-tenant collisions can't be engineered (2^128) or occur accidentally (<2^-128).Security note. The cache key is not the security barrier; access control upstream is. Old string prefixes gave correctness namespacing, never security (they're guessable). Per-tenant derivation preserves isolation by construction, not by collision improbability.
What this loses:
invalidate_prefix()method is gone.