Skip to content

Commit 389021a

Browse files
holimanfjl
andauthored
core: place a cap on reorglogs (ethereum#25711)
This PR makes the event-sending for deleted and new logs happen in batches, to prevent OOM situation due to large reorgs. Co-authored-by: Felix Lange <fjl@twurst.com>
1 parent 610cf02 commit 389021a

File tree

2 files changed

+98
-74
lines changed

2 files changed

+98
-74
lines changed

core/blockchain.go

+31-47
Original file line numberDiff line numberDiff line change
@@ -2000,21 +2000,6 @@ func (bc *BlockChain) collectLogs(hash common.Hash, removed bool) []*types.Log {
20002000
return logs
20012001
}
20022002

2003-
// mergeLogs returns a merged log slice with specified sort order.
2004-
func mergeLogs(logs [][]*types.Log, reverse bool) []*types.Log {
2005-
var ret []*types.Log
2006-
if reverse {
2007-
for i := len(logs) - 1; i >= 0; i-- {
2008-
ret = append(ret, logs[i]...)
2009-
}
2010-
} else {
2011-
for i := 0; i < len(logs); i++ {
2012-
ret = append(ret, logs[i]...)
2013-
}
2014-
}
2015-
return ret
2016-
}
2017-
20182003
// reorg takes two blocks, an old chain and a new chain and will reconstruct the
20192004
// blocks and inserts them to be part of the new canonical chain and accumulates
20202005
// potential missing transactions and post an event about them.
@@ -2028,9 +2013,6 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
20282013

20292014
deletedTxs []common.Hash
20302015
addedTxs []common.Hash
2031-
2032-
deletedLogs [][]*types.Log
2033-
rebirthLogs [][]*types.Log
20342016
)
20352017
// Reduce the longer chain to the same number as the shorter one
20362018
if oldBlock.NumberU64() > newBlock.NumberU64() {
@@ -2040,12 +2022,6 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
20402022
for _, tx := range oldBlock.Transactions() {
20412023
deletedTxs = append(deletedTxs, tx.Hash())
20422024
}
2043-
2044-
// Collect deleted logs for notification
2045-
logs := bc.collectLogs(oldBlock.Hash(), true)
2046-
if len(logs) > 0 {
2047-
deletedLogs = append(deletedLogs, logs)
2048-
}
20492025
}
20502026
} else {
20512027
// New chain is longer, stash all blocks away for subsequent insertion
@@ -2072,12 +2048,6 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
20722048
for _, tx := range oldBlock.Transactions() {
20732049
deletedTxs = append(deletedTxs, tx.Hash())
20742050
}
2075-
2076-
// Collect deleted logs for notification
2077-
logs := bc.collectLogs(oldBlock.Hash(), true)
2078-
if len(logs) > 0 {
2079-
deletedLogs = append(deletedLogs, logs)
2080-
}
20812051
newChain = append(newChain, newBlock)
20822052

20832053
// Step back with both chains
@@ -2151,28 +2121,42 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
21512121
log.Crit("Failed to delete useless indexes", "err", err)
21522122
}
21532123

2154-
// Collect the logs
2155-
for i := len(newChain) - 1; i >= 1; i-- {
2156-
// Collect reborn logs due to chain reorg
2157-
logs := bc.collectLogs(newChain[i].Hash(), false)
2158-
if len(logs) > 0 {
2159-
rebirthLogs = append(rebirthLogs, logs)
2124+
// Send out events for logs from the old canon chain, and 'reborn'
2125+
// logs from the new canon chain. The number of logs can be very
2126+
// high, so the events are sent in batches of size around 512.
2127+
2128+
// Deleted logs + blocks:
2129+
var deletedLogs []*types.Log
2130+
for i := len(oldChain) - 1; i >= 0; i-- {
2131+
// Also send event for blocks removed from the canon chain.
2132+
bc.chainSideFeed.Send(ChainSideEvent{Block: oldChain[i]})
2133+
2134+
// Collect deleted logs for notification
2135+
if logs := bc.collectLogs(oldChain[i].Hash(), true); len(logs) > 0 {
2136+
deletedLogs = append(deletedLogs, logs...)
2137+
}
2138+
if len(deletedLogs) > 512 {
2139+
bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
2140+
deletedLogs = nil
21602141
}
21612142
}
2162-
// If any logs need to be fired, do it now. In theory we could avoid creating
2163-
// this goroutine if there are no events to fire, but realistcally that only
2164-
// ever happens if we're reorging empty blocks, which will only happen on idle
2165-
// networks where performance is not an issue either way.
21662143
if len(deletedLogs) > 0 {
2167-
bc.rmLogsFeed.Send(RemovedLogsEvent{mergeLogs(deletedLogs, true)})
2144+
bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
21682145
}
2169-
if len(rebirthLogs) > 0 {
2170-
bc.logsFeed.Send(mergeLogs(rebirthLogs, false))
2171-
}
2172-
if len(oldChain) > 0 {
2173-
for i := len(oldChain) - 1; i >= 0; i-- {
2174-
bc.chainSideFeed.Send(ChainSideEvent{Block: oldChain[i]})
2146+
2147+
// New logs:
2148+
var rebirthLogs []*types.Log
2149+
for i := len(newChain) - 1; i >= 1; i-- {
2150+
if logs := bc.collectLogs(newChain[i].Hash(), false); len(logs) > 0 {
2151+
rebirthLogs = append(rebirthLogs, logs...)
21752152
}
2153+
if len(rebirthLogs) > 512 {
2154+
bc.logsFeed.Send(rebirthLogs)
2155+
rebirthLogs = nil
2156+
}
2157+
}
2158+
if len(rebirthLogs) > 0 {
2159+
bc.logsFeed.Send(rebirthLogs)
21762160
}
21772161
return nil
21782162
}

core/blockchain_test.go

+67-27
Original file line numberDiff line numberDiff line change
@@ -1158,37 +1158,53 @@ func TestLogRebirth(t *testing.T) {
11581158
blockchain.SubscribeLogsEvent(newLogCh)
11591159
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
11601160

1161-
// This chain contains a single log.
1162-
genDb, chain, _ := GenerateChainWithGenesis(gspec, engine, 2, func(i int, gen *BlockGen) {
1163-
if i == 1 {
1164-
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, logCode), signer, key1)
1165-
if err != nil {
1166-
t.Fatalf("failed to create tx: %v", err)
1161+
// This chain contains 10 logs.
1162+
genDb, chain, _ := GenerateChainWithGenesis(gspec, engine, 3, func(i int, gen *BlockGen) {
1163+
if i < 2 {
1164+
for ii := 0; ii < 5; ii++ {
1165+
tx, err := types.SignNewTx(key1, signer, &types.LegacyTx{
1166+
Nonce: gen.TxNonce(addr1),
1167+
GasPrice: gen.header.BaseFee,
1168+
Gas: uint64(1000001),
1169+
Data: logCode,
1170+
})
1171+
if err != nil {
1172+
t.Fatalf("failed to create tx: %v", err)
1173+
}
1174+
gen.AddTx(tx)
11671175
}
1168-
gen.AddTx(tx)
11691176
}
11701177
})
11711178
if _, err := blockchain.InsertChain(chain); err != nil {
11721179
t.Fatalf("failed to insert chain: %v", err)
11731180
}
1174-
checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
1181+
checkLogEvents(t, newLogCh, rmLogsCh, 10, 0)
11751182

1176-
// Generate long reorg chain containing another log. Inserting the
1177-
// chain removes one log and adds one.
1178-
_, forkChain, _ := GenerateChainWithGenesis(gspec, engine, 2, func(i int, gen *BlockGen) {
1179-
if i == 1 {
1180-
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, logCode), signer, key1)
1183+
// Generate long reorg chain containing more logs. Inserting the
1184+
// chain removes one log and adds four.
1185+
_, forkChain, _ := GenerateChainWithGenesis(gspec, engine, 3, func(i int, gen *BlockGen) {
1186+
if i == 2 {
1187+
// The last (head) block is not part of the reorg-chain, we can ignore it
1188+
return
1189+
}
1190+
for ii := 0; ii < 5; ii++ {
1191+
tx, err := types.SignNewTx(key1, signer, &types.LegacyTx{
1192+
Nonce: gen.TxNonce(addr1),
1193+
GasPrice: gen.header.BaseFee,
1194+
Gas: uint64(1000000),
1195+
Data: logCode,
1196+
})
11811197
if err != nil {
11821198
t.Fatalf("failed to create tx: %v", err)
11831199
}
11841200
gen.AddTx(tx)
1185-
gen.OffsetTime(-9) // higher block difficulty
11861201
}
1202+
gen.OffsetTime(-9) // higher block difficulty
11871203
})
11881204
if _, err := blockchain.InsertChain(forkChain); err != nil {
11891205
t.Fatalf("failed to insert forked chain: %v", err)
11901206
}
1191-
checkLogEvents(t, newLogCh, rmLogsCh, 1, 1)
1207+
checkLogEvents(t, newLogCh, rmLogsCh, 10, 10)
11921208

11931209
// This chain segment is rooted in the original chain, but doesn't contain any logs.
11941210
// When inserting it, the canonical chain switches away from forkChain and re-emits
@@ -1197,7 +1213,7 @@ func TestLogRebirth(t *testing.T) {
11971213
if _, err := blockchain.InsertChain(newBlocks); err != nil {
11981214
t.Fatalf("failed to insert forked chain: %v", err)
11991215
}
1200-
checkLogEvents(t, newLogCh, rmLogsCh, 1, 1)
1216+
checkLogEvents(t, newLogCh, rmLogsCh, 10, 10)
12011217
}
12021218

12031219
// This test is a variation of TestLogRebirth. It verifies that log events are emitted
@@ -1252,19 +1268,43 @@ func TestSideLogRebirth(t *testing.T) {
12521268

12531269
func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan RemovedLogsEvent, wantNew, wantRemoved int) {
12541270
t.Helper()
1255-
1256-
if len(logsCh) != wantNew {
1257-
t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew)
1258-
}
1259-
if len(rmLogsCh) != wantRemoved {
1260-
t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved)
1261-
}
1271+
var (
1272+
countNew int
1273+
countRm int
1274+
prev int
1275+
)
12621276
// Drain events.
1263-
for i := 0; i < len(logsCh); i++ {
1264-
<-logsCh
1277+
for len(logsCh) > 0 {
1278+
x := <-logsCh
1279+
countNew += len(x)
1280+
for _, log := range x {
1281+
// We expect added logs to be in ascending order: 0:0, 0:1, 1:0 ...
1282+
have := 100*int(log.BlockNumber) + int(log.TxIndex)
1283+
if have < prev {
1284+
t.Fatalf("Expected new logs to arrive in ascending order (%d < %d)", have, prev)
1285+
}
1286+
prev = have
1287+
}
1288+
}
1289+
prev = 0
1290+
for len(rmLogsCh) > 0 {
1291+
x := <-rmLogsCh
1292+
countRm += len(x.Logs)
1293+
for _, log := range x.Logs {
1294+
// We expect removed logs to be in ascending order: 0:0, 0:1, 1:0 ...
1295+
have := 100*int(log.BlockNumber) + int(log.TxIndex)
1296+
if have < prev {
1297+
t.Fatalf("Expected removed logs to arrive in ascending order (%d < %d)", have, prev)
1298+
}
1299+
prev = have
1300+
}
1301+
}
1302+
1303+
if countNew != wantNew {
1304+
t.Fatalf("wrong number of log events: got %d, want %d", countNew, wantNew)
12651305
}
1266-
for i := 0; i < len(rmLogsCh); i++ {
1267-
<-rmLogsCh
1306+
if countRm != wantRemoved {
1307+
t.Fatalf("wrong number of removed log events: got %d, want %d", countRm, wantRemoved)
12681308
}
12691309
}
12701310

0 commit comments

Comments
 (0)