feat(recovery): the fold-checkpoint bound — crash folds (checkpoint, head], never the whole log twice
The fold checkpoint (_system/fold-checkpoint.json) is stamped strictly after a canonical-sync barrier over every live entity touched since the last stamp (syncEntityCanonical: ids → canonical paths → fsync; an absent file fsyncs its parent directory so deletes are as durable as writes). An unclean open under log authority now folds only (checkpoint, head]; the chain bootstraps at an empty brain's adoption (three-phase hooks around adoptLogAuthority) or at a brain's first whole-log fold — existing brains converge at their first crash with zero regression. Rollback restores sync immediately; abort paths feed the barrier; a failed barrier retains the old bound (bigger fold later, never a lost write). Five structural pins including boundedness itself. Also: the production-shaped write-flow gate leg (mixed traffic racing flushes, crash mid-traffic, every ack survives — from a consumer-reported gate miss), and two release-ceremony cures (tag-first push so the publish never queues behind the release commit's CI run; raw-curl npmjs shasum probe with propagation grace instead of a one-shot false divergence).
This commit is contained in:
parent
cbe34d115e
commit
ff43de1ada
8 changed files with 695 additions and 12 deletions
|
|
@ -1390,6 +1390,29 @@ export abstract class BaseStorage extends BaseStorageAdapter {
|
|||
void paths
|
||||
}
|
||||
|
||||
/**
|
||||
* Fold-checkpoint durability barrier: make the listed entities' canonical
|
||||
* live objects durable. Maps each id to its canonical metadata + vector
|
||||
* paths and delegates to {@link BaseStorage.syncRawObjects}, whose
|
||||
* filesystem override fsyncs present files (and their rename directory
|
||||
* entries) and the parent directory of absent ones — so deletes are as
|
||||
* durable as writes. The generation store advances the fold checkpoint
|
||||
* only after this resolves (stamp-after-data).
|
||||
*
|
||||
* @param nouns - Entity ids whose canonical objects must be durable.
|
||||
* @param verbs - Relationship ids whose canonical objects must be durable.
|
||||
*/
|
||||
public async syncEntityCanonical(nouns: string[], verbs: string[]): Promise<void> {
|
||||
const paths: string[] = []
|
||||
for (const id of nouns) {
|
||||
paths.push(getNounMetadataPath(id), getNounVectorPath(id))
|
||||
}
|
||||
for (const id of verbs) {
|
||||
paths.push(getVerbMetadataPath(id), getVerbVectorPath(id))
|
||||
}
|
||||
if (paths.length > 0) await this.syncRawObjects(paths)
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an entity's raw stored objects — the exact bytes at its canonical
|
||||
* metadata + vector paths (write-cache coherent). Used by the generation
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue