Clean Close
In this section
A brain that closes cleanly leaves a store the next process can attach to. A brain that does not — a kill, a crash, a power cut — leaves a store the next process must verify before it attaches. Both are supported states. This page says exactly what each guarantees, what each costs, and why the cost has the shape it has.
What a clean close guarantees
Three things, and only three:
Flushed | Every mutation this process acknowledged is on disk. Nothing acknowledged is in a buffer only. |
Attested | The projection artifacts carry a close-time attestation, so the next open is a manifest load and an attach rather than a proof. |
Unlocked | The writer lock is released, so the next process is not made to decide whether a lock file describes a live writer or a dead one. |
Everything else a close could do, it deliberately does not.
What a close deliberately does NOT do
It does not finish background work. Background jobs are aborted at their next yield, and settled with a bound (10 s). Every one of them carries a durable progress marker, so a job cut short is a job the next open resumes — never a job whose work is lost.
The abort reaches a running body within one installment (50 ms), and it does so uniformly. Every long body calls ctx.yieldToForeground() in its inner loop — that is the installment law — and once the runner has been aborted that call throws. So a body stands down at its next unit of work whether or not its author remembered to check anything. That is the whole of the mechanism, and it is deliberately in one place: it used to be a per-body habit, and MEASURED on 2026-09-03 exactly one body in the engine had it. The metadata heal's canonical walk did not, so a close landing mid-walk waited out its full settle budget while the walk ran on against a closing engine.
The 10 s settle is therefore an outer bound for a body that never yields at all, not the ordinary cost of a close.
MEASURED (src/open/abortReachesEveryBody.e2e.test.ts, on the 32-core box under the gate lock): the same canonical walk over the same 3,000-row store, aborted at page 3 of 29 — without the yield hook it heard nothing, went on to commit all 29 pages and reported a completion 30 ms later; with it, 0 further pages and a cancellation 7 ms later. A real metadata heal over a real 177-row strand, aborted from inside its own walk: 3 ms to stop, 0 further pages, and a settle() behind it that returned in 1 ms. Three close cycles in a row: 1 ms, 1 ms, 0 ms, with consecutiveFailures still zero.
A job cut short is CANCELLED, not failed. The distinction is durable and it matters: a failure writes its reason onto the marker, arms a retry on a doubling backoff, and counts toward the three-strike latch — so three closes landing mid-walk would latch a job that had never once misbehaved. A cancellation leaves the marker exactly at its last checkpoint, arms nothing, and counts for nothing.
All three legs hold that bound, and the vector leg does not wait for its O(store) jobs at all: its close barrier settles the delta drain and the consolidation that folds it — the work a flush actually promises, and O(dirty) both — while the canonical fill, the coverage heal and the mapper maintenance are left exactly where their markers say they reached. Waiting for those was MEASURED as a 102 s close on a production brain whose projection had attached byte-exact: the barrier waited two minutes for every job the index owned, and it waited for the work to FINISH rather than aborting first, so a close that landed mid-walk paid whatever the walk had left.
It does not wait for an online rebuild. A rebuild in flight is stood down, not awaited. Its progress marker is written at every checkpoint rather than at the end, which is precisely what makes waiting unnecessary: the next open resumes the walk from where the marker says it reached.
It does not pay compaction debt. Deferred compaction — the id mapper's, the verb stores' — is tracked by counters that survive the close, and the next open's background maintenance pass folds it behind the doors. Paying it at close was measured as the single largest close cost, and every second of it was a service that had already stopped serving standing in front of one that had not started.
It does not measure the history. The generation log's retention byte cap needs to know what the committed history weighs, and deriving that number is one storage read per committed generation. The close never derives it: the total is durable with the store, and what the store has not measured yet is measured behind the doors of a running brain. See The close never measures the history for the memo's rules, the counters, and what a close does when the number is not yet in hand.
The two history maintenance passes are still paid at close, and they are still bounded. history-repack (re-representation, never deletion) and history-compact (the retention reclaim) each run under a 5,000 ms budget on the close path. They are the one place this page's law is not yet fully applied — they are bounded by a clock rather than deferred to a background pass the way the mapper's and the verb stores' debt is. They cost at most ten seconds per store, they never scale with the store, and moving them behind the doors is a separate change on its own gate.
The result is a close that costs O(dirty) plus those two bounded passes — never O(store) and never O(artifacts).
What it costs
Closing
Settle background jobs | One installment (50 ms) for any body that yields — which is every long body in the engine; ≤ 10 s is the outer bound, and a job that will not settle inside it is named and abandoned to its marker |
Flush | O(dirty) — proportional to what this process changed, not to what the store holds |
Attest | milliseconds |
MEASURED, and the reason this page exists. On a production restart, close cost 230 s on two stores and 31 s on five before the shape above was enforced. The cost was not the flush: it was an awaited rebuild unwind and up to 60 s per leg of compaction paid at close. Both are now deferred to where they belong, and neither loses work.
Booting
After a clean close | Manifest load + attach. No proof, no walk. |
After an unclean stop | Attach verification, then any strand heal, behind the doors — reads are served throughout |
The load-bearing word in the unclean row is behind the doors. An unclean boot does not become a foreground rebuild: the store serves from what it has while the verification and any heal proceed as background work with their own progress markers. A boot that had to rebuild before answering would make an unclean stop a second outage, which is exactly the failure this design refuses.
MEASURED, on the personal brain (15,941 rows) at 11.0.2: a boot whose metadata generation had to be rebuilt took 521 s, of which 351 s was the verb walk — and that 351 s was durability the build did not need, two msyncs per shard per row on a build artifact nobody replays. With the shadow build's logs in bulk-load mode and one batch per page, a build pays barriers per SHARD rather than per row. The barrier count is narrated on every shadow build, so the claim is a number in the log rather than an assertion on this page.
What is not measured here yet
The close and boot cost shape is stated above from a production restart and from the personal brain. The rehearsal fixtures' own numbers — Wicks & Whiskers and the platform copy — are owed and not yet taken, so they are not quoted. When the rehearsal lane records them, they belong in this table rather than in a thread.
Operating notes
A close that narrates deferred compaction debt is working correctly. The line names what was deferred and to where; the next open's background pass pays it.
A close that names a background job as unsettled is also working correctly — that job's marker stands, and the next open resumes it. A body that reaches that line is one that never called
ctx.yieldToForeground(), and the line names it for exactly that reason.A
cancelledjob in an ops log is not an error. It is a body that heard the close and stopped; its marker stands and the next open resumes it. Afailedjob is the different thing — something went wrong, the reason is on the marker, and a retry is armed.If a boot is slow, read the shadow build's narration: it carries rows done out of rows total, the rate, the projected remaining wall, and the barrier count. A build reporting barriers in the tens of thousands has lost bulk-load mode, and that is a defect worth filing with those numbers.