soft truncate

This commit is contained in:
jsvisa 2025-09-12 01:57:53 +08:00
parent 1c431f6f95
commit 96963e75ad
4 changed files with 22 additions and 59 deletions

View file

@ -1003,13 +1003,6 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
newHeadBlock, rootNumber = bc.rewindHead(header, root) newHeadBlock, rootNumber = bc.rewindHead(header, root)
rawdb.WriteHeadBlockHash(db, newHeadBlock.Hash()) 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. // Degrade the chain markers if they are explicitly reverted.
// In theory we should update all in-memory markers in the // In theory we should update all in-memory markers in the
// last step, however the direction of SetHead is from high // 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.blockCache.Purge()
bc.txLookupCache.Purge() bc.txLookupCache.Purge()
// Commit all soft truncations to align the state histories with the current state. // Commit all recovery operations to finalize state freezer truncation.
if bc.triedb.Scheme() == rawdb.PathScheme { if err := bc.triedb.RecoverDone(); err != nil {
if err := bc.triedb.CommitTruncation(); err != nil { log.Crit("Failed to finalize trie database recovery", "err", err)
log.Crit("Failed to commit trie database truncation", "err", err)
}
} }
// Clear safe block, finalized block if needed // Clear safe block, finalized block if needed

View file

@ -280,35 +280,15 @@ func (db *Database) Recover(target common.Hash) error {
return pdb.Recover(target) return pdb.Recover(target)
} }
// TruncateHead truncates all state histories in the freezer above the bottom state layer. // RecoverDone commits all pending soft truncations after a series of recovery operations.
// This operation is only supported by path-based database. It's typically used during // This should be called after all recover operations are complete to finalize the state
// state recovery to align the state histories with the current state. // freezer truncation in one efficient batch operation.
func (db *Database) TruncateHead() error { func (db *Database) RecoverDone() error {
pdb, ok := db.backend.(*pathdb.Database) pdb, ok := db.backend.(*pathdb.Database)
if !ok { if !ok {
return errors.New("not supported") return errors.New("not supported")
} }
return pdb.TruncateHead() return pdb.RecoverDone()
}
// 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()
} }
// Recoverable returns the indicator if the specified state is enabled to be // Recoverable returns the indicator if the specified state is enabled to be

View file

@ -502,26 +502,20 @@ func (db *Database) Recover(root common.Hash) error {
if err := db.diskdb.SyncKeyValue(); err != nil { if err := db.diskdb.SyncKeyValue(); err != nil {
return err 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))) log.Debug("Recovered state", "root", root, "elapsed", common.PrettyDuration(time.Since(start)))
return nil return nil
} }
// TruncateHead truncates all state histories in the freezer above the bottom state layer. // RecoverDone commits all pending soft truncations after a series of recovery operations.
func (db *Database) TruncateHead() error { // This should be called after all recover operations are complete to finalize the state
_, err := truncateFromHead(db.stateFreezer, db.tree.bottom().stateID()) // freezer truncation in one efficient batch operation.
return err func (db *Database) RecoverDone() error {
}
// 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 {
return db.stateFreezer.CommitTruncation() return db.stateFreezer.CommitTruncation()
} }

View file

@ -600,18 +600,16 @@ func TestDatabaseRollback(t *testing.T) {
if err := tester.db.Recover(parent); err != nil { if err := tester.db.Recover(parent); err != nil {
t.Fatalf("Failed to revert db, err: %v", err) 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 i > 0 {
if err := tester.verifyState(parent); err != nil { if err := tester.verifyState(parent); err != nil {
t.Fatalf("Failed to verify state, err: %v", err) 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 { if tester.db.tree.len() != 1 {
t.Fatal("Only disk layer is expected") t.Fatal("Only disk layer is expected")
} }