Skip to content

trie, cmd/geth: add archiver command#563

Draft
gballet wants to merge 7 commits intoarchival-state-expiryfrom
archival-command
Draft

trie, cmd/geth: add archiver command#563
gballet wants to merge 7 commits intoarchival-state-expiryfrom
archival-command

Conversation

@gballet
Copy link
Copy Markdown
Owner

@gballet gballet commented Jan 25, 2026

I'm adding this as an intermediate step before #560, because I'm thinking that this is a manual step that could be merged into mainline geth as an experimental mode. It won't touch the tree for most users, only for those who are willing to risk losing their data in the hope of getting more speed.

This adds a utility to move all of the leaves to the archive, and then slowly reconstruct the hot state by calling the resurrection code. This will buy us a bit of time when trying to assess the required changes for adding the period.

@gballet gballet requested a review from weiihann January 25, 2026 10:06
gballet and others added 6 commits February 1, 2026 22:30
…ovements

Add Trie.Walk() for exhaustive traversal that resolves expired nodes
with hash verification. Add `archive verify` subcommand that walks
the full state (account + storage tries) to validate all archived
data can be correctly resurrected.

Delete both the journal KV entry and file after archiving to force
geth to restart with a bare disk layer, rewinding the chain head to
the persistent disk state and re-executing blocks.

Also adds markSubtreeDirty() to resolveExpiredNodeData() so that all
nodes in a resolved expired subtree are captured in the NodeSet during
commit — preventing them from being lost between diff layers and the
raw DB.
* trie/archiver: streaming subtree archival to fix OOM

Replace the recursive approach that loaded the entire trie into memory
with a streaming NodeIterator-based approach:

- processTrie now uses NodeIterator to walk the trie node-by-node
- probeHeight reads nodes from raw DB, computes height bounded at 3,
  and discards decoded nodes immediately (no in-memory trie buildup)
- collectSubtree only materializes the bounded height-3 subtree being
  archived (at most ~4096 nodes)
- Memory usage: O(iterator_stack) + O(current_subtree) instead of
  O(entire_trie)

This fixes OOM kills on large storage tries (e.g. contracts with
millions of storage slots) where the previous approach would load
all nodes and subtreeInfo into memory before archiving any of them.

* cmd/geth: flush diff layers before archiving, re-journal after

Instead of deleting the pathdb journal after archive generation (which
breaks the chain head and prevents block imports), properly integrate
with pathdb:

1. Open triedb with pathdb support (not just raw KV)
2. Disable state history freezer (avoid append gaps)
3. Flush all diff layers to disk via Commit() before archiving
4. After archiving, re-journal the pathdb state (disk layer only)

This ensures geth can restart cleanly after archiving and continue
importing blocks without 'unknown ancestor' errors.

* trie: add resurrection timing and depth metrics to expired node resolution

* Update trie/archiver.go

---------

Co-authored-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com>
…ion change (#573)

The streaming archival PR changed truncateFromHead to return nil
(instead of an error) when nhead > ohead, gracefully handling unclean
shutdowns where state history was not fully written. Update the test
to expect nil for the head+1 case.

Co-authored-by: tellabg <249254436+tellabg@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants