fix(locks): live writers are never auto-evicted; evicted writers are fenced at every commit barrier
The production dev-store split-brain (two live writers alternating a store's
id-mapper between two internally-consistent truths), cured at all three of
its roots. (1) STALENESS REQUIRES PID-DEATH: the old rule evicted on
heartbeat age alone, so a >60s event-loop stall (debugger pause, GC, heavy
sync work) handed the lock to a second opener while the first kept writing;
a live process is now never auto-evicted — a wedged-but-alive holder is the
operator's call via {force:true}, and the heartbeat stays for observability.
(2) THE CLAIM IS ATOMIC: writeFile(wx)'s open→write→close left an empty-file
window a concurrent opener could read as torn, unlink a LIVE claim, and take
the lock; the claim is now tmp-write + hard-link — the lock appears with its
full contents in one step. (3) THE FENCE: every flush commit and transact
barrier verifies lock ownership first (one small read per window) — a
forced-out or lock-deleted writer fails typed (BRAINY_WRITER_FENCED) before
a single staged byte or manifest advance, instead of writing on unaware.
Pinned: live-with-ancient-heartbeat refuses typed; dead-PID self-clears
narrated; a forced-out writer's flush and transact both fence, advancing
nothing. Requested by a downstream team as single-writer guard or loud
lockout — this is both.
This commit is contained in:
parent
9ac9e70686
commit
292e7c0406
5 changed files with 225 additions and 8 deletions
|
|
@ -612,6 +612,18 @@ export abstract class BaseStorage extends BaseStorageAdapter {
|
|||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* THE FENCE: verify this instance still owns its writer lock before a
|
||||
* commit barrier proceeds; throw `BRAINY_WRITER_FENCED` if evicted. The
|
||||
* default is a no-op — adapters without a cross-process lock model (memory,
|
||||
* per-request cloud stores) have no eviction to fence against. The
|
||||
* filesystem adapter overrides this; the generation store calls it at
|
||||
* every flush commit and transact barrier.
|
||||
*/
|
||||
public async assertWriterFenceHeld(): Promise<void> {
|
||||
// No-op by default — no lock model, nothing to be evicted from.
|
||||
}
|
||||
|
||||
/**
|
||||
* Start watching for cross-process flush requests. The writer Brainy
|
||||
* instance calls this so that out-of-process inspectors can ask for a
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue