From fbd7e8e289ac967ce02fffdd57b3f421fde4b95a Mon Sep 17 00:00:00 2001 From: "will@2012" Date: Mon, 25 Nov 2024 09:33:06 +0800 Subject: [PATCH] chore: enable difflayer hashcache by resolved interface conflict --- triedb/pathdb/database.go | 3 +- triedb/pathdb/difflayer.go | 51 ++++++++++++++++++++++++++++++--- triedb/pathdb/difflayer_test.go | 2 +- triedb/pathdb/disklayer.go | 2 +- triedb/pathdb/layertree.go | 4 +-- triedb/pathdb/metrics.go | 6 +++- triedb/pathdb/reader.go | 2 +- 7 files changed, 58 insertions(+), 12 deletions(-) diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 1d472ae806..7984f18f8b 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -79,7 +79,8 @@ type layer interface { // already stale. // // Note, no error will be returned if the requested node is not found in database. - node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) + // Note, the hash parameter can access the diff-layer flat cache to speed up access. + node(owner common.Hash, path []byte, hash common.Hash, depth int) ([]byte, common.Hash, *nodeLoc, error) // rootHash returns the root hash for which this layer was made. rootHash() common.Hash diff --git a/triedb/pathdb/difflayer.go b/triedb/pathdb/difflayer.go index bce662ad17..f1a7c424f7 100644 --- a/triedb/pathdb/difflayer.go +++ b/triedb/pathdb/difflayer.go @@ -198,6 +198,12 @@ func (dl *diffLayer) originDiskLayer() *diskLayer { return dl.origin } +func (dl *diffLayer) updateOriginDiskLayer(persistLayer *diskLayer) { + dl.lock.Lock() + defer dl.lock.Unlock() + dl.origin = persistLayer +} + // rootHash implements the layer interface, returning the root hash of // corresponding state. func (dl *diffLayer) rootHash() common.Hash { @@ -220,12 +226,46 @@ func (dl *diffLayer) parentLayer() layer { // node implements the layer interface, retrieving the trie node blob with the // provided node information. No error will be returned if the node is not found. -func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) { +// The hash parameter can access the cache to speed up access. +func (dl *diffLayer) node(owner common.Hash, path []byte, hash common.Hash, depth int) ([]byte, common.Hash, *nodeLoc, error) { + if hash != (common.Hash{}) { + if n := dl.cache.Get(hash); n != nil { + // The query from the hash map is fastpath, + // avoiding recursive query of 128 difflayers. + diffHashCacheHitMeter.Mark(1) + diffHashCacheReadMeter.Mark(int64(len(n.Blob))) + return n.Blob, n.Hash, &nodeLoc{loc: locDiffLayer, depth: depth}, nil + } + } + + diffHashCacheMissMeter.Mark(1) + persistLayer := dl.originDiskLayer() + if hash != (common.Hash{}) && persistLayer != nil { + blob, rhash, nloc, err := persistLayer.node(owner, path, hash, depth+1) + if err != nil || rhash != hash { + // This is a bad case with a very low probability. + // r/w the difflayer cache and r/w the disklayer are not in the same lock, + // so in extreme cases, both reading the difflayer cache and reading the disklayer may fail, eg, disklayer is stale. + // In this case, fallback to the original 128-layer recursive difflayer query path. + diffHashCacheSlowPathMeter.Mark(1) + log.Debug("Retry difflayer due to query origin failed", + "owner", owner, "path", path, "query_hash", hash.String(), "return_hash", rhash.String(), "error", err) + return dl.intervalNode(owner, path, hash, 0) + } else { // This is the fastpath. + return blob, rhash, nloc, nil + } + } + diffHashCacheSlowPathMeter.Mark(1) + log.Debug("Retry difflayer due to origin is nil or hash is empty", + "owner", owner, "path", path, "query_hash", hash.String(), "disk_layer_is_empty", persistLayer == nil) + return dl.intervalNode(owner, path, hash, 0) +} + +func (dl *diffLayer) intervalNode(owner common.Hash, path []byte, hash common.Hash, depth int) ([]byte, common.Hash, *nodeLoc, error) { // Hold the lock, ensure the parent won't be changed during the // state accessing. dl.lock.RLock() defer dl.lock.RUnlock() - // If the trie node is known locally, return it subset, ok := dl.nodes[owner] if ok { @@ -237,9 +277,12 @@ func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co return n.Blob, n.Hash, &nodeLoc{loc: locDiffLayer, depth: depth}, nil } } - //TODO(will-2012): https://github.com/bnb-chain/bsc/pull/2508 broken // Trie node unknown to this layer, resolve from parent - return dl.parent.node(owner, path, depth+1) + if diff, ok := dl.parent.(*diffLayer); ok { + return diff.intervalNode(owner, path, hash, depth+1) + } + // Failed to resolve through diff layers, fallback to disk layer + return dl.parent.node(owner, path, hash, depth+1) } // update implements the layer interface, creating a new layer on top of the diff --git a/triedb/pathdb/difflayer_test.go b/triedb/pathdb/difflayer_test.go index 14e021c88c..23d920c6b9 100644 --- a/triedb/pathdb/difflayer_test.go +++ b/triedb/pathdb/difflayer_test.go @@ -90,7 +90,7 @@ func benchmarkSearch(b *testing.B, depth int, total int) { err error ) for i := 0; i < b.N; i++ { - have, _, _, err = layer.node(common.Hash{}, npath, 0) + have, _, _, err = layer.node(common.Hash{}, npath, common.Hash{}, 0) if err != nil { b.Fatal(err) } diff --git a/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go index c421133e4f..1a411a42d9 100644 --- a/triedb/pathdb/disklayer.go +++ b/triedb/pathdb/disklayer.go @@ -150,7 +150,7 @@ func (dl *diskLayer) markStale() { // node implements the layer interface, retrieving the trie node with the // provided node info. No error will be returned if the node is not found. -func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) { +func (dl *diskLayer) node(owner common.Hash, path []byte, hash common.Hash, depth int) ([]byte, common.Hash, *nodeLoc, error) { dl.lock.RLock() defer dl.lock.RUnlock() diff --git a/triedb/pathdb/layertree.go b/triedb/pathdb/layertree.go index 0577401a96..b75b71e11f 100644 --- a/triedb/pathdb/layertree.go +++ b/triedb/pathdb/layertree.go @@ -231,9 +231,7 @@ func (tree *layerTree) cap(root common.Hash, layers int) error { var updateOriginFunc func(root common.Hash) updateOriginFunc = func(root common.Hash) { if diff, ok := tree.layers[root].(*diffLayer); ok { - diff.lock.Lock() - diff.origin = persisted - diff.lock.Unlock() + diff.updateOriginDiskLayer(persisted) } for _, child := range children[root] { updateOriginFunc(child) diff --git a/triedb/pathdb/metrics.go b/triedb/pathdb/metrics.go index d752b86f9b..327cca2a91 100644 --- a/triedb/pathdb/metrics.go +++ b/triedb/pathdb/metrics.go @@ -49,5 +49,9 @@ var ( historyDataBytesMeter = metrics.NewRegisteredMeter("pathdb/history/bytes/data", nil) historyIndexBytesMeter = metrics.NewRegisteredMeter("pathdb/history/bytes/index", nil) - diffHashCacheLengthGauge = metrics.NewRegisteredGauge("pathdb/difflayer/hashcache/size", nil) + diffHashCacheHitMeter = metrics.NewRegisteredMeter("pathdb/difflayer/hashcache/hit", nil) + diffHashCacheReadMeter = metrics.NewRegisteredMeter("pathdb/difflayer/hashcache/read", nil) + diffHashCacheMissMeter = metrics.NewRegisteredMeter("pathdb/difflayer/hashcache/miss", nil) + diffHashCacheSlowPathMeter = metrics.NewRegisteredMeter("pathdb/difflayer/hashcache/slowpath", nil) + diffHashCacheLengthGauge = metrics.NewRegisteredGauge("pathdb/difflayer/hashcache/size", nil) ) diff --git a/triedb/pathdb/reader.go b/triedb/pathdb/reader.go index 6a58493ba6..74a7e13ffb 100644 --- a/triedb/pathdb/reader.go +++ b/triedb/pathdb/reader.go @@ -56,7 +56,7 @@ type reader struct { // node info. Don't modify the returned byte slice since it's not deep-copied // and still be referenced by database. func (r *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) { - blob, got, loc, err := r.layer.node(owner, path, 0) + blob, got, loc, err := r.layer.node(owner, path, hash, 0) if err != nil { return nil, err }