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

[WIP]feat: add snapshot multiversion cache #30073

Closed
Closed
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
118 changes: 109 additions & 9 deletions core/state/snapshot/difflayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package snapshot

import (
"bytes"
"encoding/binary"
"fmt"
"math"
Expand All @@ -28,6 +29,7 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
bloomfilter "github.com/holiman/bloomfilter/v2"
)
Expand Down Expand Up @@ -103,6 +105,9 @@ type diffLayer struct {
parent snapshot // Parent snapshot modified by this one, never nil
memory uint64 // Approximate guess as to how much memory we use

diffLayerID uint64 // diffLayerID is memory temp value, child_id = parent_id + 1, which is used as multi-version cache item version.
multiVersionCache *MultiVersionSnapshotCache // multiVersionCache is used to speed up the recursive query of 128 difflayers.

root common.Hash // Root hash to which this snapshot diff belongs to
stale atomic.Bool // Signals that the layer became stale (state progressed)

Expand Down Expand Up @@ -154,8 +159,12 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s
}
switch parent := parent.(type) {
case *diskLayer:
dl.diffLayerID = 1
dl.multiVersionCache = NewMultiVersionSnapshotCache()
dl.rebloom(parent)
case *diffLayer:
dl.diffLayerID = parent.diffLayerID + 1
dl.multiVersionCache = parent.multiVersionCache
dl.rebloom(parent.origin)
default:
panic("unknown parent type")
Expand Down Expand Up @@ -183,6 +192,14 @@ func newDiffLayer(parent snapshot, root common.Hash, destructs map[common.Hash]s
return dl
}

func (dl *diffLayer) AddToCache() {
dl.multiVersionCache.Add(dl)
}

func (dl *diffLayer) RemoveFromCache() {
dl.multiVersionCache.Remove(dl)
}

// rebloom discards the layer's current bloom and rebuilds it from scratch based
// on the parent's and the local diffs.
func (dl *diffLayer) rebloom(origin *diskLayer) {
Expand Down Expand Up @@ -272,6 +289,46 @@ func (dl *diffLayer) AccountRLP(hash common.Hash) ([]byte, error) {
dl.lock.RUnlock()
return nil, ErrSnapshotStale
}

{
// try fastpath
data, needTryDisk, err := dl.multiVersionCache.QueryAccount(dl.diffLayerID, dl.root, hash)
if err == nil {
if needTryDisk {
data, err = dl.origin.AccountRLP(hash)
diffMultiVersionCacheMissMeter.Mark(1)
} else {
diffMultiVersionCacheHitMeter.Mark(1)
diffMultiVersionCacheReadMeter.Mark(int64(len(data)))
}
if err != nil {
log.Warn("Account has bug due to query disklayer", "error", err)
diffMultiVersionCacheBugMeter.Mark(1)
}
dl.lock.RUnlock()

{
// todo: double check
expectedData, expectedErr := dl.accountRLP(hash, 0)
if !bytes.Equal(data, expectedData) {
log.Error("Has bug",
"query_version", dl.diffLayerID,
"query_root", dl.root,
"account_hash", hash,
"actual_hit_disk", needTryDisk,
"actual_disk_root", dl.origin.Root(),
"actual_data_len", len(data),
"expected_data_len", len(expectedData),
"actual_error", err,
"expected_error", expectedErr)
}
}
return data, err
}
log.Warn("Account has bug due to query multi version cache", "error", err)
diffMultiVersionCacheBugMeter.Mark(1)
}

// Check the bloom filter first whether there's even a point in reaching into
// all the maps in all the layers below
hit := dl.diffed.ContainsHash(accountBloomHash(hash))
Expand Down Expand Up @@ -345,6 +402,47 @@ func (dl *diffLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro
dl.lock.RUnlock()
return nil, ErrSnapshotStale
}

{
// try fastpath
data, needTryDisk, err := dl.multiVersionCache.QueryStorage(dl.diffLayerID, dl.root, accountHash, storageHash)
if err == nil {
if needTryDisk {
data, err = dl.origin.Storage(accountHash, storageHash)
diffMultiVersionCacheMissMeter.Mark(1)
} else {
diffMultiVersionCacheHitMeter.Mark(1)
diffMultiVersionCacheReadMeter.Mark(int64(len(data)))
}
if err != nil {
log.Warn("Storage has bug due to query disklayer", "error", err)
diffMultiVersionCacheBugMeter.Mark(1)
}
dl.lock.RUnlock()

{
// todo: double check
expectedData, expectedErr := dl.storage(accountHash, storageHash, 0)
if !bytes.Equal(data, expectedData) {
log.Warn("Has bug",
"query_version", dl.diffLayerID,
"query_root", dl.root,
"account_hash", accountHash,
"storage_hash", storageHash,
"actual_hit_disk", needTryDisk,
"actual_disk_root", dl.origin.Root(),
"actual_data_len", len(data),
"expected_data_len", len(expectedData),
"actual_error", err,
"expected_error", expectedErr)
}
}
return data, err
}
log.Warn("Storage has bug due to query multi version cache", "error", err)
diffMultiVersionCacheBugMeter.Mark(1)
}

hit := dl.diffed.ContainsHash(storageBloomHash(accountHash, storageHash))
if !hit {
hit = dl.diffed.ContainsHash(destructBloomHash(accountHash))
Expand Down Expand Up @@ -460,15 +558,17 @@ func (dl *diffLayer) flatten() snapshot {
}
// Return the combo parent
return &diffLayer{
parent: parent.parent,
origin: parent.origin,
root: dl.root,
destructSet: parent.destructSet,
accountData: parent.accountData,
storageData: parent.storageData,
storageList: make(map[common.Hash][]common.Hash),
diffed: dl.diffed,
memory: parent.memory + dl.memory,
parent: parent.parent,
origin: parent.origin,
diffLayerID: dl.diffLayerID,
multiVersionCache: dl.multiVersionCache,
root: dl.root,
destructSet: parent.destructSet,
accountData: parent.accountData,
storageData: parent.storageData,
storageList: make(map[common.Hash][]common.Hash),
diffed: dl.diffed,
memory: parent.memory + dl.memory,
}
}

Expand Down
4 changes: 3 additions & 1 deletion core/state/snapshot/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ func loadAndParseJournal(db ethdb.KeyValueStore, base *diskLayer) (snapshot, jou
// etc.), we just discard all diffs and try to recover them later.
var current snapshot = base
err := iterateJournal(db, func(parent common.Hash, root common.Hash, destructSet map[common.Hash]struct{}, accountData map[common.Hash][]byte, storageData map[common.Hash]map[common.Hash][]byte) error {
current = newDiffLayer(current, root, destructSet, accountData, storageData)
diff := newDiffLayer(current, root, destructSet, accountData, storageData)
diff.AddToCache() // add to multi-version cache from the journal at startup.
current = diff
return nil
})
if err != nil {
Expand Down
7 changes: 7 additions & 0 deletions core/state/snapshot/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,11 @@ var (
snapStorageWriteCounter = metrics.NewRegisteredCounter("state/snapshot/generation/duration/storage/write", nil)
// snapStorageCleanCounter measures time spent on deleting storages
snapStorageCleanCounter = metrics.NewRegisteredCounter("state/snapshot/generation/duration/storage/clean", nil)

// multi version cache metrics
diffMultiVersionCacheHitMeter = metrics.NewRegisteredMeter("pathdb/difflayer/multiversioncache/hit", nil)
diffMultiVersionCacheReadMeter = metrics.NewRegisteredMeter("pathdb/difflayer/multiversioncache/read", nil)
diffMultiVersionCacheMissMeter = metrics.NewRegisteredMeter("pathdb/difflayer/multiversioncache/miss", nil)
diffMultiVersionCacheBugMeter = metrics.NewRegisteredMeter("pathdb/difflayer/multiversioncache/bug", nil)
diffMultiVersionCacheLengthGauge = metrics.NewRegisteredGauge("pathdb/difflayer/multiversioncache/size", nil)
)
Loading
Loading