Skip to content

Guard FlatKV rollback floor - #4496

Merged
alexander-sei merged 3 commits into
mainfrom
yiren/flatkv-rollback-floor
Oct 7, 2026
Merged

alexander-sei merged 3 commits into
mainfrom
yiren/flatkv-rollback-floor

Conversation

@blindchaser

@blindchaser blindchaser commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Guard FlatKV's retained-history floor before composite rollback mutates any backend, and treat stale idle FlatKV directories as disposable Auto-mode seeds instead of reconcilable state.

  • sei-db/state_db/sc/flatkv/snapshot.go: adds a single read-only reachability check for snapshots plus WAL. Rollback still uses the same check before any mutation. Only "no snapshot at or below the target" is tagged ErrVersionUnreachable; filesystem errors from the snapshot scan pass through untagged.
  • sei-db/state_db/sc/flatkv/store.go: rejects unreachable historical read-only loads before creating a readonly-* working directory.
  • sei-db/state_db/giga/types/live_state_store.go: exposes CheckVersionReachable so composite rollback can preflight the FlatKV backend, and owns its error contract, ErrVersionUnreachable (the only non-fatal error of the interface). FlatKV keeps flatkv.ErrVersionUnreachable as an alias.
  • sei-db/state_db/sc/composite/store.go: refuses rollback targets FlatKV cannot reach before memIAVL moves, and points operators to state sync. Before seeding or reconciliation, it removes an idle Auto-mode FlatKV dir whose seed is above memIAVL or more than one block below it. An idle seed exactly one block behind is kept for reconcileVersions, because that is what a crash between the memIAVL and FlatKV commits of the kickoff block leaves.
  • sei-db/state_db/sc/composite/store_test.go: updates the test fake for the new interface.
  • sei-db/state_db/sc/flatkv/store_test.go: updates the beyond-WAL read-only test for the new early reachability failure.

Test plan

  • sei-db/state_db/sc/composite/rollback_floor_test.go: covers rollback to K-2 refusal without mutation, historical read/export below the floor returning errors without panic, rollback to K-1 followed by re-kickoff, delayed kickoff after a stale idle FlatKV dir, a torn kickoff commit (memIAVL at K, FlatKV seed at K-1) that must roll memIAVL back and replay K to the canonical AppHash, and state sync from a pre-kickoff snapshot with replay through migration.
  • sei-db/state_db/sc/flatkv/snapshot_test.go: an empty snapshot dir is ErrVersionUnreachable; a snapshot-scan read failure is not.
  • sei-db/state_db/sc/flatkv/store_replay_test.go: covers snapshot-only reachability, snapshot-plus-WAL reachability, below-history errors, and failing read-only loads before creating a read-only work dir.
  • go test ./sei-db/state_db/sc/flatkv ./sei-db/state_db/sc/composite
  • make fmtcheck
  • make dblint
  • go test -race ./sei-db/state_db/sc/composite/... ./sei-db/state_db/sc/flatkv/... ./sei-db/state_db/giga/... ./sei-db/bootstrap/...
  • scripts/ramtest.sh ./sei-db/... failed once in unrelated TestRapidFlushes with "Expected at most 25 flushes, got 32"; rerunning scripts/ramtest.sh ./sei-db/db_engine/litt/disktable passed.

Co-authored-by: Cursor <cursoragent@cursor.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-07T05:18:53.282860Z 9410286 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cursor

cursor Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes rollback, historical read, and Auto-mode migration startup paths in the composite/FlatKV commit stack; behavior is safer but operators hitting the new floor must state sync instead of rolling back.

Overview
Introduces a non-fatal ErrVersionUnreachable and CheckVersionReachable on LiveStateStore, so callers can tell when FlatKV cannot reconstruct a height from retained snapshots plus WAL.

FlatKV centralizes that logic in reachableBaseVersion (shared by rollback and the new check), tags true “history floor” failures with ErrVersionUnreachable, and preflights LoadVersionReadOnly so unreachable targets fail before creating readonly-* work dirs.

Composite blocks rollback (and enriches version-reconcile rollback errors) when FlatKV cannot reach the target, with guidance to use state sync; on Auto LoadLatest, it drops stale idle FlatKV dirs whose seed is above memIAVL or more than one block behind, while keeping a seed exactly one block behind for torn kickoff recovery.

New rollback_floor_test.go and FlatKV tests cover refusal below the floor, re-kickoff after rollback to K−1, stale idle cleanup, torn kickoff replay, and pre-kickoff snapshot import through migration.

Reviewed by Cursor Bugbot for commit f5c8937. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedOct 7, 2026, 7:02 AM

@codecov

codecov Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.92308% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 57.10%. Comparing base (055503e) to head (f5c8937).

