0032. D-server on-disk fragment layout (FsChunkStore)
Context
The ChunkStore trait is addressed by FragmentId (chunk id + fragment index), never by
path: put_fragment(FragmentId, Bytes) / get_fragment(FragmentId). How a backend turns a
FragmentId into physical storage is exactly what the trait abstracts, and pluggable backends
are a deliberate design property (ADR-0008, ADR-0010): a future object-store or sharded backend
is meant to map FragmentId differently while interoperating perfectly, because the only
cross-implementation contract is the fragment byte format (ADR-0019, specs/chunk-format) plus
the trait — not the bytes' physical placement.
FsChunkStore (crates/chunkstore-fs) is the one on-disk ChunkStore implementation today. It
maps FragmentId { chunk: u128, index: u16 } to root/<32-hex chunk-id>/<5-digit ec-fragment-index>.frag — a directory per chunk, one file per fragment index — written
crash-safely via a sibling temp file and rename (crates/chunkstore-fs/src/lib.rs:203-206,
:81-83). get/delete recompute the path directly (no scan); list_fragments is the
two-level inverse walk (:99-136). A fragment's authoritative identity lives in its header
(chunk_id, ec_fragment_index) and is re-verified against the path-derived id on every read
(:53-66), so the path is a lookup index, not the source of truth.
This layout has never been recorded as a decision; it was discovered in code, not chosen on the
record. Two facts make recording it worthwhile. First, FsChunkStore is described in-code as the
"dev and NAS profile" store, but the networked production D server injects it today
(crates/server/src/cli.rs:267; crates/chunkstore-grpc/src/server.rs:21 names it "the
production injection") — so a dev/NAS-scoped layout is currently also the production backend, with
nothing flagging the tension. Second, the layout has a scaling characteristic worth stating
plainly: the chunk directories are not themselves sharded — the root holds one sub-directory
per chunk with no prefix fan-out — and because failure-domain placement puts at most one
fragment of a chunk on any one D server, the root directory grows roughly one entry per stored
fragment, while list_fragments is O(fragments) and materializes every id into a Vec.
Decision
-
Physical layout is a per-backend implementation detail, not a normative contract. The normative, cross-implementation contracts are the fragment byte format (ADR-0019) and the
FragmentId-addressedChunkStoretrait. A conformantChunkStoreMAY place bytes however it likes (object-store keys, a sharded tree, a database). The chunk-format spec is not extended to cover directories; doing so would forbid the backend pluggability ADR-0008 / ADR-0010 rely on. -
Record
FsChunkStore's layout as it stands.root/<32-hex chunk-id>/<5-digit ec-fragment-index>.frag, written via temp-then-rename;list_fragmentsis the two-level inverse walk;get/deleteare direct path computations. Identity is authoritative in the fragment header and verified against the path on every read — the path is an index, not the source of truth. -
Scope and fitness.
FsChunkStoretargets the dev / single-binary / NAS profiles and serves as the interim production networked-D-server backend until the at-scaleChunkStoreswap lands (ADR-0008, ADR-0010). The current layout is fit for dev / NAS / small deployments. It is not claimed fit for a high-fragment-count production D server: the root directory does not fan out, andlist_fragmentsis O(N) and fully materialized, so enumeration cost, directory-block and inode overhead, and (on ext4) the htree index ceiling become binding as fragment count per server rises. The at-scale backend's layout — prefix fan-out, streaming enumeration, or an object store — is a separate future decision; this ADR records that it is owed, and does not pre-decide it. -
Within-backend longevity is the format's job, not the path's. Because each fragment is self-describing and validates standalone, an out-of-band backup / salvage / fsck tool can recover a D server's fragments by decoding files and reading their headers, independent of the directory convention — consistent with the backup-bootstrap rule (section 8.2) and the disaster-recovery ordering (section 6.5). If such tooling is built and chooses to rely on the directory convention, that reliance is documented with the tool, still not in the chunk-format spec.
Consequences
- The pluggability claim (ADR-0008) is protected on the record: a future object-store or prefix-sharded backend is explicitly conformant, because physical placement was never part of the contract.
- The previously-undocumented scaling characteristic is now on the record — a flag for anyone
sizing a production
FsChunkStoredeployment and a stated prerequisite for the at-scale backend. This ADR records the characteristic; it does not change the layout. - A future change to
FsChunkStore's own layout (e.g. adding prefix fan-out) is a backend-internal data-migration concern for already-written fragments, not aformat_versionbreak (the byte format is untouched). Existing on-disk data must remain readable or be migrated; this ADR flags that obligation without choosing the mechanism. - ADR-0019 is unaffected and unsuperseded: it governs the fragment byte format, this ADR governs one backend's directory layout. They are adjacent, not in tension.
- No code changes. The at-scale
ChunkStorebackend, the prefix-fan-out / streaming-enumeration decision, and any out-of-band salvage tooling remain future work, tracked separately.