feat(store): durable log + snapshots + restore + replay (Phase 2)#30
Open
dsrw wants to merge 7 commits into
Open
feat(store): durable log + snapshots + restore + replay (Phase 2)#30dsrw wants to merge 7 commits into
dsrw wants to merge 7 commits into
Conversation
A thread that only uses explicit contexts never mints the implicit Ed.thread_ctx, so the first emitted warn/notice under log_defaults dereferenced a nil active_ctx.
The authority appends every canonical op to append-only JSONL log segments at the stamp point, snapshots full state as per-object files sealed by a manifest, restores from snapshot + tail on restart, and serves read-only historical views via EdContext.replay(path, lsn). The store layout is git-shaped on purpose (immutable rotated segments, per-object snapshot files, codec seam) -- see docs/persistence.md. - publish_changes builds/stamps/appends even with no eligible subscriber: the store is a permanently-eligible subscriber - CREATEs log once at the creation event (defaults / relay_fill), never in per-subscriber publish_create - ctx.replaying bypasses the loopback + own-op guards during restore (logged entries carry our own origin) and suppresses publish/stamp/append; the LSN frontier stays active for idempotency - Message.epoch is now real: bumped per store open, stamped on ordered ops; a follower seeing a higher epoch from an upstream resets its frontier (a restarted authority may reissue LSNs it never made durable) - restore refuses truncated history (no valid snapshot and a log that does not reach LSN 0); a valid snapshot at the current watermark is kept rather than replaced (no delete-then-rename crash window) - build_create extracted from publish_create; contents=false handle pushes skip serialization entirely
22 cases: append/restore roundtrips, own-origin delta replay, headless capture, torn-tail vs mid-file corruption, snapshot+tail, retention, replay_to, epoch bump + hostile-epoch trust gate, tombstones, same-id recreate, nested/owned restore + fresh-follower push, PACKED, exactly-once follower capture, format roundtrips.
Stamp ED_SCHEMA_VERSION into every manifest's reserved schema slot; open_store/replay refuse a store whose slot differs from the running build (StoreError, overridable via allow_schema_mismatch). Bump the constant when a persisted type changes shape -- tid = hash(name) can't see a field/enum change, so a mismatched build would otherwise deserialize garbage silently. The manual half of schema safety until structure-aware tids land; the slot then gains automatic structural detection.
The forward direction reasoned through alongside Phase 2: the trust / authority / topology models (Model 1 target, Model 2 open), the sequencing-vs-distribution scaling story (relay trees + spatial interest), state roots without SSZ (per-object content hash), and the single-serializer decision (adopt nim-serialization, retire flatty; keep + extend the type registry as the schema owner). Companion to consistency-and-partial-sync-plan.md; cross-linked.
- persistence.md: codec seam takes over from flatty (not 'beside'), matching the decided single-serializer cutover; note the planned PERSIST flag that replaces persist-iff-synced. - decentralization-and-scaling.md: mark the manifest schema-version gate done (it shipped), not a pending 'now' item.
Adopting a schema-capable serializer for the durable path is needed; dropping flatty entirely (one codec for both jobs) is the lean, not a constraint -- keeping flatty for the hot path is an acceptable outcome if unifying isn't clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lands roadmap Phase 2 (docs/consistency-and-partial-sync-plan.md): the authority appends its canonical op stream to a durable, git-shaped store — append-only JSONL log segments + per-object snapshot directories — restores from snapshot + tail on restart, and serves read-only historical views via
EdContext.replay(path, lsn). Design + rationale: docs/persistence.md.Highlights
stamp_lsnand fanout; the store is a permanently-eligible subscriber, so a headless authority still captures its ops. CREATEs log once at the creation event, never in per-subscriberpublish_create.ctx.replayingbypasses the loopback/own-op guards during restore (entries carry our own origin — the delta short-circuit would otherwise drop every self-originated collection op) and suppresses publish/stamp/append. The LSN frontier stays active, making double-covered segments idempotent.Message.epochis now real: bumped per store open, stamped on ordered ops; a follower seeing a higher epoch from an upstream resets its frontier, closing the fanout-before-durable crash window under the defaultFlushPerTickdurability.StoreError) instead of silently canonizing a near-empty world.v/txn/commit, manifestschema,codecseam for a future human-readable payload codec, platform (endian/int-width) guard.log_defaultsdereferenced a nil thread ctx on threads using only explicit contexts.Verification
nimble test195/195 (22 new persistence cases: torn-tail vs mid-file corruption, hostile-epoch trust gate, same-id recreate replay, nested/owned restore + fresh-follower push, exactly-once follower capture, ...)kill -9'd mid-stream → committed ops survived, un-ticked tail correctly lost, epoch bumped, time-travel view correct, network follower re-synced from the restored authorityDeferred (deliberately)
Serving fetch misses from the log (lands with authority eviction), TypeSchema / structure-aware tids (slots reserved), the ack/commit callback, git integration itself. Known pre-existing gap documented in persistence.md: the single-subscriber return-to-source echo violation.
🤖 Generated with Claude Code
https://claude.ai/code/session_014o1niF74BvbfxYU1oWEQ38