Skip to content

Fix pruning issue in SS causing huge disk spike - #4321

Merged
alrevuelta merged 2 commits into
mainfrom
prunning-issue
Sep 24, 2026
Merged

alrevuelta merged 2 commits into
mainfrom
prunning-issue

Conversation

@alrevuelta

Copy link
Copy Markdown
Contributor
  • Pruning in SS removes old state to keep a recent window of x blocks.
  • From Pebble perspective, pruning means iterating all keys and removing the ones older than the retention window (+ keeping some past ones if there were no changes). But deleting does delete right a way. For pebble a deletion is inserting a tombstone value that means that a given key no longer exists. Then Pebble has to "clean that up" removing the old value + the tombstone during compaction and other background tasks.
  • The bug: When iterating the keys to prune, the iterator was closed after (via defer) after completing the compaction. But Pebble could not compact because the iterator was active, hence "locking" the data that was supposed to be removed.

left: fix
right: the bug
image

@cursor

cursor Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches the descending MVCC prune path and compaction ordering; behavior change is intentional and localized, but incorrect iterator lifecycle directly affected disk growth and compaction progress.

Overview
Fixes a disk spike during state-store pruning by releasing the Pebble scan iterator before post-prune compaction runs.

In pruneDescending, the full-DB iterator used to stay open until function return (deferred Close), so compactPrunedRange could run for a long time while that iterator still pinned Pebble readState and prevented deletion of obsolete SSTables. The change adds an idempotent closeItr helper, closes the iterator immediately after the scan (before batch commit and compaction), and keeps the defer only for error paths mid-loop.

Also wires seilog for this package and logs an info line after compaction with version, elapsed time, and error.

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

@github-actions

github-actions Bot commented Sep 24, 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 8, 2026, 9:10 AM

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

The fix is correct: deleted keys are cloned via bytes.Clone, so closing the scan iterator before commit and compaction is safe, and it releases the pinned readState before compactPrunedRange runs. The notes below cover structure, log noise, and the same bug still sitting in the ascending path.

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

Blockers

  • None at the file/PR level.

Non-blocking

  • [suggestion] No regression test comes with the change. A test could check that the deferred-close path still works when the loop returns an error. Better, it could assert that obsolete sstables are freed after pruneDescending, for example through db.storage.Metrics() or the table count / disk usage after a prune plus compaction. Either would stop the iterator lifetime from quietly regressing.
  • 2 suggestion(s)/nit(s) flagged inline on specific lines.
  • 1 non-blocking pre-existing issue(s) listed below under pre-existing issues.

Pre-existing issues

  • [suggestion] sei-db/db_engine/pebbledb/mvcc/db_ascending.go (pruneAscending, around line 122) still has the bug this PR fixes. It closes the scan iterator with defer only after compactPrunedRange returns, so nodes on the legacy ascending encoding keep the same sstable pinning and disk spike. Apply the same early close there, ideally through a shared helper. (Also raised by Codex.)

// closing it twice: the scan closes it explicitly as soon as it's done
// reading, right before compaction, and this defer only still applies if an
// error returned from inside the loop below.
itrOpen := true

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] AGENTS.md ('Structural corrections') asks that this kind of fix be made by naming a step, not by adding an itrOpen flag, a closeItr closure, and two long inline comments. Consider moving the scan into its own function, e.g. scanAndDeletePruned(...) (first, last []byte, err error), that opens the iterator and closes it with a plain defer. pruneDescending then reads as scan → commit → compact, and the iterator is closed before compaction by construction. The note about readState pinning belongs in that function's doc comment. pruneAscending has the same bug and could use the same shape.

return db.compactPrunedRange(firstDeletedKey, lastDeletedKey)
compactStart := time.Now()
err = db.compactPrunedRange(firstDeletedKey, lastDeletedKey)
logger.Info("pruneDescending: compacted pruned range", "version", version, "elapsed", time.Since(compactStart), "err", err)

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] This Info line runs on every prune pass, even when nothing was deleted and compactPrunedRange returned immediately (first == nil). It also logs failures at Info as err=<error>. Consider logging only when a compaction actually ran (firstDeletedKey != nil), and moving failures to Error/Warn or leaving them to the caller, which already receives the returned error.

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 184297f. Configure here.