Files with missing lines Patch % Lines
sei-db/state_db/sc/composite/store.go 75.75% 8 Missing ⚠️
sei-db/state_db/sc/flatkv/snapshot.go 75.00% 4 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #4496   +/-   ##
=======================================
  Coverage   57.10%   57.10%           
=======================================
  Files        2127     2127           
  Lines      166451   166488   +37     
=======================================
+ Hits        95054    95076   +22     
- Misses      71392    71407   +15     
  Partials        5        5           
Flag Coverage Δ
sei-chain 55.29% <ø> (-0.01%) ⬇️
sei-db 75.17% <ø> (ø)
sei-db-state-db 78.89% <76.92%> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
sei-db/state_db/sc/flatkv/store.go 79.38% <100.00%> (-0.24%) ⬇️
sei-db/state_db/sc/flatkv/snapshot.go 76.39% <75.00%> (+0.41%) ⬆️
sei-db/state_db/sc/composite/store.go 80.00% <75.75%> (+0.02%) ⬆️

... and 27 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94102864e1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// CheckVersionReachable verifies that targetVersion can be reconstructed from this store's retained
// snapshots and WAL. It does not modify snapshot, symlink, database, or WAL state.
func (s *CommitStore) CheckVersionReachable(targetVersion int64) error {
_, err := s.reachableBaseVersion(s.flatkvDir(), targetVersion)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Serialize reachability checks with snapshot pruning

CheckVersionReachable calls seekSnapshot directly, outside the SnapshotWriter goroutine, while live stores may publish and prune snapshots concurrently. As documented by SnapshotWriter.CloneSnapshot in snapshot_writer.go, resolving a snapshot on the caller thread allows pruning to delete it between resolution and copying; the new preflight can therefore approve a snapshot that disappears before LoadVersionReadOnly clones it, causing historical reads and exports of an otherwise reachable version to fail intermittently. Route the online reachability check through the writer-serialized snapshot path rather than inspecting the snapshot directory independently.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably worth doing.

seidroid[bot]
seidroid Bot previously requested changes Oct 7, 2026

@seidroid seidroid Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds a read-only FlatKV reachability preflight (ErrVersionUnreachable, CheckVersionReachable) so composite rollback and historical read-only loads are refused before anything is mutated, and deletes stale idle Auto-mode FlatKV directories on load. It is blocked because the new discard path also catches a crash between the memIAVL and FlatKV commits of the kickoff block, which the old reconcile handled correctly; the only other reading (codex) found nothing, so it added no findings and this blocker contradicts it.

Blocking

1 finding on the changed lines, as inline comments.

  • No test covers a torn commit on the kickoff block in Auto mode: memIAVL committed at K, FlatKV left at its seed K-1. That is the crash-recovery case the new discardStaleIdleFlatKV path changes. The new crash_in_kickoff_block and stale-idle tests only close the store before either backend commits, so they never reach the case that regresses.

Non-blocking

1 finding on the changed lines, as inline comments.

seidroid review · decision request_changes · session 2fbdda8e80564efeab67728e5074dd55 · turn resp_claude_9e39aab20adaf8349e2956497b81eed2 · item 9625cf3d8a2158b793096826a9187b0f

Findings: 2 blocking | 1 non-blocking | 2 posted inline

Comment thread sei-db/state_db/sc/composite/store.go Outdated
Comment thread sei-db/state_db/sc/flatkv/snapshot.go Outdated
…unreachable

Co-authored-by: Cursor <cursoragent@cursor.com>

@seidroid seidroid Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since my last review, the discard of an idle Auto-mode FlatKV is limited by isStaleSeed to seeds above memIAVL or more than one block behind it, so a torn kickoff commit now goes to reconcileVersions, and a new test replays block K to the canonical AppHash; seekSnapshot now tags only a missing snapshot as ErrVersionUnreachable, which fixes both of my earlier findings. Nothing blocks; codex again found nothing and contributed no findings.

Non-blocking

  • The stale-seed cleanup runs only in LoadLatest. materializeFlatKV still adopts an existing non-zero idle seed without checking it against memIAVL, so a kickoff that fires in-process after the node has committed past an idle seed would still hit the commit version mismatch. The kickoff trigger is deterministic chain state, so this needs an unusual sequence, but a matching guard there would close the remaining path.

seidroid review · decision approve · session 2fbdda8e80564efeab67728e5074dd55 · turn resp_claude_ee6e2929a6b2cefc751bd2f4538c2a69 · item 5437a4fa02c25eb59b8913d6a1a4102d

Findings: 0 blocking | 1 non-blocking | 0 posted inline

@seidroid
seidroid Bot dismissed their stale review October 7, 2026 06:01

Superseded: the latest review found nothing blocking in this change.

The LiveStateStore interface now owns the CheckVersionReachable error
contract. Shared reachability messages no longer mention rollback, and
the tests use require.ErrorIs, a named subtest table and a noKickoff
constant.

Co-authored-by: Cursor <cursoragent@cursor.com>
@blindchaser blindchaser added the backport release/v6.7 Backport to release v6.7 label Oct 7, 2026
@blindchaser
blindchaser added this pull request to the merge queue Oct 7, 2026
@alexander-sei
alexander-sei removed this pull request from the merge queue due to a manual request Oct 7, 2026
@alexander-sei
alexander-sei added this pull request to the merge queue Oct 7, 2026
Merged via the queue into main with commit 8b5e550 Oct 7, 2026
71 checks passed
@alexander-sei
alexander-sei deleted the yiren/flatkv-rollback-floor branch October 7, 2026 16:29
@seidroid

seidroid Bot commented Oct 7, 2026

Copy link
Copy Markdown

Created backport PR for release/v6.7:

Please cherry-pick the changes locally and resolve any conflicts.

git fetch origin backport-4496-to-release/v6.7
git worktree add --checkout .worktree/backport-4496-to-release/v6.7 backport-4496-to-release/v6.7
cd .worktree/backport-4496-to-release/v6.7
git reset --hard HEAD^
git cherry-pick -x 8b5e550e9c5de26a0e0907f9bcc422e446cdc3a3
git push --force-with-lease

blindchaser added a commit that referenced this pull request Oct 7, 2026
Guard FlatKV's retained-history floor before composite rollback mutates
any backend, and treat stale idle FlatKV directories as disposable
Auto-mode seeds instead of reconcilable state.

- `sei-db/state_db/sc/flatkv/snapshot.go`: adds a single read-only
reachability check for snapshots plus WAL. Rollback still uses the same
check before any mutation. Only "no snapshot at or below the target" is
tagged `ErrVersionUnreachable`; filesystem errors from the snapshot scan
pass through untagged.
- `sei-db/state_db/sc/flatkv/store.go`: rejects unreachable historical
read-only loads before creating a `readonly-*` working directory.
- `sei-db/state_db/giga/types/live_state_store.go`: exposes
`CheckVersionReachable` so composite rollback can preflight the FlatKV
backend, and owns its error contract, `ErrVersionUnreachable` (the only
non-fatal error of the interface). FlatKV keeps
`flatkv.ErrVersionUnreachable` as an alias.
- `sei-db/state_db/sc/composite/store.go`: refuses rollback targets
FlatKV cannot reach before memIAVL moves, and points operators to state
sync. Before seeding or reconciliation, it removes an idle Auto-mode
FlatKV dir whose seed is above memIAVL or more than one block below it.
An idle seed exactly one block behind is kept for `reconcileVersions`,
because that is what a crash between the memIAVL and FlatKV commits of
the kickoff block leaves.
- `sei-db/state_db/sc/composite/store_test.go`: updates the test fake
for the new interface.
- `sei-db/state_db/sc/flatkv/store_test.go`: updates the beyond-WAL
read-only test for the new early reachability failure.

- `sei-db/state_db/sc/composite/rollback_floor_test.go`: covers rollback
to K-2 refusal without mutation, historical read/export below the floor
returning errors without panic, rollback to K-1 followed by re-kickoff,
delayed kickoff after a stale idle FlatKV dir, a torn kickoff commit
(memIAVL at K, FlatKV seed at K-1) that must roll memIAVL back and
replay K to the canonical AppHash, and state sync from a pre-kickoff
snapshot with replay through migration.
- `sei-db/state_db/sc/flatkv/snapshot_test.go`: an empty snapshot dir is
`ErrVersionUnreachable`; a snapshot-scan read failure is not.
- `sei-db/state_db/sc/flatkv/store_replay_test.go`: covers snapshot-only
reachability, snapshot-plus-WAL reachability, below-history errors, and
failing read-only loads before creating a read-only work dir.
- `go test ./sei-db/state_db/sc/flatkv ./sei-db/state_db/sc/composite`
- `make fmtcheck`
- `make dblint`
- `go test -race ./sei-db/state_db/sc/composite/...
./sei-db/state_db/sc/flatkv/... ./sei-db/state_db/giga/...
./sei-db/bootstrap/...`
- `scripts/ramtest.sh ./sei-db/...` failed once in unrelated
`TestRapidFlushes` with "Expected at most 25 flushes, got 32"; rerunning
`scripts/ramtest.sh ./sei-db/db_engine/litt/disktable` passed.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 8b5e550)
alexander-sei added a commit that referenced this pull request Oct 7, 2026
Backport of #4496 to `release/v6.7`.

Co-authored-by: yirenz <blindchaser@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: alexander-sei <alexanderh@seinetwork.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants