Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/time-based-pruning' into shutdow…
Browse files Browse the repository at this point in the history
…n-markers
  • Loading branch information
tsahee committed Aug 30, 2022
2 parents ef4387e + 21d73e3 commit 9cf4cb7
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 9 deletions.
26 changes: 20 additions & 6 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ const (
txLookupCacheLimit = 1024
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
TriesInMemory = 128

// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
//
Expand Down Expand Up @@ -132,12 +131,21 @@ type CacheConfig struct {
SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory
Preimages bool // Whether to store preimage of trie key to the disk

// Arbitrum: configure GC window
TriesInMemory uint64 // Height difference before which a trie may not be garbage-collected
TrieRetention time.Duration // Time limit before which a trie may not be garbage-collected

SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
}

// defaultCacheConfig are the default caching values if none are specified by the
// user (also used during testing).
var defaultCacheConfig = &CacheConfig{

// Arbitrum Config Options
TriesInMemory: 128,
TrieRetention: 30 * time.Minute,

TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
Expand Down Expand Up @@ -862,6 +870,9 @@ func (bc *BlockChain) Stop() {
}
}

// Arbitrum: only discard tries sufficiently old in both time and height
retain := bc.FindRetentionBound()

// Ensure the state of a recent block is also stored to disk before exiting.
// We're writing three different states to catch different restart scenarios:
// - HEAD: So we don't need to reprocess any blocks in the general case
Expand All @@ -870,7 +881,7 @@ func (bc *BlockChain) Stop() {
if !bc.cacheConfig.TrieDirtyDisabled {
triedb := bc.stateCache.TrieDB()

for _, offset := range []uint64{0, 1, TriesInMemory - 1} {
for _, offset := range []uint64{0, 1, retain - 1} {
if number := bc.CurrentBlock().NumberU64(); number > offset {
recent := bc.GetBlockByNumber(number - offset)
if recent.Root() == (common.Hash{}) {
Expand Down Expand Up @@ -1275,6 +1286,9 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
}
triedb := bc.stateCache.TrieDB()

// Arbitrum: only discard tries sufficiently old in both time and height
retain := bc.FindRetentionBound()

// If we're running an archive node, always flush
if bc.cacheConfig.TrieDirtyDisabled {
return triedb.Commit(root, false, nil)
Expand All @@ -1283,7 +1297,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
bc.triegc.Push(root, -int64(block.NumberU64()))

if current := block.NumberU64(); current > TriesInMemory {
if current := block.NumberU64(); current > retain {
// If we exceeded our memory allowance, flush matured singleton nodes to disk
var (
nodes, imgs = triedb.Size()
Expand All @@ -1293,7 +1307,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
triedb.Cap(limit - ethdb.IdealBatchSize)
}
// Find the next state trie we need to commit
chosen := current - TriesInMemory
chosen := current - retain

// If we exceeded out time allowance, flush an entire trie to disk
if bc.gcproc > bc.cacheConfig.TrieTimeLimit {
Expand All @@ -1305,8 +1319,8 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
} else {
// If we're exceeding limits but haven't reached a large enough memory gap,
// warn the user that the system is becoming unstable.
if chosen < lastWrite+TriesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/TriesInMemory)
if chosen < lastWrite+retain && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/float64(retain))
}
// Flush an entire trie and restart the counters
triedb.Commit(header.Root, true, nil)
Expand Down
49 changes: 49 additions & 0 deletions core/blockchain_arbitrum.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,52 @@ func (bc *BlockChain) ClipToPostNitroGenesis(blockNum rpc.BlockNumber) (rpc.Bloc
}
return blockNum, currentBlock
}

// finds the number of blocks that aren't prunable
func (bc *BlockChain) FindRetentionBound() uint64 {
minimumSpan := bc.cacheConfig.TriesInMemory
minimumAge := uint64(bc.cacheConfig.TrieRetention.Seconds())

saturatingCast := func(value int64) uint64 {
if value < 0 {
return 0
}
return uint64(value)
}

// enforce that the block be sufficiently deep
current := bc.CurrentBlock()
heightBound := saturatingCast(int64(current.NumberU64()) - int64(minimumSpan) + 1)

// find the left bound to our subsequent binary search
timeBound := heightBound
leap := int64(1)
for timeBound > 0 {
age := current.Time() - bc.GetBlockByNumber(uint64(timeBound)).Time()
if age > minimumAge {
break
}
timeBound = saturatingCast(int64(timeBound) - leap)
leap *= 2
}
if timeBound == heightBound {
return current.NumberU64() - timeBound + 1
}

// Algo: binary search on the interval [a, b] for the first prunable block
// Timebound is a prunable block, if one exists.
// We want to find the first block that's not prunable.
//
a := timeBound // a prunable block, if possible
b := heightBound // not prunable
for a+1 < b {
mid := a/2 + b/2 // a < mid < b
age := current.Time() - bc.GetBlockByNumber(mid).Time()
if age <= minimumAge {
b = mid // mid is not prunable and less than b
} else {
a = mid // mid is prunable, but might equal a
}
}
return current.NumberU64() - a
}
10 changes: 10 additions & 0 deletions core/blockchain_repair_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1767,6 +1767,11 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
engine = ethash.NewFullFaker()
config = &CacheConfig{

// Arbitrum
TriesInMemory: 128,
TrieRetention: 30 * time.Minute,

TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
Expand Down Expand Up @@ -1891,6 +1896,11 @@ func TestIssue23496(t *testing.T) {
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
engine = ethash.NewFullFaker()
config = &CacheConfig{

// Arbitrum
TriesInMemory: 128,
TrieRetention: 30 * time.Minute,

TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
Expand Down
5 changes: 5 additions & 0 deletions core/blockchain_sethead_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1967,6 +1967,11 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
engine = ethash.NewFullFaker()
config = &CacheConfig{

// Arbitrum
TriesInMemory: 128,
TrieRetention: 30 * time.Minute,

TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
Expand Down
15 changes: 15 additions & 0 deletions core/blockchain_snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,11 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) {

// Insert a few more blocks without enabling snapshot
var cacheConfig = &CacheConfig{

// Arbitrum
TriesInMemory: 128,
TrieRetention: 30 * time.Minute,

TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
Expand Down Expand Up @@ -363,6 +368,11 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
chain.Stop()

config := &CacheConfig{

// Arbitrum
TriesInMemory: 128,
TrieRetention: 30 * time.Minute,

TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
Expand All @@ -378,6 +388,11 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {

// Restart the chain, the wiper should starts working
config = &CacheConfig{

// Arbitrum
TriesInMemory: 128,
TrieRetention: 30 * time.Minute,

TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
Expand Down
5 changes: 5 additions & 0 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
EnablePreimageRecording: config.EnablePreimageRecording,
}
cacheConfig = &core.CacheConfig{

// Arbitrum
TriesInMemory: 128,
TrieRetention: 30 * time.Minute,

TrieCleanLimit: config.TrieCleanCache,
TrieCleanJournal: stack.ResolvePath(config.TrieCleanCacheJournal),
TrieCleanRejournal: config.TrieCleanCacheRejournal,
Expand Down
2 changes: 1 addition & 1 deletion eth/gasprice/gasprice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
// Construct testing chain
diskdb := rawdb.NewMemoryDatabase()
gspec.Commit(diskdb)
chain, err := core.NewBlockChain(diskdb, &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec.Config, engine, vm.Config{}, nil, nil)
chain, err := core.NewBlockChain(diskdb, &core.CacheConfig{TrieCleanNoPrefetch: true, TriesInMemory: 128}, gspec.Config, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to create local chain, %v", err)
}
Expand Down
5 changes: 5 additions & 0 deletions eth/tracers/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i i
// Import the canonical chain
gspec.MustCommit(backend.chaindb)
cacheConfig := &core.CacheConfig{

// Arbitrum
TriesInMemory: 128,
TrieRetention: 30 * time.Minute,

TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
Expand Down
2 changes: 1 addition & 1 deletion miner/worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
}
genesis := gspec.MustCommit(db)

chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil, nil)
chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true, TriesInMemory: 128}, gspec.Config, engine, vm.Config{}, nil, nil)
txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain)

// Generate a small n-block chain and an uncle block for it
Expand Down
2 changes: 1 addition & 1 deletion tests/block_test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (t *BlockTest) Run(snapshotter bool) error {
} else {
engine = ethash.NewShared()
}
cache := &core.CacheConfig{TrieCleanLimit: 0}
cache := &core.CacheConfig{TrieCleanLimit: 0, TriesInMemory: 128}
if snapshotter {
cache.SnapshotLimit = 1
cache.SnapshotWait = true
Expand Down

0 comments on commit 9cf4cb7

Please sign in to comment.