Skip to content

Commit 86af788

Browse files
authoredMay 30, 2022
core: use less memory during reorgs (ethereum#24616)
This PR significantly reduces the memory consumption of a long reorg

File tree

2 files changed

+43
-13
lines changed

2 files changed

+43
-13
lines changed
 

‎core/blockchain.go

+25-13
Original file line numberDiff line numberDiff line change
@@ -1965,8 +1965,8 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
19651965
oldChain types.Blocks
19661966
commonBlock *types.Block
19671967

1968-
deletedTxs types.Transactions
1969-
addedTxs types.Transactions
1968+
deletedTxs []common.Hash
1969+
addedTxs []common.Hash
19701970

19711971
deletedLogs [][]*types.Log
19721972
rebirthLogs [][]*types.Log
@@ -1976,7 +1976,9 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
19761976
// Old chain is longer, gather all transactions and logs as deleted ones
19771977
for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) {
19781978
oldChain = append(oldChain, oldBlock)
1979-
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
1979+
for _, tx := range oldBlock.Transactions() {
1980+
deletedTxs = append(deletedTxs, tx.Hash())
1981+
}
19801982

19811983
// Collect deleted logs for notification
19821984
logs := bc.collectLogs(oldBlock.Hash(), true)
@@ -2006,7 +2008,9 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
20062008
}
20072009
// Remove an old block as well as stash away a new block
20082010
oldChain = append(oldChain, oldBlock)
2009-
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
2011+
for _, tx := range oldBlock.Transactions() {
2012+
deletedTxs = append(deletedTxs, tx.Hash())
2013+
}
20102014

20112015
// Collect deleted logs for notification
20122016
logs := bc.collectLogs(oldBlock.Hash(), true)
@@ -2025,6 +2029,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
20252029
return fmt.Errorf("invalid new chain")
20262030
}
20272031
}
2032+
20282033
// Ensure the user sees large reorgs
20292034
if len(oldChain) > 0 && len(newChain) > 0 {
20302035
logFn := log.Info
@@ -2041,7 +2046,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
20412046
} else if len(newChain) > 0 {
20422047
// Special case happens in the post merge stage that current head is
20432048
// the ancestor of new head while these two blocks are not consecutive
2044-
log.Info("Extend chain", "add", len(newChain), "number", newChain[0].NumberU64(), "hash", newChain[0].Hash())
2049+
log.Info("Extend chain", "add", len(newChain), "number", newChain[0].Number(), "hash", newChain[0].Hash())
20452050
blockReorgAddMeter.Mark(int64(len(newChain)))
20462051
} else {
20472052
// len(newChain) == 0 && len(oldChain) > 0
@@ -2054,19 +2059,17 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
20542059
// Insert the block in the canonical way, re-writing history
20552060
bc.writeHeadBlock(newChain[i])
20562061

2057-
// Collect reborn logs due to chain reorg
2058-
logs := bc.collectLogs(newChain[i].Hash(), false)
2059-
if len(logs) > 0 {
2060-
rebirthLogs = append(rebirthLogs, logs)
2061-
}
20622062
// Collect the new added transactions.
2063-
addedTxs = append(addedTxs, newChain[i].Transactions()...)
2063+
for _, tx := range newChain[i].Transactions() {
2064+
addedTxs = append(addedTxs, tx.Hash())
2065+
}
20642066
}
2067+
20652068
// Delete useless indexes right now which includes the non-canonical
20662069
// transaction indexes, canonical chain indexes which above the head.
20672070
indexesBatch := bc.db.NewBatch()
2068-
for _, tx := range types.TxDifference(deletedTxs, addedTxs) {
2069-
rawdb.DeleteTxLookupEntry(indexesBatch, tx.Hash())
2071+
for _, tx := range types.HashDifference(deletedTxs, addedTxs) {
2072+
rawdb.DeleteTxLookupEntry(indexesBatch, tx)
20702073
}
20712074
// Delete any canonical number assignments above the new head
20722075
number := bc.CurrentBlock().NumberU64()
@@ -2080,6 +2083,15 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
20802083
if err := indexesBatch.Write(); err != nil {
20812084
log.Crit("Failed to delete useless indexes", "err", err)
20822085
}
2086+
2087+
// Collect the logs
2088+
for i := len(newChain) - 1; i >= 1; i-- {
2089+
// Collect reborn logs due to chain reorg
2090+
logs := bc.collectLogs(newChain[i].Hash(), false)
2091+
if len(logs) > 0 {
2092+
rebirthLogs = append(rebirthLogs, logs)
2093+
}
2094+
}
20832095
// If any logs need to be fired, do it now. In theory we could avoid creating
20842096
// this goroutine if there are no events to fire, but realistcally that only
20852097
// ever happens if we're reorging empty blocks, which will only happen on idle

‎core/types/transaction.go

+18
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,24 @@ func TxDifference(a, b Transactions) Transactions {
432432
return keep
433433
}
434434

435+
// HashDifference returns a new set which is the difference between a and b.
436+
func HashDifference(a, b []common.Hash) []common.Hash {
437+
keep := make([]common.Hash, 0, len(a))
438+
439+
remove := make(map[common.Hash]struct{})
440+
for _, hash := range b {
441+
remove[hash] = struct{}{}
442+
}
443+
444+
for _, hash := range a {
445+
if _, ok := remove[hash]; !ok {
446+
keep = append(keep, hash)
447+
}
448+
}
449+
450+
return keep
451+
}
452+
435453
// TxByNonce implements the sort interface to allow sorting a list of transactions
436454
// by their nonces. This is usually only useful for sorting transactions from a
437455
// single account, otherwise a nonce comparison doesn't make much sense.

0 commit comments

Comments
 (0)
Please sign in to comment.