feat(log): the guarded log-authority core — group-commit durable-at-ack, the per-brain switch, the verification oracle
The storage-authority adoption path, guarded shape: the canonical tree
stays authoritative by default ('tree'); a brain flips to 'log' only
through the verification oracle, and the flip is stored, per-brain,
checked at open only.
- FactLog.ensureSynced(): classic group commit — concurrent writers
append, then join ONE covering fsync (running + queued slots give the
covering guarantee: the sync a caller awaits always starts after its
append landed). Solo writer = immediate sync.
- GenerationStore.logDurability 'deferred' (default, byte-identical to
today: fact durability rides the group-commit flush, ack latency
unchanged) | 'at-ack' (log-authority mode: every single-op ack awaits a
covering log fsync — an acked write's fact survives power loss, by
contract). transact() was already durable-at-return in both modes.
- src/db/logAuthority.ts: the stored switch artifact
(_system/log-authority.json, absent = tree), readLogAuthority, and the
VERIFICATION ORACLE — replay the fact log, fold latest state per id
(digests, never bodies — memory-bounded), diff against the canonical
tree paged; verdict green iff every canonical row is exactly reproduced
AND the log claims nothing canonical denies. Divergences are NAMED by
class (pre-log-record → needs baseline backfill; state-differs;
log-live-canonical-absent; log-tombstone-canonical-present). The flip
REFUSES on red with the first divergence and the cure in the message.
- Brainy: authority read at open (log → durable-at-ack enabled);
logAuthority() / verifyLogAuthority() / adoptLogAuthority() public API.
Nothing flips by itself; nothing changes for existing brains.
This commit is contained in:
parent
9fda6d9566
commit
6595309765
4 changed files with 409 additions and 3 deletions
|
|
@ -134,6 +134,22 @@ export class GenerationStore {
|
|||
*/
|
||||
private factLog: FactLog | null = null
|
||||
|
||||
/**
|
||||
* Fact-log durability mode. 'deferred' (default) = the fact becomes
|
||||
* durable at the group-commit flush, together with the buffered history —
|
||||
* the pre-log-authority contract, zero added ack latency. 'at-ack' =
|
||||
* every single-op ack awaits a covering log fsync (shared via the log's
|
||||
* group commit) — the log-authority contract: an acked write's fact
|
||||
* survives power loss. Set by the owner from the stored authority switch
|
||||
* at open; transact() is durable-at-return in BOTH modes (unchanged).
|
||||
*/
|
||||
private logDurability: 'deferred' | 'at-ack' = 'deferred'
|
||||
|
||||
/** Switch the fact-log durability mode (see {@link logDurability}). */
|
||||
setLogDurability(mode: 'deferred' | 'at-ack'): void {
|
||||
this.logDurability = mode
|
||||
}
|
||||
|
||||
/** Latest reserved/observed generation (≥ {@link committed}). */
|
||||
private counter = 0
|
||||
/** Committed-transaction watermark (manifest generation). */
|
||||
|
|
@ -1270,13 +1286,23 @@ export class GenerationStore {
|
|||
// Fact log (dual-write): the acked write's AFTER-IMAGE fact, appended
|
||||
// now (read back warm, under the mutex — group-commit means flush-time
|
||||
// canonical only holds the LATEST state, so each generation's after-image
|
||||
// exists only here). Durability rides the group-commit flush, exactly
|
||||
// like the buffered before-image history: a crash before the flush loses
|
||||
// the fact AND the generation together — never a torn state.
|
||||
// exists only here).
|
||||
//
|
||||
// Durability is MODE-GOVERNED:
|
||||
// - 'deferred' (default, the pre-log-authority behavior): durability
|
||||
// rides the group-commit flush like the buffered history — a crash
|
||||
// before the flush loses the fact AND the generation together, never
|
||||
// a torn state.
|
||||
// - 'at-ack' (log-authority mode): the ack awaits a covering fsync via
|
||||
// the log's group-commit (many concurrent writers share ONE sync) —
|
||||
// an acked write's fact survives power loss, by contract.
|
||||
if (this.factLog) {
|
||||
await this.factLog.append(
|
||||
await this.buildCommitFact({ generation: gen, timestamp, nouns, verbs })
|
||||
)
|
||||
if (this.logDurability === 'at-ack') {
|
||||
await this.factLog.ensureSynced()
|
||||
}
|
||||
}
|
||||
this.schedulePendingFlush()
|
||||
return { generation: gen, timestamp }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue