Skip to content

Commit

Permalink
chore: enable difflayer hashcache by resolved interface conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
will@2012 committed Nov 25, 2024
1 parent a10ba79 commit fbd7e8e
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 12 deletions.
3 changes: 2 additions & 1 deletion triedb/pathdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
51 changes: 47 additions & 4 deletions triedb/pathdb/difflayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion triedb/pathdb/difflayer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion triedb/pathdb/disklayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
4 changes: 1 addition & 3 deletions triedb/pathdb/layertree.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 5 additions & 1 deletion triedb/pathdb/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
2 changes: 1 addition & 1 deletion triedb/pathdb/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down

0 comments on commit fbd7e8e

Please sign in to comment.