compactStart := time.Now()
err = db.compactPrunedRange(firstDeletedKey, lastDeletedKey)
logger.Info("pruneDescending: compacted pruned range", "version", version, "elapsed", time.Since(compactStart), "err", err)
return err

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ascending prune still pins iterator

Medium Severity

The early iterator close before compactPrunedRange is only applied in pruneDescending. pruneAscending still defers itr.Close() until after compaction, so the scan iterator keeps Pebble's readState pinned and obsolete sstables stay undeletable. Legacy and archive nodes on the ascending path still hit the same disk spike this change targets.

Fix in Cursor Fix in Web

Triggered by learned rule: sei-db pebbledb: ascending and descending MVCC iterators must stay aligned

Reviewed by Cursor Bugbot for commit 184297f. Configure here.

@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.30769% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 66.44%. Comparing base (f978be0) to head (e615a10).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
sei-db/db_engine/pebbledb/mvcc/db.go 92.30% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4321      +/-   ##
==========================================
- Coverage   67.50%   66.44%   -1.07%     
==========================================
  Files        2181     2060     -121     
  Lines      167848   155789   -12059     
==========================================
- Hits       113306   103511    -9795     
+ Misses      54532    52268    -2264     
  Partials       10       10              
Flag Coverage Δ
sei-chain-pr 76.01% <92.30%> (?)
sei-db 74.50% <ø> (ø)
sei-db-state-db ?

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

Files with missing lines Coverage Δ
sei-db/db_engine/pebbledb/mvcc/db.go 78.29% <92.30%> (+0.20%) ⬆️

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

@yzang2019

Copy link
Copy Markdown
Contributor

Nice Finding!

@alrevuelta
alrevuelta added this pull request to the merge queue Sep 24, 2026
Merged via the queue into main with commit 58b7631 Sep 24, 2026
68 checks passed
@alrevuelta
alrevuelta deleted the prunning-issue branch September 24, 2026 15:49
yzang2019 added a commit that referenced this pull request Sep 25, 2026
* main: (21 commits)
  Backport evmonly parse, app-hash and changeset perf fixes from giga-1 (#4345)
  Remove the oracle module behind a v6.8 upgrade (#4319)
  fix(seidb): report only the current migration boundary on the snapshot gauge (#4327)
  fix(flatkv): keep 10 old checkpoints instead of mirroring memIAVL's count (#4322)
  Backport Autobahn execute-loop and produced-tx metrics from giga-1 (#4330)
  Add giga.storage.receipts to toggle the Autobahn receipt store (#4333)
  optimize gather phase (#4326)
  Regenerate the Unreleased changelog as a plain PR list (#4336)
  Bump sei-protocol/go-ethereum to v1.15.7-sei-21 (#4332)
  Fail dynamic-gas precompile out-of-gas as an EVM out-of-gas call (#4318)
  Add dashboard and topology option for Autobahn e2e (#4167)
  Add Giga fetch/serve and BlockDB prune metrics (#4329)
  Add eth_getLogs to the EVM-only Giga RPC (#4308)
  Generate v6.8 precompiles (#4320)
  Add [giga] app.toml section and honor it on the Autobahn node (#4323)
  feat(evmonly): add eth_estimateGas via existing libraries (#4325)
  Use Pebble batch directly in SS (#4300)
  Fix pruning issue in SS causing huge disk spike (#4321)
  Make Autobahn always run the EVM-only executor, disable/remove some integration tests (#4316)
  reduce seal lock contention (#4314)
  ...
@masih masih added the backport release/v6.7 Backport to release v6.7 label Oct 8, 2026
@masih

masih commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

/backport

@seidroid

seidroid Bot commented Oct 8, 2026

Copy link
Copy Markdown

Successfully created backport PR for release/v6.7:

alexander-sei added a commit that referenced this pull request Oct 12, 2026
…ike (#4508)

Backport of #4321 to `release/v6.7`.

Co-authored-by: Alvaro Revuelta <alvrevuelta@gmail.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.

4 participants