@@ -223,6 +223,11 @@ type BlockChain struct {
223
223
vmConfig vm.Config
224
224
}
225
225
226
+ type trieGcEntry struct {
227
+ Root common.Hash
228
+ Timestamp uint64
229
+ }
230
+
226
231
// NewBlockChain returns a fully initialised block chain using information
227
232
// available in the database. It initialises the default Ethereum Validator
228
233
// and Processor.
@@ -870,9 +875,6 @@ func (bc *BlockChain) Stop() {
870
875
}
871
876
}
872
877
873
- // Arbitrum: only discard tries sufficiently old in both time and height
874
- retain := bc .FindRetentionBound ()
875
-
876
878
// Ensure the state of a recent block is also stored to disk before exiting.
877
879
// We're writing three different states to catch different restart scenarios:
878
880
// - HEAD: So we don't need to reprocess any blocks in the general case
@@ -881,7 +883,7 @@ func (bc *BlockChain) Stop() {
881
883
if ! bc .cacheConfig .TrieDirtyDisabled {
882
884
triedb := bc .stateCache .TrieDB ()
883
885
884
- for _ , offset := range []uint64 {0 , 1 , retain - 1 } {
886
+ for _ , offset := range []uint64 {0 , 1 , bc . cacheConfig . TriesInMemory } {
885
887
if number := bc .CurrentBlock ().NumberU64 (); number > offset {
886
888
recent := bc .GetBlockByNumber (number - offset )
887
889
if recent .Root () == (common.Hash {}) {
@@ -901,7 +903,7 @@ func (bc *BlockChain) Stop() {
901
903
}
902
904
}
903
905
for ! bc .triegc .Empty () {
904
- triedb .Dereference (bc .triegc .PopItem ().(common. Hash ) )
906
+ triedb .Dereference (bc .triegc .PopItem ().(trieGcEntry ). Root )
905
907
}
906
908
if size , _ := triedb .Size (); size != 0 {
907
909
log .Error ("Dangling trie nodes after full cleanup" )
@@ -1224,8 +1226,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
1224
1226
return 0 , nil
1225
1227
}
1226
1228
1227
- var lastWrite uint64
1228
-
1229
1229
// writeBlockWithoutState writes only the block and its metadata to the database,
1230
1230
// but does not write any state. This is used to construct competing side forks
1231
1231
// up to the point where they exceed the canonical total difficulty.
@@ -1286,18 +1286,18 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
1286
1286
}
1287
1287
triedb := bc .stateCache .TrieDB ()
1288
1288
1289
- // Arbitrum: only discard tries sufficiently old in both time and height
1290
- retain := bc .FindRetentionBound ()
1291
-
1292
1289
// If we're running an archive node, always flush
1293
1290
if bc .cacheConfig .TrieDirtyDisabled {
1294
1291
return triedb .Commit (root , false , nil )
1295
1292
} else {
1296
1293
// Full but not archive node, do proper garbage collection
1297
1294
triedb .Reference (root , common.Hash {}) // metadata reference to keep trie alive
1298
- bc .triegc .Push (root , - int64 (block .NumberU64 ()))
1295
+ bc .triegc .Push (trieGcEntry { root , block . Header (). Time } , - int64 (block .NumberU64 ()))
1299
1296
1300
- if current := block .NumberU64 (); current > retain {
1297
+ blockLimit := int64 (block .NumberU64 ()) - int64 (bc .cacheConfig .TriesInMemory ) // only cleared if below that
1298
+ timeLimit := time .Now ().Unix () - int64 (bc .cacheConfig .TrieRetention .Seconds ()) // only cleared if less than that
1299
+
1300
+ if blockLimit > 0 && timeLimit > 0 {
1301
1301
// If we exceeded our memory allowance, flush matured singleton nodes to disk
1302
1302
var (
1303
1303
nodes , imgs = triedb .Size ()
@@ -1306,36 +1306,39 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
1306
1306
if nodes > limit || imgs > 4 * 1024 * 1024 {
1307
1307
triedb .Cap (limit - ethdb .IdealBatchSize )
1308
1308
}
1309
- // Find the next state trie we need to commit
1310
- chosen := current - retain
1311
-
1309
+ var prevEntry * trieGcEntry
1310
+ var prevNum uint64
1311
+ // Garbage collect anything below our required write retention
1312
+ for ! bc .triegc .Empty () {
1313
+ tmp , number := bc .triegc .Pop ()
1314
+ triegcEntry := tmp .(trieGcEntry )
1315
+ if uint64 (- number ) > uint64 (blockLimit ) || triegcEntry .Timestamp > uint64 (timeLimit ) {
1316
+ bc .triegc .Push (triegcEntry , number )
1317
+ break
1318
+ }
1319
+ if prevEntry != nil {
1320
+ triedb .Dereference (prevEntry .Root )
1321
+ }
1322
+ prevEntry = & triegcEntry
1323
+ prevNum = uint64 (- number )
1324
+ }
1312
1325
// If we exceeded out time allowance, flush an entire trie to disk
1313
- if bc .gcproc > bc .cacheConfig .TrieTimeLimit {
1326
+ if bc .gcproc > bc .cacheConfig .TrieTimeLimit && prevEntry != nil {
1314
1327
// If the header is missing (canonical chain behind), we're reorging a low
1315
1328
// diff sidechain. Suspend committing until this operation is completed.
1316
- header := bc .GetHeaderByNumber (chosen )
1329
+ header := bc .GetHeaderByNumber (prevNum )
1317
1330
if header == nil {
1318
- log .Warn ("Reorg in progress, trie commit postponed" , "number" , chosen )
1331
+ log .Warn ("Reorg in progress, trie commit postponed" )
1319
1332
} else {
1320
1333
// If we're exceeding limits but haven't reached a large enough memory gap,
1321
1334
// warn the user that the system is becoming unstable.
1322
- if chosen < lastWrite + retain && bc .gcproc >= 2 * bc .cacheConfig .TrieTimeLimit {
1323
- log .Info ("State in memory for too long, committing" , "time" , bc .gcproc , "allowance" , bc .cacheConfig .TrieTimeLimit , "optimum" , float64 (chosen - lastWrite )/ float64 (retain ))
1324
- }
1325
1335
// Flush an entire trie and restart the counters
1326
1336
triedb .Commit (header .Root , true , nil )
1327
- lastWrite = chosen
1328
1337
bc .gcproc = 0
1329
1338
}
1330
1339
}
1331
- // Garbage collect anything below our required write retention
1332
- for ! bc .triegc .Empty () {
1333
- root , number := bc .triegc .Pop ()
1334
- if uint64 (- number ) > chosen {
1335
- bc .triegc .Push (root , number )
1336
- break
1337
- }
1338
- triedb .Dereference (root .(common.Hash ))
1340
+ if prevEntry != nil {
1341
+ triedb .Dereference (prevEntry .Root )
1339
1342
}
1340
1343
}
1341
1344
}
0 commit comments