fix(snapshot): the state cursor marks the whole restore, not the tip - #1297
Conversation
The restore pipeline set the cursor at the end of step 5 and rebuilt the live-UTxO index dimensions at step 6, so an interruption between the two left a node `has_existing_data()` reported as restored and that answered address and asset queries with fewer rows rather than an error. `set_cursor` now runs after the rebuild, as the last write of the restore: a node reads as restored only when its `utxo::*` dimensions are there too, and an interruption anywhere leaves a node that reports as empty — which is what it is. `--continue` still repairs it from the epoch checkpoints; without it the stele is restored again from the top over keyed writes, a rewrite and not a duplication. Nothing else moves: `rebuild_utxo_indexes` takes the chain point as an argument and never reads it back off the state store, and the tip is never checkpointed, so a resumed restore already redid exactly the work that now follows the cursor. PROFILE.md §"Restore pipeline" and `restore.rs`'s module doc are amended to say what the order is, superseding the open question they carried. The test pins it through a new `TestFault::IndexApplyError`, which fails only `IndexWriter::apply` — the restore's one call, in the rebuild. Re-implemented from #1283, which could no longer merge once the stelae crates left this workspace. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RR5d8fir268ZksEocd8kbm
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (6)
Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review. 📝 WalkthroughWalkthroughThe Stelae dependencies and snapshot documentation now include blob-index sidecar handling. Restore writes the cursor after live-UTxO index rebuilding. Fault injection tests verify interrupted restores leave no cursor. ChangesStelae restore flow
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: 🔵 Low · up to The restore now marks completion only after live-UTxO indexes are rebuilt, preventing interrupted restores from being treated as complete. A late rebuild failure can still leave a partial index appearing current to direct index consumers, although normal startup remains gated by the state cursor; this is a bounded low-risk consistency issue for owner awareness or follow-up, not a merge blocker. Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant Restore
participant IndexWriter
participant CursorStore
participant CheckpointStore
Restore->>IndexWriter: Rebuild live-UTxO indexes
Restore->>CursorStore: Set and commit cursor
Restore->>CheckpointStore: Clear checkpoint state
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 3 files. (3 skipped: 3 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
stelae's `SteleWriter::seal` now writes the identity→blob map beside `inscription.json` as a `blobs.json` sidecar, and `SteleDir::blob_index` reads it instead of decompressing every blob to rebuild it. Nothing in this crate changes shape for that — `restore_dir` already asks the stele for its map — so what lands here is the prose: `restore_dir`'s doc, the preflight comment that said `blob_index` reads blobs, and ADR-004's `--output-dir` line, which now names the sidecar among what a publish writes to disk. The stelae pin moves to `v0.2.0`, the release that carries the sidecar — one tag for both crates, per the lockstep rule the dependency comment states. Re-implemented from #1283, which could no longer merge once the stelae crates left this workspace. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RR5d8fir268ZksEocd8kbm
58a79a1 to
83a186e
Compare
Re-implements #1283 on top of the stelae repository split, which left that PR unmergeable. Plan:
plans/dolos-stelae-restore-operability.mdin the txpipe domain root. Thecrates/stelaehalf now lives in txpipe/stelae#1 (branchfeat/dir-blob-sidecar); this PR is the profile-side half.What changed
Part (1) — the state cursor marks the whole restore, not the tip (
20f2347a)set_cursormoves after the live-UTxO index rebuild, as the last write of the restore. A node reads as restored only when itsutxo::*dimensions are there too; an interruption anywhere leaves a nodehas_existing_data()reports as empty, and--continuerepairs it from the epoch checkpoints.crates/snapshot/PROFILE.md§"Restore pipeline" steps 5–6 andrestore.rs's module doc say what the order now is, superseding the open question they carried.TestFault::IndexApplyErrorfails onlyIndexWriter::apply, the restore's one call in the rebuild;a_restore_interrupted_in_the_live_utxo_rebuild_leaves_no_cursorpins the invariant.Part (2) — a directory restore reads the blob map its stele carries (
83a186e9)restore_dir's doc, the preflight comment, and ADR-004's--output-dirline now describe theblobs.jsonsidecar. The code already asks the stele for its map.v0.2.0.crates/snapshot/Cargo.tomlpinsstelaeandstelae-driverto tagv0.2.0, the release that carries the sidecar (perf(dir): a directory states its blob map instead of rebuilding it stelae#1, merged). The interim pin to the branch commitbe10860is gone; this is the pin that merges.Verification
At
83a186e9, locally, against the pinned release:cargo +nightly-2026-08-27 fmt --all -- --check— cleancargo clippy --all-targets --all-features -- -D warnings— cleancargo test -p dolos-snapshot -p dolos-testing --all-features— 185 passed, 0 failed (the ignored suites are the live-registry ones)cargo deny check advisories— okThe same fmt, clippy and test set was also run before the pin moved, against
v0.1.0, and passed.Not in this PR
Closing #1283, whose branch
feat/stelae-dir-blob-sidecarthis supersedes.🤖 Generated with Claude Code
https://claude.ai/code/session_01RR5d8fir268ZksEocd8kbm
Summary by CodeRabbit
New Features
blobs.jsonsidecar file in the output directory alongside blobs and inscription data.Bug Fixes