Skip to content

Fixed-size (u128) cache keys #7832

Description

@wjones127

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions