Skip to content

Backport release/v6.7: retain 72 FlatKV checkpoints instead of mirroring memIAVL's count - #4146

Closed
blindchaser wants to merge 1 commit into
release/v6.7from
yiren/flatkv-checkpoint-retention-v67
Closed

blindchaser wants to merge 1 commit into
release/v6.7from
yiren/flatkv-checkpoint-retention-v67

Conversation

@blindchaser

Copy link
Copy Markdown
Contributor

Backport of #4145 to release/v6.7. This is the branch the change is actually needed
on: the FlatKV EVM migration ships in v6.7.

What this changes

FlatKV stops taking its retention count from memIAVL and keeps its own default of
72 old checkpoints. The snapshot interval is still mirrored.

before after
FlatKV checkpoints retained 1 72
Guaranteed reach 10,000 blocks (74 min) 720,000 blocks (~89 h)
Extra disk at mainnet state size ~20 GiB

Why

alignFlatKVSnapshotWithMemIAVL overwrote both of FlatKV's snapshot knobs from
memIAVL's sc-* keys, and sc-keep-recent defaults to 1. FlatKV therefore kept one
old checkpoint, and ExternalPruning is false on this path, so nothing else extended
that.

Below FlatKV's reach, migrate-evm-status, dump-flatkv and a cross-backend digest
cannot open a past version, and a rollback has no base snapshot to rewind to. The EVM
migration window became unobservable 74 minutes after passing.

The shared knob cannot express the setting we want, and this is not a matter of cost.
Measured on the mainnet-sized pacific-1 shadow node, post-H_done, at the tip:

one further retained version
memIAVL 56,782 MiB (snapshots are independent full copies)
FlatKV ~286 MiB (261 MiB of pinned SSTs + ~25 MiB of state WAL)

A Pebble checkpoint hardlinks its SSTs, so an old checkpoint only pins the bytes
compaction has since obsoleted. The factor is about 200. At the depth FlatKV wants,
the memIAVL side of a shared count would need 3.68 TiB on a 2 TiB volume.

Why 72

72 checkpoints at the 10,000-block interval is a guaranteed reach of 720,000 blocks,
about 89 hours at the shadow node's measured 2.247 blocks/s, against an 85-hour drain
at NumKeysToMigratePerBlock = 1024. It covers the whole migration window with margin.

The cost is linear in depth: each older checkpoint pins exactly the files obsoleted
during its own interval, and those sets are disjoint by construction. It only
saturates at ~93 checkpoints, where the pinned bytes reach the 24,241 MiB of live SSTs.

How operators get it

The production store is built by parseSCConfigs, which starts from the in-code
defaults and reads no FlatKV retention key, so raising the default reaches every
existing pacific-1 validator with no app.toml edit. Nodes carrying a stale
explicit state-commit.flatkv.snapshot-keep-recent from the pre-#3745 template are
also unaffected, because that key never reaches a store.

This backport deliberately adds no new config read. Making the key readable is a
follow-up on main only.

Differences from #4145

Not a clean cherry-pick. main has since removed the golden config suite (#4057) and
the FlatKV default constants (#4039), so this branch additionally records the new
default in two golden files, and puts the rationale on the existing
DefaultSnapshotKeepRecent constant rather than reintroducing one.

Golden updates, regenerated per package with -update:

  • app/testdata/state-commit.golden: FlatKVConfig.SnapshotKeepRecent uint32(1)uint32(72)
  • sei-cosmos/server/config/testdata/server_config.golden: StateCommit.FlatKVConfig.SnapshotKeepRecent uint32(1)uint32(72)

Testing

  • TestAlignFlatKVSnapshotIntervalWithMemIAVL replaces the test that pinned the
    both-values mirror; it now pins that the interval is mirrored, a zero interval is
    still healed before assignment, and neither a set nor a zero memIAVL keep-recent
    reaches FlatKV.
  • TestFlatKVDefaultRetentionSpansTheMigrationWindow guards the reach itself.
  • ./sei-db/state_db/sc/composite/, ./sei-db/state_db/sc/flatkv/...,
    ./sei-db/config/, ./sei-cosmos/server/config/, ./app/ all pass.
  • make fmtcheck and make dblint are clean.

Release note

v6.7.0-rc1 is already cut, so merging this implies an rc2.

…oring memIAVL's count

FlatKV took both its snapshot interval and its retention count from
memIAVL's sc-* keys, and sc-keep-recent defaults to 1. FlatKV therefore
kept one old checkpoint, a guaranteed reach of 10,000 blocks, or 74
minutes at mainnet's block rate. Below that reach migrate-evm-status,
dump-flatkv and a cross-backend digest cannot open a past version, and a
rollback has no base snapshot to rewind to, so the EVM migration window
was unobservable within an hour of passing.

A single shared count cannot serve both backends, because it is a disk
decision and not a cadence. Measured on a mainnet-sized shadow node, one
further retained memIAVL snapshot costs 56,782 MiB, since memIAVL
snapshots are independent full copies, while one further FlatKV
checkpoint costs about 286 MiB, since Pebble checkpoints hardlink their
SSTs and so only pin the bytes compaction has since obsoleted. The depth
FlatKV wants would ask memIAVL for more than the volume holds.

Mirror only the interval, which must match because a rollback rewinds
both backends and a cross-backend digest opens both at one height, and
give FlatKV its own retention default of 72. That is a guaranteed reach
of 720,000 blocks, about 89 hours, against an 85-hour drain at
K=1024, for roughly 20 GiB.

Backport of #4145.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 11, 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-09-11T14:41:30.274479Z 989e1b0 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 Sep 11, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes default FlatKV snapshot retention and composite store alignment at state-commit construction, increasing disk use but fixing too-shallow checkpoint history during the EVM migration window.

Overview
FlatKV no longer inherits memIAVL’s sc-keep-recent (default 1) for Pebble checkpoint retention. alignFlatKVSnapshotIntervalWithMemIAVL replaces the old align helper and only copies snapshot interval from memIAVL; DefaultSnapshotKeepRecent rises from 1 → 72, so guaranteed historical reach grows from ~10k blocks (~74 min) to 720k blocks (~89 h) for tools like migrate-evm-status, dump-flatkv, cross-backend digest, and FlatKV rollback—without an app.toml change on existing nodes.

Tests and golden configs are updated to lock in interval-only mirroring and the new default depth (~20 GiB extra disk at mainnet-sized state, from hardlinked checkpoints). CHANGELOG documents the behavior for v6.7.

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

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown

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

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedSep 11, 2026, 5:38 PM

@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: 989e1b0c3b

ℹ️ 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".

Comment on lines +14 to +18
// DefaultSnapshotKeepRecent keeps 72 old checkpoints besides the latest one,
// which at DefaultSnapshotInterval is a guaranteed reach of 720,000 blocks —
// about 89 hours at mainnet's block rate, so it spans the EVM migration
// window at the rate that window is planned for.
//

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Shorten the constant godoc to its contract

Rewrite this exported godoc to state only that the constant is the default number of retained checkpoints. The migration timing, disk-cost estimates, hardlink mechanism, and operational rationale turn it into a three-paragraph design note that will become stale with deployment conditions and violates the repository requirement that godocs describe what a subject is in one or two sentences rather than why or how it works.

AGENTS.md reference: AGENTS.md:L59-L72

Useful? React with 👍 / 👎.

@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.36%. Comparing base (e59189e) to head (989e1b0).

Additional details and impacted files

Impacted file tree graph

@@               Coverage Diff                @@
##           release/v6.7    #4146      +/-   ##
================================================
- Coverage         61.34%   60.36%   -0.99%     
================================================
  Files              2163     2070      -93     
  Lines            188785   177915   -10870     
================================================
- Hits             115813   107396    -8417     
+ Misses            62254    60665    -1589     
+ Partials          10718     9854     -864     
Flag Coverage Δ
sei-chain-pr 94.47% <ø> (?)
sei-db 69.80% <ø> (ø)
sei-db-state-db ?
sei-db-state-db-pr 71.95% <100.00%> (?)

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

Files with missing lines Coverage Δ
sei-cosmos/server/config/config.go 97.80% <ø> (ø)
sei-db/state_db/sc/composite/store.go 71.38% <100.00%> (-0.05%) ⬇️
sei-db/state_db/sc/flatkv/config/config.go 76.27% <ø> (ø)

... and 93 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.

@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.

Backport is mechanically correct: the retention mirror is dropped, the interval mirror kept, and goldens, tests, and comments are updated consistently. Two non-blocking points on the documented disk cost (the WAL term is measured post-migration but retention is sized for the migration window) and on retention now having no operator-visible knob.

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

Blockers

  • None at the file/PR level.

Non-blocking

  • None at the file/PR level.
  • 2 suggestion(s)/nit(s) flagged inline on specific lines.

// Reach matters because it bounds what can be answered about a past height at
// all. Below it, migrate-evm-status, dump-flatkv and a cross-backend digest
// cannot open a version, and a rollback has no base snapshot to rewind to.
DefaultSnapshotKeepRecent uint32 = 72

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[suggestion] The ~20 GiB cost model is extrapolated from a steady-state measurement ("post-H_done, at the tip"), but the depth is sized for the migration window, where both components are larger.

The WAL term is the bigger gap. tryTruncateWAL (sei-db/state_db/sc/flatkv/snapshot.go:682) prunes the state WAL below the earliest retained snapshot, so 1 -> 72 widens retained WAL from ~20,000 blocks to ~730,000. During the drain every block's FlatKV changeset also carries the NumKeysToMigratePerBlock batch (migration_manager.go:301-315 routes migrated pairs into the new DB's changeset, which Commit writes to the WAL), so the WAL retained across that window approaches the whole migrated EVM state — on the order of the 24,241 MiB of live SSTs this PR cites — rather than the ~1.8 GiB implied by 72 x 25 MiB. K is governance-tunable up to MaxNumKeysToMigratePerBlock = 1_000_000, so the rate this is measured against is not a floor. The pinned-SST term is also likely above 261 MiB/interval while the whole state is being bulk-inserted and compaction churn is at its peak.

Probably still affordable on the 2 TiB volume, but the number operators will plan against is off by roughly an order of magnitude in exactly the window the default exists for. Worth re-measuring mid-drain and correcting this comment and the CHANGELOG, or bounding WAL retention independently of snapshot count.

// Options.FillDefaults would apply at OpenDB. Mirroring a raw 0 is never correct:
// 0 disables auto-snapshots for FlatKV, which lets the WAL grow without bound and
// makes every restart replay from snapshot-0.
func alignFlatKVSnapshotIntervalWithMemIAVL(cfg *config.StateCommitConfig) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[suggestion] After this change FlatKV's retention has no operator-visible lever at all: parseSCConfigs (app/seidb.go:101) reads no FlatKV snapshot key, the template hides flatkv.*, and the sc-keep-recent mirror is gone. A disk-constrained node cannot reduce the 73 retained checkpoints without a new binary. The doc's own argument for keeping the interval mirror unconditional — "otherwise FlatKVOnly would have no template-visible way to tune it" — now applies to the count with no answer.

Relatedly, because retention is a fixed count while the interval is mirrored from sc-snapshot-interval, both the reach and the disk cost scale with whatever the operator sets there. sc-snapshot-interval = 1000 collapses the "guaranteed" 720,000-block reach to 72,000 blocks (~9 h, well under the 85-h drain) with no warning, and a larger interval multiplies the bytes each checkpoint pins. TestFlatKVDefaultRetentionSpansTheMigrationWindow only pins this at the default interval, so that case is not covered.

Expressing the target in blocks (or clamping the count against the effective interval so the reach invariant holds for any interval) would make the guarantee this default is named for actually hold, and give the count a defined behavior when the interval is tuned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant