Skip to content

Commit

Permalink
blockchain: make tries in memory configurable via flag (#401)
Browse files Browse the repository at this point in the history
This commit adds --triesinmemory flag to configure the number of tries that is
kept in memory before pruning.
  • Loading branch information
minh-bq authored Feb 7, 2024
1 parent 7a34f4b commit 9767790
Show file tree
Hide file tree
Showing 11 changed files with 51 additions and 31 deletions.
1 change: 1 addition & 0 deletions cmd/ronin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ var (
utils.GCModeFlag,
utils.SnapshotFlag,
utils.TxLookupLimitFlag,
utils.TriesInMemoryFlag,
utils.LightServeFlag,
utils.LightIngressFlag,
utils.LightEgressFlag,
Expand Down
1 change: 1 addition & 0 deletions cmd/ronin/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
utils.ExitWhenSyncedFlag,
utils.GCModeFlag,
utils.TxLookupLimitFlag,
utils.TriesInMemoryFlag,
utils.EthStatsURLFlag,
utils.IdentityFlag,
utils.LightKDFFlag,
Expand Down
6 changes: 6 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ var (
Usage: "Number of recent blocks to maintain transactions index for (default = about one year, 0 = entire chain)",
Value: ethconfig.Defaults.TxLookupLimit,
}
TriesInMemoryFlag = cli.IntFlag{
Name: "triesinmemory",
Usage: "The number of tries is kept in memory before pruning (default = 128)",
Value: 128,
}
MonitorDoubleSign = cli.BoolFlag{
Name: "monitor.doublesign",
Usage: "Enable double sign monitoring",
Expand Down Expand Up @@ -1730,6 +1735,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheSnapshotFlag.Name) {
cfg.SnapshotCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheSnapshotFlag.Name) / 100
}
cfg.TriesInMemory = ctx.GlobalInt(TriesInMemoryFlag.Name)
if !ctx.GlobalBool(SnapshotFlag.Name) {
// If snap-sync is requested, this flag is also required
if cfg.SyncMode == downloader.SnapSync {
Expand Down
22 changes: 16 additions & 6 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const (
txLookupCacheLimit = 1024
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
TriesInMemory = 128
DefaultTriesInMemory = 128
dirtyAccountsCacheLimit = 32
internalTxsCacheLimit = 32

Expand Down Expand Up @@ -136,6 +136,7 @@ type CacheConfig struct {
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
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
TriesInMemory int // The number of tries is kept in memory before pruning

SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
}
Expand Down Expand Up @@ -235,6 +236,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
if cacheConfig == nil {
cacheConfig = defaultCacheConfig
}
if cacheConfig.TriesInMemory == 0 {
cacheConfig.TriesInMemory = DefaultTriesInMemory
}
bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit)
receiptsCache, _ := lru.New(receiptsCacheLimit)
Expand Down Expand Up @@ -933,7 +937,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, uint64(bc.cacheConfig.TriesInMemory) - 1} {
if number := bc.CurrentBlock().NumberU64(); number > offset {
recent := bc.GetBlockByNumber(number - offset)

Expand Down Expand Up @@ -1456,7 +1460,8 @@ 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 {
triesInMemory := uint64(bc.cacheConfig.TriesInMemory)
if current := block.NumberU64(); current > triesInMemory {
// If we exceeded our memory allowance, flush matured singleton nodes to disk
var (
nodes, imgs = triedb.Size()
Expand All @@ -1466,7 +1471,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 - triesInMemory

// If we exceeded out time allowance, flush an entire trie to disk
if bc.gcproc > bc.cacheConfig.TrieTimeLimit {
Expand All @@ -1478,8 +1483,13 @@ 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+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)/float64(triesInMemory),
)
}
// Flush an entire trie and restart the counters
triedb.Commit(header.Root, true, nil)
Expand Down
28 changes: 14 additions & 14 deletions core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1518,7 +1518,7 @@ func TestTrieForkGC(t *testing.T) {

db := rawdb.NewMemoryDatabase()
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }, true)
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*DefaultTriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }, true)

// Generate a bunch of fork blocks, each side forking from the canonical chain
forks := make([]*types.Block, len(blocks))
Expand Down Expand Up @@ -1547,7 +1547,7 @@ func TestTrieForkGC(t *testing.T) {
}
}
// Dereference all the recent tries and ensure no past trie is left in
for i := 0; i < TriesInMemory; i++ {
for i := 0; i < DefaultTriesInMemory; i++ {
chain.stateCache.TrieDB().Dereference(blocks[len(blocks)-1-i].Root())
chain.stateCache.TrieDB().Dereference(forks[len(blocks)-1-i].Root())
}
Expand All @@ -1566,8 +1566,8 @@ func TestLargeReorgTrieGC(t *testing.T) {
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)

shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }, true)
original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }, true)
competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) }, true)
original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*DefaultTriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }, true)
competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*DefaultTriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) }, true)

// Import the shared chain and the original canonical one
diskdb := rawdb.NewMemoryDatabase()
Expand Down Expand Up @@ -1602,7 +1602,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil {
t.Fatalf("failed to finalize competitor chain: %v", err)
}
for i, block := range competitor[:len(competitor)-TriesInMemory] {
for i, block := range competitor[:len(competitor)-DefaultTriesInMemory] {
if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
t.Fatalf("competitor %d: competing chain state missing", i)
}
Expand Down Expand Up @@ -1758,7 +1758,7 @@ func TestLowDiffLongChain(t *testing.T) {

// We must use a pretty long chain to ensure that the fork doesn't overtake us
// until after at least 128 blocks post tip
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*TriesInMemory, func(i int, b *BlockGen) {
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*DefaultTriesInMemory, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{1})
b.OffsetTime(-9)
}, true)
Expand All @@ -1776,7 +1776,7 @@ func TestLowDiffLongChain(t *testing.T) {
}
// Generate fork chain, starting from an early block
parent := blocks[10]
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*TriesInMemory, func(i int, b *BlockGen) {
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*DefaultTriesInMemory, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2})
}, true)

Expand Down Expand Up @@ -1811,7 +1811,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)

// Generate and import the canonical chain
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil, true)
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*DefaultTriesInMemory, nil, true)
diskdb := rawdb.NewMemoryDatabase()
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
Expand All @@ -1822,9 +1822,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}

lastPrunedIndex := len(blocks) - TriesInMemory - 1
lastPrunedIndex := len(blocks) - DefaultTriesInMemory - 1
lastPrunedBlock := blocks[lastPrunedIndex]
firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory]
firstNonPrunedBlock := blocks[len(blocks)-DefaultTriesInMemory]

// Verify pruning of lastPrunedBlock
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
Expand All @@ -1841,7 +1841,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
// Generate fork chain, make it longer than canon
parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
parent := blocks[parentIndex]
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 2*TriesInMemory, func(i int, b *BlockGen) {
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 2*DefaultTriesInMemory, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2})
}, true)
// Prepend the parent(s)
Expand Down Expand Up @@ -2483,7 +2483,7 @@ func TestSideImportPrunedBlocks(t *testing.T) {
genesis := (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)

// Generate and import the canonical chain
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil, true)
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*DefaultTriesInMemory, nil, true)
diskdb := rawdb.NewMemoryDatabase()
(&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
Expand All @@ -2494,14 +2494,14 @@ func TestSideImportPrunedBlocks(t *testing.T) {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}

lastPrunedIndex := len(blocks) - TriesInMemory - 1
lastPrunedIndex := len(blocks) - DefaultTriesInMemory - 1
lastPrunedBlock := blocks[lastPrunedIndex]

// Verify pruning of lastPrunedBlock
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
}
firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory]
firstNonPrunedBlock := blocks[len(blocks)-DefaultTriesInMemory]
// Verify firstNonPrunedBlock is not pruned
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
Expand Down
1 change: 1 addition & 0 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
TrieTimeLimit: config.TrieTimeout,
SnapshotLimit: config.SnapshotCache,
Preimages: config.Preimages,
TriesInMemory: config.TriesInMemory,
}
)
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit)
Expand Down
11 changes: 6 additions & 5 deletions eth/ethconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,11 @@ var Defaults = Config{
BlockProduceLeftOver: 200 * time.Millisecond,
BlockSizeReserve: 500000,
},
TxPool: core.DefaultTxPoolConfig,
RPCGasCap: 50000000,
RPCEVMTimeout: 5 * time.Second,
GPO: FullNodeGPO,
RPCTxFeeCap: 1, // 1 ether
TxPool: core.DefaultTxPoolConfig,
RPCGasCap: 50000000,
RPCEVMTimeout: 5 * time.Second,
GPO: FullNodeGPO,
RPCTxFeeCap: 1, // 1 ether
}

