Skip to content

Commit

Permalink
feat: supprt fastcache
Browse files Browse the repository at this point in the history
  • Loading branch information
flywukong committed Jul 12, 2024
1 parent 695740c commit 74a7719
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 93 deletions.
14 changes: 8 additions & 6 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
}

// Initialise cache among blocks
bc.cacheAmongBlocks = state.NewCacheAmongBlocks()
// Start future block processor.
bc.wg.Add(1)
go bc.updateFutureBlocks()
Expand Down Expand Up @@ -2242,15 +2241,18 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
}

// Check whether the cache pool among blocks can be used
//If parent root is the same, use it
if bc.cacheAmongBlocks == nil {
bc.cacheAmongBlocks = state.NewCacheAmongBlocks(parent.Root)
}
// Check whether the cache pool among blocks can be used, if parent root is the same, use it
// Else drop and reset the cache.
if parent.Root != bc.cacheAmongBlocks.GetRoot() {
log.Error("root is not same with cache root", "parent root:", parent.Root,
log.Info("root is not same with cache root", "parent root:", parent.Root,
"cache root", bc.cacheAmongBlocks.GetRoot())
bc.cacheAmongBlocks = state.NewCacheAmongBlocks()
bc.cacheAmongBlocks.Reset()
}
log.Info("new state db with cache", "cache root", bc.cacheAmongBlocks.GetRoot())

//log.Info("new state db with cache", "cache root", bc.cacheAmongBlocks.GetRoot())
statedb, err := state.NewWithCacheAmongBlocks(parent.Root, bc.stateCache, bc.snaps, bc.cacheAmongBlocks)
if err != nil {
return it.index, err
Expand Down
85 changes: 47 additions & 38 deletions core/state/shared_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ package state
import (
"sync"

"github.com/ethereum/go-ethereum/common/lru"
"github.com/VictoriaMetrics/fastcache"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"

"github.com/ethereum/go-ethereum/common"
)

const (
AccountCacheSize = 10
StorageCacheSize = 100
)

// StoragePool is used to store maps of originStorage of stateObjects
type StoragePool struct {
sync.RWMutex
Expand Down Expand Up @@ -41,66 +47,69 @@ func (s *StoragePool) getStorage(address common.Address) *sync.Map {
return storageMap
}

// CacheAmongBlocks is used to store difflayer data in a flat cache,
// it only stores the latest version of the data
type CacheAmongBlocks struct {
// Cache among blocks
cacheRoot common.Hash
aMux sync.Mutex
sMux sync.Mutex
accountsCache *lru.Cache[common.Hash, *types.SlimAccount]
storagesCache *lru.Cache[string, []byte]
//storagesCache2 *lru.Cache[common.Hash, map[common.Hash][]byte]
//accountsCache *fastcache.Cache
//storagesCache *fastcache.Cache
sMux sync.Mutex // TODO use mutex to update the cache if pipeline used the cache
accountsCache *fastcache.Cache
storagesCache *fastcache.Cache
}

func NewCacheAmongBlocks() *CacheAmongBlocks {
func NewCacheAmongBlocks(cacheRoot common.Hash) *CacheAmongBlocks {
return &CacheAmongBlocks{
cacheRoot: types.EmptyRootHash,
accountsCache: lru.NewCache[common.Hash, *types.SlimAccount](10000),
storagesCache: lru.NewCache[string, []byte](80000),
//storagesCache2: lru.NewCache[common.Hash, map[common.Hash][]byte](20000),
// storagesCache2: map[string]map[common.Hash],
//lru.NewCache[string, []byte](250000),
// accountsCache: fastcache.New(10000),
// storagesCache: fastcache.New(10000),
cacheRoot: cacheRoot,
accountsCache: fastcache.New(AccountCacheSize * 1024 * 1024),
storagesCache: fastcache.New(StorageCacheSize * 1024 * 1024),
}
}

func (c *CacheAmongBlocks) GetRoot() common.Hash {
return c.cacheRoot
}

func (c *CacheAmongBlocks) Purge() {
// c.accountsCache.Purge()
c.storagesCache.Purge()
func (c *CacheAmongBlocks) PurgeStorageCache() {
c.storagesCache.Reset()
}

func (c *CacheAmongBlocks) Reset() {
c.accountsCache.Reset()
c.storagesCache.Reset()
c.cacheRoot = types.EmptyRootHash
}

func (c *CacheAmongBlocks) SetRoot(root common.Hash) {
c.cacheRoot = root
}

func (c *CacheAmongBlocks) GetAccount(key common.Hash) (*types.SlimAccount, bool) {
//return c.accountsCache.HasGet(nil, key)
return c.accountsCache.Get(key)
}

func (c *CacheAmongBlocks) GetAccountsNum() int {
return len(c.accountsCache.Keys())
}

func (c *CacheAmongBlocks) GetStorageNum() int {
return len(c.storagesCache.Keys())
if blob, found := c.accountsCache.HasGet(nil, key[:]); found {
if len(blob) == 0 { // can be both nil and []byte{}
return nil, true
}
account := new(types.SlimAccount)
if err := rlp.DecodeBytes(blob, account); err != nil {
panic(err)
} else {
return account, true
}
}
return nil, false
}

func (c *CacheAmongBlocks) GetStorage(key string) ([]byte, bool) {
//return c.storagesCache.HasGet(nil, key)
return c.storagesCache.Get(key)
func (c *CacheAmongBlocks) GetStorage(accountHash common.Hash, storageKey common.Hash) ([]byte, bool) {
key := append(accountHash.Bytes(), storageKey.Bytes()...)
if blob, found := c.storagesCache.HasGet(nil, key); found {
return blob, true
}
return nil, false
}

func (c *CacheAmongBlocks) SetAccount(key common.Hash, account *types.SlimAccount) {
c.accountsCache.Add(key, account)
func (c *CacheAmongBlocks) SetAccount(key common.Hash, account []byte) {
c.accountsCache.Set(key[:], account)
}

func (c *CacheAmongBlocks) SetStorage(key string, value []byte) {
c.storagesCache.Add(key, value)
func (c *CacheAmongBlocks) SetStorage(accountHash common.Hash, storageKey common.Hash, value []byte) {
key := append(accountHash.Bytes(), storageKey.Bytes()...)
c.storagesCache.Set(key, value)
}
2 changes: 0 additions & 2 deletions core/state/snapshot/difflayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@ func (dl *diffLayer) rebloom(origin *diskLayer) {
parent.lock.RUnlock()
} else {
dl.diffed, _ = bloomfilter.New(uint64(bloomSize), uint64(bloomFuncs))
log.Info("renew bloom filter")
}
// Iterate over all the accounts and storage slots and index them
for hash := range dl.destructSet {
Expand Down Expand Up @@ -445,7 +444,6 @@ func (dl *diffLayer) storage(accountHash, storageHash common.Hash, depth int) ([
if n := len(data); n > 0 {
snapshotDirtyStorageReadMeter.Mark(int64(n))
} else {
log.Info("difflayer hit ", "account ", accountHash, "storage hash", storageHash, "data len", 0)
snapshotDirtyStorageInexMeter.Mark(1)
}
snapshotBloomStorageTrueHitMeter.Mark(1)
Expand Down
1 change: 1 addition & 0 deletions core/state/snapshot/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func generateSnapshot(diskdb ethdb.KeyValueStore, triedb *triedb.Database, cache
if err := batch.Write(); err != nil {
log.Crit("Failed to write initialized state marker", "err", err)
}
log.Info("use fast cache", "cache size", cache)
base := &diskLayer{
diskdb: diskdb,
triedb: triedb,
Expand Down
3 changes: 2 additions & 1 deletion core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,11 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
start := time.Now()
existInCache := false
storageKey := crypto.Keccak256Hash(key.Bytes())

// Try to get from cache among blocks if root is not nil
if s.db.cacheAmongBlocks != nil && s.db.cacheAmongBlocks.GetRoot() == s.db.originalRoot {
start1 := time.Now()
enc, existInCache = s.db.cacheAmongBlocks.GetStorage(s.addrHash.String() + storageKey.String())
enc, existInCache = s.db.cacheAmongBlocks.GetStorage(s.addrHash, storageKey)
if existInCache {
SnapshotBlockCacheStorageHitMeter.Mark(1)
BlockCacheStorageTimer.Update(time.Since(start1))
Expand Down
56 changes: 10 additions & 46 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ type StateDB struct {
onCommit func(states *triestate.Set) // Hook invoked when commit is performed
}

// NewWithSharedPool creates a new state with sharedStorge on layer 1.5
// NewWithCacheAmongBlocks creates a new state with a cache which store the data among blocks
func NewWithCacheAmongBlocks(root common.Hash, db Database, snaps *snapshot.Tree, cache *CacheAmongBlocks) (*StateDB, error) {
statedb, err := New(root, db, snaps)
if err != nil {
Expand Down Expand Up @@ -963,7 +963,7 @@ func (s *StateDB) copyInternal(doPrefetch bool) *StateDB {
snap: s.snap,
}

// state.cacheAmongBlocks = s.cacheAmongBlocks
state.cacheAmongBlocks = s.cacheAmongBlocks
// Copy the dirty states, logs, and preimages
for addr := range s.journal.dirties {
// As documented [here](https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527),
Expand Down Expand Up @@ -1823,9 +1823,6 @@ func (s *StateDB) Commit(block uint64, failPostCommitFunc func(), postCommitFunc
root = types.EmptyRootHash
}

if root != s.expectedRoot {
log.Warn("compare root not same", "state root", root, "expected root", s.expectedRoot)
}
if s.cacheAmongBlocks != nil {
s.cacheAmongBlocks.SetRoot(root)
}
Expand All @@ -1837,6 +1834,7 @@ func (s *StateDB) Commit(block uint64, failPostCommitFunc func(), postCommitFunc
s.storagesOrigin = make(map[common.Address]map[common.Hash][]byte)
s.stateObjectsDirty = make(map[common.Address]struct{})
s.stateObjectsDestruct = make(map[common.Address]*types.StateAccount)
s.cacheAmongBlocks = nil
return root, diffLayer, nil
}

Expand All @@ -1847,24 +1845,19 @@ func (s *StateDB) SnapToDiffLayer() ([]common.Address, []types.DiffAccount, []ty
if s.cacheAmongBlocks != nil {
obj, exist := s.stateObjects[accountAddr]
if !exist {
s.cacheAmongBlocks.SetAccount(crypto.Keccak256Hash(accountAddr.Bytes()), nil)
// log.Info("cache set the destruct as nil", "account", crypto.Keccak256Hash(accountAddr.Bytes()))
s.cacheAmongBlocks.SetAccount(crypto.Keccak256Hash(accountAddr.Bytes()), []byte(""))
} else {
s.cacheAmongBlocks.SetAccount(obj.addrHash, nil)
log.Info("cache set the destruct as nil", "account", obj.addrHash)
s.cacheAmongBlocks.SetAccount(obj.addrHash, []byte(""))
}
// if it is a CA account, purge the storage cache to avoid reading dirty storage data
if account != nil && account.Root != types.EmptyRootHash {
log.Info("it is CA account", "root", account.Root, "account hash", accountAddr)
SnapshotBlockCacheStoragePurge.Mark(1)
s.cacheAmongBlocks.Purge()
s.cacheAmongBlocks.PurgeStorageCache()
}
}

}

keysize := 0
valSize := 0
keyNum := 0
accounts := make([]types.DiffAccount, 0, len(s.accounts))
for accountHash, account := range s.accounts {
accounts = append(accounts, types.DiffAccount{
Expand All @@ -1873,32 +1866,10 @@ func (s *StateDB) SnapToDiffLayer() ([]common.Address, []types.DiffAccount, []ty
})

if s.cacheAmongBlocks != nil {
keyNum++
acc := new(types.SlimAccount)
if err := rlp.DecodeBytes(account, acc); err == nil {
keysize += len(accountHash)
valSize += len(account)
s.cacheAmongBlocks.SetAccount(accountHash, acc)
} else {
log.Error("decode account err", "err", err.Error())
panic("Shouldn't happen!")
}
s.cacheAmongBlocks.SetAccount(accountHash, account)
}
}

if keyNum >= 1 {
log.Info("account avg size of cache storage", "key", keysize/keyNum, "value", valSize/keyNum,
"total", (keysize+valSize)/keyNum)
}

/*
log.Info(" SnapToDiffLayer info",
"account num of cacheAmongBlocks is", s.cacheAmongBlocks.GetAccountsNum(),
"storage num of cacheAmongBlocks is", s.cacheAmongBlocks.GetStorageNum())
*/
keysize = 0
valSize = 0
keyNum = 0
storages := make([]types.DiffStorage, 0, len(s.storages))
for accountHash, storage := range s.storages {
keys := make([]common.Hash, 0, len(storage))
Expand All @@ -1907,11 +1878,7 @@ func (s *StateDB) SnapToDiffLayer() ([]common.Address, []types.DiffAccount, []ty
keys = append(keys, k)
values = append(values, v)
if s.cacheAmongBlocks != nil {
keyNum++
cacheKey := accountHash.String() + k.String()
keysize += len(cacheKey)
valSize += len(v)
s.cacheAmongBlocks.SetStorage(cacheKey, v)
s.cacheAmongBlocks.SetStorage(accountHash, k, v)
}
}
storages = append(storages, types.DiffStorage{
Expand All @@ -1920,10 +1887,7 @@ func (s *StateDB) SnapToDiffLayer() ([]common.Address, []types.DiffAccount, []ty
Vals: values,
})
}
if keyNum >= 1 {
log.Info("storage avg size of cache storage", "key", keysize/keyNum, "value", valSize/keyNum,
"total", (keysize+valSize)/keyNum)
}

return destructs, accounts, storages
}

Expand Down

0 comments on commit 74a7719

Please sign in to comment.