fix(open): pending-embed recovery keeps the crash-recovery contract — foreground, bounded by the mark
Some checks failed
CI / Node 24 (push) Successful in 12m24s
CI / Node 22 (push) Successful in 12m35s
CI / Integration + conformance (Node 22) (push) Failing after 17m18s
CI / Bun (latest) (push) Successful in 12m28s
Some checks failed
CI / Node 24 (push) Successful in 12m24s
CI / Node 22 (push) Successful in 12m35s
CI / Integration + conformance (Node 22) (push) Failing after 17m18s
CI / Bun (latest) (push) Successful in 12m28s
The delta gate caught the backgrounded fold breaking six pinned crash-recovery cases: a reopened brain must have its markers re-armed when open() returns, and a background latch races every consumer of that contract. The backgrounding is reverted; the low-water mark stays — it is the part that kills the whole-history scan, and with it the foreground fold costs the log's tail on any brain that has ever drained. The unmarked first open after upgrade pays one full scan, once, and the open narrates it as its own step.
This commit is contained in:
parent
88e79729d3
commit
8a2ebacf02
2 changed files with 37 additions and 51 deletions
|
|
@ -6,9 +6,8 @@
|
|||
* on the open's foreground — O(whole history) per open on long-lived brains.
|
||||
* Now: an advisory low-water mark (`_system/pending_embeds_lowwater.json`)
|
||||
* records the committed generation whenever the pending set drains to empty,
|
||||
* recovery scans from `mark + 1`, and the fold runs behind the doors as a
|
||||
* latched background task the worker, `awaitPendingEmbeds()` and `close()`
|
||||
* wait on. The mark is advisory: stale-low costs a longer scan, never a
|
||||
* recovery scans from `mark + 1` on the open's foreground — the crash-recovery
|
||||
* contract keeps markers re-armed when open() returns. The mark is advisory: stale-low costs a longer scan, never a
|
||||
* marker — a pending embed enqueued before a crash is still recovered.
|
||||
*/
|
||||
import { describe, it, expect, afterEach, vi } from 'vitest'
|
||||
|
|
@ -20,7 +19,7 @@ import { NounType } from '../../src/types/graphTypes'
|
|||
|
||||
const LOWWATER_PATH = '_system/pending_embeds_lowwater.json'
|
||||
|
||||
describe('pending-embed recovery: bounded by the low-water mark, behind the doors', () => {
|
||||
describe('pending-embed recovery: bounded by the low-water mark', () => {
|
||||
const roots: string[] = []
|
||||
const dir = (): string => {
|
||||
const d = mkdtempSync(join(tmpdir(), 'brainy-lowwater-'))
|
||||
|
|
@ -100,7 +99,6 @@ describe('pending-embed recovery: bounded by the low-water mark, behind the door
|
|||
await (brain as any).storage.releaseWriterLock()
|
||||
|
||||
const brain2 = await open(root)
|
||||
await (brain2 as any)._pendingEmbedRecovery
|
||||
expect(brain2.pendingEmbedCount()).toBeGreaterThan(0)
|
||||
await brain2.awaitPendingEmbeds()
|
||||
expect(brain2.pendingEmbedCount()).toBe(0)
|
||||
|
|
@ -110,7 +108,7 @@ describe('pending-embed recovery: bounded by the low-water mark, behind the door
|
|||
await brain.close().catch(() => undefined)
|
||||
})
|
||||
|
||||
it('open arms the fold as a background latch; awaitPendingEmbeds waits on it', async () => {
|
||||
it('a reopened brain has its pending set settled when open() returns', async () => {
|
||||
const root = dir()
|
||||
const brain = await open(root)
|
||||
await brain.add({ id: 'a-row', data: 'some data', type: NounType.Thing })
|
||||
|
|
@ -118,10 +116,8 @@ describe('pending-embed recovery: bounded by the low-water mark, behind the door
|
|||
await brain.close()
|
||||
|
||||
const brain2 = await open(root)
|
||||
// The latch exists the moment init() returns (writable filesystem brain)…
|
||||
expect((brain2 as any)._pendingEmbedRecovery).not.toBeNull()
|
||||
// …and the barrier settles it before answering.
|
||||
await brain2.awaitPendingEmbeds()
|
||||
// The crash-recovery contract: markers are re-armed by open itself —
|
||||
// no latch, no background race. (Here the drain landed, so zero.)
|
||||
expect(brain2.pendingEmbedCount()).toBe(0)
|
||||
await brain2.close()
|
||||
})
|
||||
|
|
|
|||
Reference in a new issue