func init() {
Expand Down Expand Up @@ -172,6 +172,7 @@ type Config struct {
TrieTimeout time.Duration
SnapshotCache int
Preimages bool
TriesInMemory int

// Mining options
Miner miner.Config
Expand Down
4 changes: 2 additions & 2 deletions les/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ func TestGetStaleCodeLes4(t *testing.T) { testGetStaleCode(t, 4) }

func testGetStaleCode(t *testing.T, protocol int) {
netconfig := testnetConfig{
blocks: core.TriesInMemory + 4,
blocks: core.DefaultTriesInMemory + 4,
protocol: protocol,
nopruning: true,
}
Expand Down Expand Up @@ -430,7 +430,7 @@ func TestGetStaleProofLes4(t *testing.T) { testGetStaleProof(t, 4) }

func testGetStaleProof(t *testing.T, protocol int) {
netconfig := testnetConfig{
blocks: core.TriesInMemory + 4,
blocks: core.DefaultTriesInMemory + 4,
protocol: protocol,
nopruning: true,
}
Expand Down
2 changes: 1 addition & 1 deletion les/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -1058,7 +1058,7 @@ func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, ge

// If local ethereum node is running in archive mode, advertise ourselves we have
// all version state data. Otherwise only recent state is available.
stateRecent := uint64(core.TriesInMemory - blockSafetyMargin)
stateRecent := uint64(core.DefaultTriesInMemory - blockSafetyMargin)
if server.archiveMode {
stateRecent = 0
}
Expand Down
4 changes: 2 additions & 2 deletions les/server_requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ func handleGetCode(msg Decoder) (serveRequestFn, uint64, uint64, error) {
// Refuse to search stale state data in the database since looking for
// a non-exist key is kind of expensive.
local := bc.CurrentHeader().Number.Uint64()
if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local {
if !backend.ArchiveMode() && header.Number.Uint64()+core.DefaultTriesInMemory <= local {
p.Log().Debug("Reject stale code request", "number", header.Number.Uint64(), "head", local)
p.bumpInvalid()
continue
Expand Down Expand Up @@ -396,7 +396,7 @@ func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) {
// Refuse to search stale state data in the database since looking for
// a non-exist key is kind of expensive.
local := bc.CurrentHeader().Number.Uint64()
if !backend.ArchiveMode() && header.Number.Uint64()+core.TriesInMemory <= local {
if !backend.ArchiveMode() && header.Number.Uint64()+core.DefaultTriesInMemory <= local {
p.Log().Debug("Reject stale trie request", "number", header.Number.Uint64(), "head", local)
p.bumpInvalid()
continue
Expand Down
2 changes: 1 addition & 1 deletion les/test_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ func (p *testPeer) handshakeWithClient(t *testing.T, td *big.Int, head common.Ha
sendList = sendList.add("serveHeaders", nil)
sendList = sendList.add("serveChainSince", uint64(0))
sendList = sendList.add("serveStateSince", uint64(0))
sendList = sendList.add("serveRecentState", uint64(core.TriesInMemory-4))
sendList = sendList.add("serveRecentState", uint64(core.DefaultTriesInMemory-4))
sendList = sendList.add("txRelay", nil)
sendList = sendList.add("flowControl/BL", testBufLimit)
sendList = sendList.add("flowControl/MRR", testBufRecharge)
Expand Down

0 comments on commit 9767790

Please sign in to comment.