Expand description
The disk tier for the KV prefix cache: where a block goes so that a prefix survives eviction from RAM, and a process restart.
One file per block, named by its
BlockHash and sharded into
subdirectories by a hex prefix of that hash, so a machine that has
cached a million prefixes never puts a million entries in one
directory. The payload is the block’s per-layer K and V tensors,
flattened, in the cache’s own dtype – no re-encoding, no
compression: a KV block is already dense float data, and a
compressor would only spend CPU on the request path to lose.
§What a reader is protected from
A cache file outlives the process that wrote it, so every failure mode here is “someone else’s bytes”:
- A torn write. The publish is temp-file +
fsync+rename, which is atomic within a directory on every filesystem ferrox targets – a reader sees the whole file or no file. But a crash mid-writeto the temp file, a truncated copy, or a partial restore from a backup can still leave a short file lying around, so the format records its own total length and a SHA-256 of its body. A file that does not match is refused (BlockFormatError), never partially deserialized. - A different build. The format is versioned with an explicit readable-set; an unknown version is refused rather than guessed at.
- A different model or config. That is
kv_signature’s job, and this module does not duplicate it: a decoded file becomes anUnverifiedBlock, and onlyUnverifiedBlock::verifyagainst the reader’s own expectation produces a usableKvBlock.
§The write-ordering invariant
A write is accepted on one thread and finished on another, so for a while a block is “in the store” without being on disk. The rule that makes that safe, and the one every step below is ordered around:
buffer -> index -> queue. A concurrent reader must never see an index hit for a block that has neither a file nor a buffered payload.
So the payload is reachable before anything claims the block
exists, and on the way out the file is published before the
buffered copy is released. A reader holds the index lock while it
consults the buffer, because “this block is not on disk yet” and
“here is its payload” have to be one decision – as two, the writer
can publish and release in between and the reader finds nothing.
When that invariant does break, the reader gets
StoreError::MissingPayload rather than a quiet miss: a
correctness bug that degrades into a cache miss is a bug nobody ever
finds.
The queue is bounded and never drops: a full queue makes the
caller write the block itself (DiskStats::inline_writes counts
it). Dropping writes silently would be indistinguishable from a cold
cache later.
§Layout
<root>/.tmp/<hash>.<pid>.<n>.tmp in-progress writes
<root>/<hh>/<full-hex>.kvb published blocks (hh = shard prefix)§File format (version 2)
magic 8 b"FRXKVBLK"
format_version 4 u32 LE, checked against READABLE_FORMAT_VERSIONS
header_len 4 u32 LE
body_len 8 u64 LE
digest 32 SHA-256 over header || body
header header_len block hash, dims, dtype, block layout, model identity
body body_len per layer: all K elements, then all V elementsVersion 2 added the two block-layout fields – block size and
sliding window – and version 1 was dropped from the readable set
rather than being read with the window assumed absent. A v1 file
cannot say what window it was cut under, and “it did not say” is not
“there was none”: see kv_swa for what a
mis-aligned block does to an answer. A restart onto this build
therefore starts from a cold cache once, and the old files are
evicted as unreadable rather than reinterpreted.
The digest covers header and body but not the fixed prefix, so the
lengths are checked against the real file size before anything is
hashed or parsed – a 4 GB body_len on a 200-byte file is rejected
by arithmetic, not by allocating.
Structs§
- Decoded
Block - The identity and payload recovered from a block file. The signature
is deliberately unverified: use
UnverifiedBlock::verifyto turn it into a block this process may use. - Disk
Config - How the store is sized, laid out, and how much writing it will do off the calling thread.
- Disk
KvStore - A content-addressed block store on disk, with its own writer threads.
- Disk
Stats - A snapshot of the tier’s behaviour. Note the two time-valued fields: hit rate alone cannot tell an operator whether a disk hit was cheaper than recomputing the prefix, which is the only question that decides whether the tier is worth having.
- Read
Handle - A read that may not have finished yet.
Enums§
- Block
Format Error - Why a block file was refused. Every variant means “these bytes are not a block this build can read”, and none of them is recoverable by reading harder.
- Store
Error - Something went wrong reaching the disk tier. Corruption and
incompatibility are not here: those are misses, reported through
DiskStats, because a caller’s only sane response to either is to recompute the prefix.
Constants§
- BLOCK_
FILE_ EXT - Extension of a published block file.
Functions§
- decode_
block - Parses a block file. Checks, in order: length, magic, format version, declared-vs-actual size, checksum, then structure. Nothing is allocated from a length field until that length has been checked against the bytes actually present.
- encode_
block - Serializes a block. The
hashis stored inside the file as well as in its name, so a block found under a wrong or renamed path can still be checked against the identity it claims. - encoded_
len - Total on-disk size of a block with this signature, header included.
Type Aliases§
- Free
Space Probe - Asks how many bytes are still free on the filesystem holding a
path.
Nonemeans “cannot tell”, and the store then trusts only its configured byte budget. - Read
Outcome - What a read produced.
Ok(None)is a miss (absent, corrupt, or incompatible);Erris I/O, or the write-ordering invariant breaking.