feat(log): v2 is the LIVE write format — envelope records with minted ints, genesis, sector seals; v1 readable forever
The cutover: new tail segments write format v2 (per-record [type, version, cipherFlag, keyId] envelope; noun/verb after-images carry dense ints MINTED AT APPEND from the id mapper — a rebuilt mapper reproduces assignments exactly; log.genesis opens every new log with the id-space width + a minted brain id; sync() seals to the header-declared sector boundary with reader-invisible pad frames). Existing v1 segments are never rewritten — per-segment decoder dispatch reads both formats and v2 facts map to the exact CommitFact shape all consumers already read. Cutover on a live v1 log: an empty v1 tail re-heads in place; a non-empty one is sealed by rotation, byte-identical. Records reserve the encryption fields (cipherFlag 0 / keyId nil are the only legal values; anything else refuses typed naming the needed newer reader) — crypto-ready with no future bump on the compat surface. Empty-records facts are legal (an all-deduped batch is a real generation — v1 semantics preserved; the refusal there tore a column-store flush mid-commit in the full suite, the consistency guard caught it loudly, and the root is fixed). Golden byte vectors pinned for the second (native) reader implementation. Pins: cutover 5/5 · codec 54 · kill-matrix stays 11/11.
This commit is contained in:
parent
73eb88d481
commit
26c6025158
6 changed files with 1372 additions and 135 deletions
|
|
@ -46,7 +46,13 @@ import type {
|
|||
TxLogEntry
|
||||
} from './types.js'
|
||||
import { readLogAuthority } from './logAuthority.js'
|
||||
import { FactLog, storageSupportsFactLog, type CommitFact, type FactOp } from './factLog.js'
|
||||
import {
|
||||
FactLog,
|
||||
storageSupportsFactLog,
|
||||
type CommitFact,
|
||||
type FactOp,
|
||||
type FactIntMinter
|
||||
} from './factLog.js'
|
||||
import { GenerationSegmentStore, type FoldGeneration } from './generationSegments.js'
|
||||
import { crc32c } from '../utils/crc32c.js'
|
||||
|
||||
|
|
@ -182,6 +188,22 @@ export class GenerationStore {
|
|||
this.logDurability = mode
|
||||
}
|
||||
|
||||
/**
|
||||
* The fact log's v2 int minter — injected by the OWNER (brainy wires the
|
||||
* metadata index's id mapper here right after the index is ready), because
|
||||
* this store cannot know the mapper. With the minter installed, new fact
|
||||
* segments write the v2 format and after-image records carry minted dense
|
||||
* ints reproducible by an id-mapper rebuild. Survives reopen: `open()`
|
||||
* re-installs it on the fresh {@link FactLog} instance.
|
||||
*/
|
||||
private intMinter: FactIntMinter | null = null
|
||||
|
||||
/** Install the fact log's v2 int minter (see {@link intMinter}). */
|
||||
setIntMinter(mint: FactIntMinter): void {
|
||||
this.intMinter = mint
|
||||
this.factLog?.setIntMinter(mint)
|
||||
}
|
||||
|
||||
/** Latest reserved/observed generation (≥ {@link committed}). */
|
||||
private counter = 0
|
||||
/** Committed-transaction watermark (manifest generation). */
|
||||
|
|
@ -493,6 +515,7 @@ export class GenerationStore {
|
|||
// hosts no fact log (readers fall back to canonical enumeration).
|
||||
if (storageSupportsFactLog(this.storage)) {
|
||||
this.factLog = new FactLog(this.storage)
|
||||
if (this.intMinter) this.factLog.setIntMinter(this.intMinter)
|
||||
// LOG-AUTHORITY REPLAY (durable-at-ack's recovery half): when this
|
||||
// brain's stored authority is the log, an intact fact ABOVE the
|
||||
// manifest is an ACKED write whose canonical bytes may not have
|
||||
|
|
|
|||
Reference in a new issue