diff --git a/core/blockchain.go b/core/blockchain.go index 51fd8e026e..be8b1c866b 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1003,13 +1003,6 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha newHeadBlock, rootNumber = bc.rewindHead(header, root) rawdb.WriteHeadBlockHash(db, newHeadBlock.Hash()) - // Use soft truncate for fast rewind - actual truncation happens at the end - if bc.triedb.Scheme() == rawdb.PathScheme { - if err := bc.triedb.SoftTruncateHead(); err != nil { - log.Warn("Failed to soft truncate trie database", "err", err) - } - } - // Degrade the chain markers if they are explicitly reverted. // In theory we should update all in-memory markers in the // last step, however the direction of SetHead is from high @@ -1103,11 +1096,9 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha bc.blockCache.Purge() bc.txLookupCache.Purge() - // Commit all soft truncations to align the state histories with the current state. - if bc.triedb.Scheme() == rawdb.PathScheme { - if err := bc.triedb.CommitTruncation(); err != nil { - log.Crit("Failed to commit trie database truncation", "err", err) - } + // Commit all recovery operations to finalize state freezer truncation. + if err := bc.triedb.RecoverDone(); err != nil { + log.Crit("Failed to finalize trie database recovery", "err", err) } // Clear safe block, finalized block if needed diff --git a/triedb/database.go b/triedb/database.go index 36c3450f77..2ac5ccb11e 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -280,35 +280,15 @@ func (db *Database) Recover(target common.Hash) error { return pdb.Recover(target) } -// TruncateHead truncates all state histories in the freezer above the bottom state layer. -// This operation is only supported by path-based database. It's typically used during -// state recovery to align the state histories with the current state. -func (db *Database) TruncateHead() error { +// RecoverDone commits all pending soft truncations after a series of recovery operations. +// This should be called after all recover operations are complete to finalize the state +// freezer truncation in one efficient batch operation. +func (db *Database) RecoverDone() error { pdb, ok := db.backend.(*pathdb.Database) if !ok { return errors.New("not supported") } - return pdb.TruncateHead() -} - -// SoftTruncateHead marks state histories above the bottom state layer as logically deleted -// without physical truncation. This is much faster than TruncateHead. -func (db *Database) SoftTruncateHead() error { - pdb, ok := db.backend.(*pathdb.Database) - if !ok { - return errors.New("not supported") - } - return pdb.SoftTruncateHead() -} - -// CommitTruncation performs the actual file truncation to match any soft truncations. -// This should be called after all soft truncations are complete. -func (db *Database) CommitTruncation() error { - pdb, ok := db.backend.(*pathdb.Database) - if !ok { - return errors.New("not supported") - } - return pdb.CommitTruncation() + return pdb.RecoverDone() } // Recoverable returns the indicator if the specified state is enabled to be diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 9582843deb..b5c74878fe 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -502,26 +502,20 @@ func (db *Database) Recover(root common.Hash) error { if err := db.diskdb.SyncKeyValue(); err != nil { return err } + + // Soft truncate the state freezer to the recovered state + if _, err := db.stateFreezer.SoftTruncateHead(dl.stateID()); err != nil { + return err + } + log.Debug("Recovered state", "root", root, "elapsed", common.PrettyDuration(time.Since(start))) return nil } -// TruncateHead truncates all state histories in the freezer above the bottom state layer. -func (db *Database) TruncateHead() error { - _, err := truncateFromHead(db.stateFreezer, db.tree.bottom().stateID()) - return err -} - -// SoftTruncateHead marks state histories above the bottom state layer as logically deleted -// without physical truncation. This is much faster than TruncateHead. -func (db *Database) SoftTruncateHead() error { - _, err := db.stateFreezer.SoftTruncateHead(db.tree.bottom().stateID()) - return err -} - -// CommitTruncation performs the actual file truncation to match any soft truncations. -// This should be called after all soft truncations are complete. -func (db *Database) CommitTruncation() error { +// RecoverDone commits all pending soft truncations after a series of recovery operations. +// This should be called after all recover operations are complete to finalize the state +// freezer truncation in one efficient batch operation. +func (db *Database) RecoverDone() error { return db.stateFreezer.CommitTruncation() } diff --git a/triedb/pathdb/database_test.go b/triedb/pathdb/database_test.go index d44ad2c2a9..3731e337d6 100644 --- a/triedb/pathdb/database_test.go +++ b/triedb/pathdb/database_test.go @@ -600,18 +600,16 @@ func TestDatabaseRollback(t *testing.T) { if err := tester.db.Recover(parent); err != nil { t.Fatalf("Failed to revert db, err: %v", err) } - if err := tester.db.SoftTruncateHead(); err != nil { - t.Fatalf("Failed to soft truncate head, err: %v", err) - } - if err := tester.db.CommitTruncation(); err != nil { - t.Fatalf("Failed to commit truncation, err: %v", err) - } if i > 0 { if err := tester.verifyState(parent); err != nil { t.Fatalf("Failed to verify state, err: %v", err) } } } + // Finalize all recovery operations + if err := tester.db.RecoverDone(); err != nil { + t.Fatalf("Failed to finalize recovery, err: %v", err) + } if tester.db.tree.len() != 1 { t.Fatal("Only disk layer is expected") }