diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index c7dddab0ad..2f13631607 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -128,6 +128,10 @@ type Snapshot interface { // Storage directly retrieves the storage data associated with a particular hash, // within a particular account. Storage(accountHash, storageHash common.Hash) ([]byte, error) + + // Parent returns the subsequent layer of a snapshot, or nil if the base was + // reached. + Parent() snapshot } // snapshot is the internal version of the snapshot data layer that supports some @@ -135,13 +139,6 @@ type Snapshot interface { type snapshot interface { Snapshot - // Parent returns the subsequent layer of a snapshot, or nil if the base was - // reached. - // - // Note, the method is an internal helper to avoid type switching between the - // disk and diff layers. There is no locking involved. - Parent() snapshot - // Update creates a new layer on top of the existing snapshot diff tree with // the specified data items. // diff --git a/core/state/statedb.go b/core/state/statedb.go index e7a3eff569..ff047f6cf7 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -214,7 +214,12 @@ func (s *StateDB) StartPrefetcher(namespace string) { s.prefetcher = nil } if s.snap != nil { - s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace) + parent := s.snap.Parent() + if parent != nil { + s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, parent.Root(), namespace) + } else { + s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, common.Hash{}, namespace) + } } } @@ -967,7 +972,11 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { } } if s.prefetcher != nil && len(addressesToPrefetch) > 0 { - s.prefetcher.prefetch(s.originalRoot, addressesToPrefetch, emptyAddr) + if s.snap.Verified() { + s.prefetcher.prefetch(s.originalRoot, addressesToPrefetch, emptyAddr) + } else if s.prefetcher.rootPrefetch != (common.Hash{}) { + s.prefetcher.prefetch(s.prefetcher.rootPrefetch, addressesToPrefetch, emptyAddr) + } } // Invalidate journal because reverting across transactions is not allowed. s.clearJournalAndRefund() diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go index e42628dc67..9883273f7e 100644 --- a/core/state/trie_prefetcher.go +++ b/core/state/trie_prefetcher.go @@ -47,10 +47,11 @@ type prefetchMsg struct { // // Note, the prefetcher's API is not thread safe. type triePrefetcher struct { - db Database // Database to fetch trie nodes through - root common.Hash // Root hash of theaccount trie for metrics - fetches map[common.Hash]Trie // Partially or fully fetcher tries - fetchers map[common.Hash]*subfetcher // Subfetchers for each trie + db Database // Database to fetch trie nodes through + root common.Hash // Root hash of theaccount trie for metrics + rootPrefetch common.Hash + fetches map[common.Hash]Trie // Partially or fully fetcher tries + fetchers map[common.Hash]*subfetcher // Subfetchers for each trie closed int32 closeMainChan chan struct{} // it is to inform the mainLoop @@ -70,14 +71,20 @@ type triePrefetcher struct { storageDupMeter metrics.Meter storageSkipMeter metrics.Meter storageWasteMeter metrics.Meter + + accountStaleLoadMeter metrics.Meter + accountStaleDupMeter metrics.Meter + accountStaleSkipMeter metrics.Meter + accountStaleWasteMeter metrics.Meter } // newTriePrefetcher -func newTriePrefetcher(db Database, root common.Hash, namespace string) *triePrefetcher { +func newTriePrefetcher(db Database, root, rootPrefetch common.Hash, namespace string) *triePrefetcher { prefix := triePrefetchMetricsPrefix + namespace p := &triePrefetcher{ db: db, root: root, + rootPrefetch: rootPrefetch, fetchers: make(map[common.Hash]*subfetcher), // Active prefetchers use the fetchers map abortChan: make(chan *subfetcher, abortChanSize), closeAbortChan: make(chan struct{}), @@ -95,6 +102,11 @@ func newTriePrefetcher(db Database, root common.Hash, namespace string) *triePre storageDupMeter: metrics.GetOrRegisterMeter(prefix+"/storage/dup", nil), storageSkipMeter: metrics.GetOrRegisterMeter(prefix+"/storage/skip", nil), storageWasteMeter: metrics.GetOrRegisterMeter(prefix+"/storage/waste", nil), + + accountStaleLoadMeter: metrics.GetOrRegisterMeter(prefix+"/accountst/load", nil), + accountStaleDupMeter: metrics.GetOrRegisterMeter(prefix+"/accountst/dup", nil), + accountStaleSkipMeter: metrics.GetOrRegisterMeter(prefix+"/accountst/skip", nil), + accountStaleWasteMeter: metrics.GetOrRegisterMeter(prefix+"/accountst/waste", nil), } go p.abortLoop() go p.mainLoop() @@ -118,8 +130,10 @@ func (p *triePrefetcher) mainLoop() { for _, fetcher := range p.fetchers { p.abortChan <- fetcher // safe to do multiple times <-fetcher.term + if metrics.EnabledExpensive { - if fetcher.root == p.root { + switch fetcher.root { + case p.root: p.accountLoadMeter.Mark(int64(len(fetcher.seen))) p.accountDupMeter.Mark(int64(fetcher.dups)) p.accountSkipMeter.Mark(int64(len(fetcher.tasks))) @@ -129,7 +143,19 @@ func (p *triePrefetcher) mainLoop() { } fetcher.lock.Unlock() p.accountWasteMeter.Mark(int64(len(fetcher.seen))) - } else { + + case p.rootPrefetch: + p.accountStaleLoadMeter.Mark(int64(len(fetcher.seen))) + p.accountStaleDupMeter.Mark(int64(fetcher.dups)) + p.accountStaleSkipMeter.Mark(int64(len(fetcher.tasks))) + fetcher.lock.Lock() + for _, key := range fetcher.used { + delete(fetcher.seen, string(key)) + } + fetcher.lock.Unlock() + p.accountStaleWasteMeter.Mark(int64(len(fetcher.seen))) + + default: p.storageLoadMeter.Mark(int64(len(fetcher.seen))) p.storageDupMeter.Mark(int64(fetcher.dups)) p.storageSkipMeter.Mark(int64(len(fetcher.tasks))) @@ -140,6 +166,7 @@ func (p *triePrefetcher) mainLoop() { } fetcher.lock.Unlock() p.storageWasteMeter.Mark(int64(len(fetcher.seen))) + } } } diff --git a/core/state/trie_prefetcher_test.go b/core/state/trie_prefetcher_test.go index aa178dc9d0..8d8888fb24 100644 --- a/core/state/trie_prefetcher_test.go +++ b/core/state/trie_prefetcher_test.go @@ -55,7 +55,7 @@ func prefetchGuaranteed(prefetcher *triePrefetcher, root common.Hash, keys [][]b func TestCopyAndClose(t *testing.T) { db := filledStateDB() - prefetcher := newTriePrefetcher(db.db, db.originalRoot, "") + prefetcher := newTriePrefetcher(db.db, db.originalRoot, common.Hash{}, "") skey := common.HexToHash("aaa") prefetchGuaranteed(prefetcher, db.originalRoot, [][]byte{skey.Bytes()}, common.Hash{}) prefetchGuaranteed(prefetcher, db.originalRoot, [][]byte{skey.Bytes()}, common.Hash{}) @@ -80,7 +80,7 @@ func TestCopyAndClose(t *testing.T) { func TestUseAfterClose(t *testing.T) { db := filledStateDB() - prefetcher := newTriePrefetcher(db.db, db.originalRoot, "") + prefetcher := newTriePrefetcher(db.db, db.originalRoot, common.Hash{}, "") skey := common.HexToHash("aaa") prefetchGuaranteed(prefetcher, db.originalRoot, [][]byte{skey.Bytes()}, common.Hash{}) a := prefetcher.trie(db.originalRoot) @@ -96,7 +96,7 @@ func TestUseAfterClose(t *testing.T) { func TestCopyClose(t *testing.T) { db := filledStateDB() - prefetcher := newTriePrefetcher(db.db, db.originalRoot, "") + prefetcher := newTriePrefetcher(db.db, db.originalRoot, common.Hash{}, "") skey := common.HexToHash("aaa") prefetchGuaranteed(prefetcher, db.originalRoot, [][]byte{skey.Bytes()}, common.Hash{}) cpy := prefetcher.copy()