test(fold-checkpoint): the ARM-AT-FLIP pin arms its crash instead of racing the pending-flush timer
The pin wrote a post-flip row, abandoned the brain as crashed, and then deleted the row's canonical bytes to prove the first post-flip boot folds BOUNDED above the flip's stamp. Between the write's ack and the abandon sat the store's 50ms pending-flush timer: on a loaded box (the plant lane) the flush won, barrier-synced the row and advanced the checkpoint over it — and the fold, correctly bounded, did not restore bytes the test had destroyed after they were stamped durable. Green locally, red on the plant: the engine was right, the pin was timing-dependent. The crash is now armed at exactly singleop-after-fact-append: the fact is appended and at-ack synced, no flush is ever scheduled, the stamp provably still reads the flip's value when the pre-flip bytes are dropped, and the post-flip row's bytes — which lived only in the pending tier's RAM — are lost for real, not synthetically. The reopen must re-materialize it from its fact and must not restore the pre-flip row.
This commit is contained in:
parent
116550eb16
commit
4176439ba3
1 changed files with 26 additions and 7 deletions
|
|
@ -23,6 +23,7 @@ import { Brainy } from '../../src/brainy.js'
|
|||
import { NounType } from '../../src/types/graphTypes.js'
|
||||
import {
|
||||
abandonAsCrashed,
|
||||
armCrash,
|
||||
dropCanonicalNoun,
|
||||
makeTempDir,
|
||||
openBrain,
|
||||
|
|
@ -177,15 +178,33 @@ describe('fold-checkpoint bound — crash recovery folds (checkpoint, head], nev
|
|||
expect(founded, 'checkpoint founded at flip').toBe(committedOf(brain))
|
||||
|
||||
// First post-flip boot, unclean (the production first-restart shape):
|
||||
// a post-flip write above the checkpoint is restored FROM ITS AT-ACK FACT
|
||||
// (deliberately NOT flushed — a flush would barrier-sync it and advance
|
||||
// the stamp over it, making its loss synthetic); the pre-flip row (its
|
||||
// baseline fact ≤ checkpoint, its bytes barrier-synced at the flip) is
|
||||
// OUTSIDE the fold — vaporizing it synthetically proves the bound.
|
||||
const postFlip = await brain.add({ data: 'post-flip write', type: NounType.Document, metadata: { era: 'log' } })
|
||||
// a post-flip write above the checkpoint is restored FROM ITS AT-ACK FACT;
|
||||
// the pre-flip row (its baseline fact ≤ checkpoint, its bytes barrier-
|
||||
// synced at the flip) is OUTSIDE the fold — vaporizing it synthetically
|
||||
// proves the bound.
|
||||
//
|
||||
// THE CRASH IS ARMED, NOT RACED. The post-flip write "dies" at exactly
|
||||
// `singleop-after-fact-append`: its fact is in the log and at-ack synced,
|
||||
// and NO pending flush was ever scheduled — so the checkpoint provably
|
||||
// still reads the flip's stamp when the bytes are dropped. The earlier
|
||||
// shape (`add()` then abandon) raced the store's 50ms pending-flush
|
||||
// timer: on a loaded box the flush won, barrier-synced the row, advanced
|
||||
// the stamp over it — and the fold, CORRECTLY bounded, did not restore
|
||||
// bytes the test had synthetically destroyed after they were stamped
|
||||
// durable. The plant lane caught it; the engine was right, the pin was
|
||||
// timing-dependent.
|
||||
const postFlip = `post-flip-${Date.now().toString(36)}-0000-4000-8000-000000000000`
|
||||
const arm = armCrash(brain, 'singleop-after-fact-append')
|
||||
await expect(
|
||||
brain.add({ id: postFlip, data: 'post-flip write', type: NounType.Document, metadata: { era: 'log' } })
|
||||
).rejects.toThrow('simulated process crash at singleop-after-fact-append')
|
||||
expect(arm.fired).toContain('singleop-after-fact-append')
|
||||
expect(readCheckpoint(dir), 'the stamp did not move — nothing flushed after the flip').toBe(founded)
|
||||
await abandonAsCrashed(liveBrains.pop()!)
|
||||
// The post-flip row's canonical bytes lived only in the pending tier's
|
||||
// RAM (written at flush, never reached) — the crash takes them for real;
|
||||
// nothing to drop. Only the pre-flip row is vaporized synthetically.
|
||||
dropCanonicalNoun(dir, preFlip)
|
||||
dropCanonicalNoun(dir, postFlip)
|
||||
|
||||
const reopened = await openBrain(dir, { logAuthority: 'adopt' })
|
||||
liveBrains.push(reopened)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue