From b74240ff7664bd4bbe2222136805119e5ca4bcb1 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 14 Oct 2016 05:54:16 +0200 Subject: [PATCH] trie, core/state: use cache unloading for account trie --- core/blockchain.go | 2 +- core/state/state_object.go | 4 ++-- core/state/statedb.go | 7 +++++-- eth/downloader/downloader_test.go | 2 +- light/trie.go | 6 +++--- trie/secure_trie.go | 11 +++++++---- trie/secure_trie_test.go | 4 ++-- 7 files changed, 21 insertions(+), 15 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index f68f4fc0df..7657fce78e 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -269,7 +269,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error { if block == nil { return fmt.Errorf("non existent block [%x…]", hash[:4]) } - if _, err := trie.NewSecure(block.Root(), self.chainDb); err != nil { + if _, err := trie.NewSecure(block.Root(), self.chainDb, 0); err != nil { return err } // If all checks out, manually set the head block diff --git a/core/state/state_object.go b/core/state/state_object.go index 6eab27d9e7..edb073173d 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -137,9 +137,9 @@ func (self *StateObject) markSuicided() { func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie { if c.trie == nil { var err error - c.trie, err = trie.NewSecure(c.data.Root, db) + c.trie, err = trie.NewSecure(c.data.Root, db, 0) if err != nil { - c.trie, _ = trie.NewSecure(common.Hash{}, db) + c.trie, _ = trie.NewSecure(common.Hash{}, db, 0) c.setError(fmt.Errorf("can't create storage trie: %v", err)) } } diff --git a/core/state/statedb.go b/core/state/statedb.go index ec9e9392f0..07e1bd94a3 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -43,6 +43,9 @@ const ( // is max uncle depth + 1. maxTrieCacheLength = 8 + // Trie cache generation limit. + maxTrieCacheGen = 100 + // Number of codehash->size associations to keep. codeSizeCacheSize = 100000 ) @@ -86,7 +89,7 @@ type StateDB struct { // Create a new state from a given trie func New(root common.Hash, db ethdb.Database) (*StateDB, error) { - tr, err := trie.NewSecure(root, db) + tr, err := trie.NewSecure(root, db, maxTrieCacheGen) if err != nil { return nil, err } @@ -155,7 +158,7 @@ func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) { return &tr, nil } } - return trie.NewSecure(root, self.db) + return trie.NewSecure(root, self.db, maxTrieCacheGen) } func (self *StateDB) pushTrie(t *trie.SecureTrie) { diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index ae9a85ae17..366c248bbe 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -286,7 +286,7 @@ func (dl *downloadTester) headFastBlock() *types.Block { func (dl *downloadTester) commitHeadBlock(hash common.Hash) error { // For now only check that the state trie is correct if block := dl.getBlock(hash); block != nil { - _, err := trie.NewSecure(block.Root(), dl.stateDb) + _, err := trie.NewSecure(block.Root(), dl.stateDb, 0) return err } return fmt.Errorf("non existent block: %x", hash[:4]) diff --git a/light/trie.go b/light/trie.go index e9c96ea48e..42a943d50c 100644 --- a/light/trie.go +++ b/light/trie.go @@ -79,7 +79,7 @@ func (t *LightTrie) do(ctx context.Context, fallbackKey []byte, fn func() error) func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error) { err = t.do(ctx, key, func() (err error) { if t.trie == nil { - t.trie, err = trie.NewSecure(t.originalRoot, t.db) + t.trie, err = trie.NewSecure(t.originalRoot, t.db, 0) } if err == nil { res, err = t.trie.TryGet(key) @@ -98,7 +98,7 @@ func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error) func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) { err = t.do(ctx, key, func() (err error) { if t.trie == nil { - t.trie, err = trie.NewSecure(t.originalRoot, t.db) + t.trie, err = trie.NewSecure(t.originalRoot, t.db, 0) } if err == nil { err = t.trie.TryUpdate(key, value) @@ -112,7 +112,7 @@ func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) { func (t *LightTrie) Delete(ctx context.Context, key []byte) (err error) { err = t.do(ctx, key, func() (err error) { if t.trie == nil { - t.trie, err = trie.NewSecure(t.originalRoot, t.db) + t.trie, err = trie.NewSecure(t.originalRoot, t.db, 0) } if err == nil { err = t.trie.TryDelete(key) diff --git a/trie/secure_trie.go b/trie/secure_trie.go index c0ff6f5601..4d9ebe4d37 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -49,8 +49,12 @@ type SecureTrie struct { // If root is the zero hash or the sha3 hash of an empty string, the // trie is initially empty. Otherwise, New will panic if db is nil // and returns MissingNodeError if the root node cannot be found. +// // Accessing the trie loads nodes from db on demand. -func NewSecure(root common.Hash, db Database) (*SecureTrie, error) { +// Loaded nodes are kept around until their 'cache generation' expires. +// A new cache generation is created by each call to Commit. +// cachelimit sets the number of past cache generations to keep. +func NewSecure(root common.Hash, db Database, cachelimit uint16) (*SecureTrie, error) { if db == nil { panic("NewSecure called with nil database") } @@ -58,9 +62,8 @@ func NewSecure(root common.Hash, db Database) (*SecureTrie, error) { if err != nil { return nil, err } - return &SecureTrie{ - trie: *trie, - }, nil + trie.SetCacheLimit(cachelimit) + return &SecureTrie{trie: *trie}, nil } // Get returns the value for key stored in the trie. diff --git a/trie/secure_trie_test.go b/trie/secure_trie_test.go index 3171b8c315..159640fdaf 100644 --- a/trie/secure_trie_test.go +++ b/trie/secure_trie_test.go @@ -29,7 +29,7 @@ import ( func newEmptySecure() *SecureTrie { db, _ := ethdb.NewMemDatabase() - trie, _ := NewSecure(common.Hash{}, db) + trie, _ := NewSecure(common.Hash{}, db, 0) return trie } @@ -37,7 +37,7 @@ func newEmptySecure() *SecureTrie { func makeTestSecureTrie() (ethdb.Database, *SecureTrie, map[string][]byte) { // Create an empty trie db, _ := ethdb.NewMemDatabase() - trie, _ := NewSecure(common.Hash{}, db) + trie, _ := NewSecure(common.Hash{}, db, 0) // Fill it with some arbitrary data content := make(map[string][]byte)