fix(restore): a restore is an unclean event — the swap runs quiesced and the snapshot's durability stamps never survive it
Two defects with one root, found by the fold-checkpoint work's first integration gate. (1) THE RACE: restore() never quiesced the generation store, so a background flush could write into _system/ while the swap was removing it — observed as ENOTEMPTY mid-swap when a checkpoint stamp landed between readdir and rmdir. The swap now runs inside the store's exclusive section (runStateReplacement): flush timer disarmed, pending tier and checkpoint accumulator discarded BEFORE any directory moves. (2) THE INHERITED ASSERTION: a snapshot carries its source brain's clean-shutdown marker and fold checkpoint, but the restored files were bulk-copied without per-file fsync — the inherited stamps would suppress exactly the recovery fold that cures a post-restore power cut. reopenAfterRestore now deletes both stamps before reopening: the open treats the store as uncleanly shut, folds the restored log into canonical, barrier-syncs what it re-applied, and stamps fresh — the restored state is durably founded at restore time instead of borrowing assertions about bytes this disk never synced. Pinned: restore under in-flight traffic completes; the pre-restore stamp does not survive; the post-restore stamp is the reopen fold's own, at the restored watermark.
This commit is contained in:
parent
ff43de1ada
commit
9ca80667c3
3 changed files with 85 additions and 1 deletions
|
|
@ -9229,7 +9229,13 @@ export class Brainy<T = any> implements BrainyInterface<T> {
|
|||
}
|
||||
|
||||
const floorGeneration = this.generationStore.generation()
|
||||
await this.storage.restoreFromDirectory(path)
|
||||
// The swap runs inside the generation store's exclusive section: pending
|
||||
// flush timers are disarmed and buffers discarded BEFORE any directory is
|
||||
// removed, so a background flush can never write into `_system/` mid-swap
|
||||
// (the ENOTEMPTY race a checkpoint stamp once hit).
|
||||
await this.generationStore.runStateReplacement(() =>
|
||||
this.storage.restoreFromDirectory(path)
|
||||
)
|
||||
await this.generationStore.reopenAfterRestore(floorGeneration)
|
||||
|
||||
// If the entity-id mapper is a NATIVE provider with a `rebuild()`, reload it
|
||||
|
|
|
|||
Reference in a new issue