Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: enable difflayer hashcache by resolved interface conflict #2765

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading