Skip to main content

Module ordered

Module ordered 

Source
Expand description

Ordered writes — durability and ordering without atomicity, for trees where every prefix is a legal state.

A ChangeSet buys all-or-nothing: a journal, rollback, and a recovery precondition, because for the trees it serves a half-applied set is illegal and must be impossible to observe. Not every tree is like that. An append-only store — content-addressed blobs, operation logs, a write-once history — declares every partially-written batch legal: files that arrived without the record that names them are exactly what any interrupted transfer leaves, and the format already has a name for that state. Buying atomicity there pays for a journal to rule out states that were never illegal, and worse, it plants that journal (and a recover-before-read obligation) in a tree that never asked for either.

What such a tree does need is narrower, and this module is exactly it:

  • Ordering. Nothing durable may name anything that is not durable yet. The record naming a payload must never survive a crash the payload did not — a store where it could would hold a name pointing at nothing, which is an illegal state.
  • Durability, when asked for: once apply returns, the batch survives a power cut.

§The protocol

An OrderedBatch is tiers of writes separated by barriers. Within a tier nothing is ordered — payloads may land in any order, because nothing names them yet. At each barrier, everything staged so far is ordered (Durability::Ordered) ahead of everything after it: each tier’s files are flushed to the barrier and each directory that gained an entry is flushed too, since a name in a directory is its own write and persists separately from the bytes it names. The final tier is flushed to whatever finality the caller asks — Durable for “this write survives power loss”, Ordered for “consistent, but the tail may be lost with the crash that interrupted it”.

A crash therefore leaves some prefix of the barriers: every tier before the interruption whole and durable, the interrupted tier possibly partial, everything after it absent. For the store shaped as above, every one of those states is a state it already tolerates.

§What a partial tier can hold

The two op kinds degrade differently inside the interrupted tier, and the difference is the port’s, honestly inherited:

  • A write lands through the backend’s own Storage::replace — its override included — so a crash shows the whole old file or the whole new one wherever the backend can promise that, and the documented degrade where it cannot. Its flush rides with the tier’s, like everything else here: the bytes are barriered inside the call, and the entry that publishes them is the directory flush’s to carry.
  • A create_new is an exclusive create under its final name — decision-grade for concurrency (two writers racing to one name see one winner), but a crash mid-write can leave the newest tier’s file torn. A consumer whose names promise their contents (a digest-named blob) must be able to recognize and discard a torn file nothing names yet; the barrier guarantees the “nothing names it yet” half.

§What this does not do

No journal, no rollback, no recovery step, no stale-journal refusal. An error mid-apply returns immediately and the tree holds a consistent prefix — the same shape a crash leaves — for the caller to complete, retry, or garbage-collect on its own terms. Single writer, like everything in this crate: two appliers against one tree race, and exclusivity beyond one create_new name is the caller’s to arrange.

Structs§

OrderedBatch
Tiers of writes separated by barriers, applied in order by apply: within a tier nothing is ordered, across a barrier everything is.

Enums§

BatchOp
One staged op of an OrderedBatch. Paths are root-relative, joined onto the root at apply time, exactly as FileOp’s are.