From 674595d6c9b0774d6e27daa233d329b6b2a2a68e Mon Sep 17 00:00:00 2001 From: Alexey Akhunov Date: Thu, 9 Aug 2018 20:49:40 +0100 Subject: [PATCH 1/2] #461 Add Caching to Store Database --- state/database.go | 15 ++++++++++----- state/trie.go | 15 +++++++++++++-- test/run.go | 3 ++- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/state/database.go b/state/database.go index 5e13ccc1..c41c4ae7 100644 --- a/state/database.go +++ b/state/database.go @@ -59,6 +59,8 @@ type Database struct { // EXTCODESIZE calls. codeSizeCache *lru.Cache + dataCache *lru.Cache + Tracing bool } @@ -66,7 +68,7 @@ type Database struct { // implements Ethereum's state.Database interface. An error is returned if the // latest state failed to load. The underlying storage structure is defined by // the Cosmos SDK IAVL tree. -func NewDatabase(stateDB, codeDB dbm.DB) (*Database, error) { +func NewDatabase(stateDB, codeDB dbm.DB, dataCacheSize int) (*Database, error) { // Initialize an implementation of Ethereum state.Database and create a // Cosmos SDK multi-store. db := &Database{ @@ -94,6 +96,7 @@ func NewDatabase(stateDB, codeDB dbm.DB) (*Database, error) { db.ethTrieDB = ethtrie.NewDatabase(&core.EthereumDB{CodeDB: codeDB}) db.codeSizeCache, _ = lru.New(codeSizeCacheSize) + db.dataCache, _ = lru.New(dataCacheSize) return db, nil } @@ -133,6 +136,7 @@ func (db *Database) OpenTrie(root ethcmn.Hash) (ethstate.Trie, error) { store: db.accountsCache, accountsCache: db.accountsCache, storageCache: db.storageCache, + dataCache: db.dataCache, ethTrieDB: db.ethTrieDB, empty: isRootEmpty(root), root: rootHashFromVersion(db.stateStore.LastCommitID().Version), @@ -154,10 +158,11 @@ func (db *Database) OpenStorageTrie(addrHash, root ethcmn.Hash) (ethstate.Trie, // a contract storage trie does not need an accountCache, storageCache or // an Ethereum trie because it will not be used upon commitment. return &Trie{ - store: db.storageCache, - prefix: addrHash.Bytes(), - empty: isRootEmpty(root), - root: rootHashFromVersion(db.stateStore.LastCommitID().Version), + store: db.storageCache, + dataCache: db.dataCache, + prefix: addrHash.Bytes(), + empty: isRootEmpty(root), + root: rootHashFromVersion(db.stateStore.LastCommitID().Version), }, nil } diff --git a/state/trie.go b/state/trie.go index a29892ba..46017c89 100644 --- a/state/trie.go +++ b/state/trie.go @@ -8,6 +8,8 @@ import ( ethcmn "github.com/ethereum/go-ethereum/common" ethdb "github.com/ethereum/go-ethereum/ethdb" ethtrie "github.com/ethereum/go-ethereum/trie" + + lru "github.com/hashicorp/golang-lru" ) const ( @@ -25,6 +27,8 @@ type Trie struct { // ordering. storageCache store.CacheKVStore + dataCache *lru.Cache + // Store is an IAVL KV store that is part of a larger store except it used // for a specific prefix. It will either be an accountsCache or a // storageCache. @@ -64,8 +68,13 @@ func (t *Trie) TryGet(key []byte) ([]byte, error) { if t.IsStorageTrie() { key = t.prefixKey(key) } - - return t.store.Get(key), nil + keyStr := string(key) + if cached, ok := t.dataCache.Get(keyStr); ok { + return cached.([]byte), nil + } + value := t.store.Get(key) + t.dataCache.Add(keyStr, value) + return value, nil } // TryUpdate implements the Ethereum state.Trie interface. It associates a @@ -83,6 +92,7 @@ func (t *Trie) TryUpdate(key, value []byte) error { } t.store.Set(key, value) + t.dataCache.Add(string(key), value) return nil } @@ -98,6 +108,7 @@ func (t *Trie) TryDelete(key []byte) error { } t.store.Delete(key) + t.dataCache.Remove(string(key)) return nil } diff --git a/test/run.go b/test/run.go index 20a86fb3..e2b79a28 100644 --- a/test/run.go +++ b/test/run.go @@ -19,6 +19,7 @@ var ( cpuprofile = flag.String("cpu-profile", "", "write cpu profile `file`") blockchain = flag.String("blockchain", "data/blockchain", "file containing blocks to load") datadir = flag.String("datadir", path.Join(os.Getenv("HOME"), ".ethermint"), "directory for ethermint data") + cachesize = flag.Int("cachesize", 1024*1024, "number of key-value pairs for the state stored in memory") ) func main() { @@ -51,7 +52,7 @@ func main() { stateDB := dbm.NewDB("state", dbm.LevelDBBackend, *datadir) codeDB := dbm.NewDB("code", dbm.LevelDBBackend, *datadir) - ethermintDB, err := state.NewDatabase(stateDB, codeDB) + ethermintDB, err := state.NewDatabase(stateDB, codeDB, *cachesize) if err != nil { panic(fmt.Sprintf("failed to initialize geth Database: %v", err)) } From 80cfa77826e17cb933d805d0740bffbd3358b966 Mon Sep 17 00:00:00 2001 From: Alexey Akhunov Date: Fri, 10 Aug 2018 14:22:59 +0100 Subject: [PATCH 2/2] Addressed comments --- state/database.go | 25 +++++++++++++++---------- state/trie.go | 10 +++++----- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/state/database.go b/state/database.go index c41c4ae7..92e2c1df 100644 --- a/state/database.go +++ b/state/database.go @@ -59,7 +59,7 @@ type Database struct { // EXTCODESIZE calls. codeSizeCache *lru.Cache - dataCache *lru.Cache + storeCache *lru.Cache Tracing bool } @@ -68,7 +68,7 @@ type Database struct { // implements Ethereum's state.Database interface. An error is returned if the // latest state failed to load. The underlying storage structure is defined by // the Cosmos SDK IAVL tree. -func NewDatabase(stateDB, codeDB dbm.DB, dataCacheSize int) (*Database, error) { +func NewDatabase(stateDB, codeDB dbm.DB, storeCacheSize int) (*Database, error) { // Initialize an implementation of Ethereum state.Database and create a // Cosmos SDK multi-store. db := &Database{ @@ -95,8 +95,13 @@ func NewDatabase(stateDB, codeDB dbm.DB, dataCacheSize int) (*Database, error) { db.codeDB = codeDB db.ethTrieDB = ethtrie.NewDatabase(&core.EthereumDB{CodeDB: codeDB}) - db.codeSizeCache, _ = lru.New(codeSizeCacheSize) - db.dataCache, _ = lru.New(dataCacheSize) + var err error + if db.codeSizeCache, err = lru.New(codeSizeCacheSize); err != nil { + return nil, err + } + if db.storeCache, err = lru.New(storeCacheSize); err != nil { + return nil, err + } return db, nil } @@ -136,7 +141,7 @@ func (db *Database) OpenTrie(root ethcmn.Hash) (ethstate.Trie, error) { store: db.accountsCache, accountsCache: db.accountsCache, storageCache: db.storageCache, - dataCache: db.dataCache, + storeCache: db.storeCache, ethTrieDB: db.ethTrieDB, empty: isRootEmpty(root), root: rootHashFromVersion(db.stateStore.LastCommitID().Version), @@ -158,11 +163,11 @@ func (db *Database) OpenStorageTrie(addrHash, root ethcmn.Hash) (ethstate.Trie, // a contract storage trie does not need an accountCache, storageCache or // an Ethereum trie because it will not be used upon commitment. return &Trie{ - store: db.storageCache, - dataCache: db.dataCache, - prefix: addrHash.Bytes(), - empty: isRootEmpty(root), - root: rootHashFromVersion(db.stateStore.LastCommitID().Version), + store: db.storageCache, + storeCache: db.storeCache, + prefix: addrHash.Bytes(), + empty: isRootEmpty(root), + root: rootHashFromVersion(db.stateStore.LastCommitID().Version), }, nil } diff --git a/state/trie.go b/state/trie.go index 46017c89..7d78042f 100644 --- a/state/trie.go +++ b/state/trie.go @@ -27,7 +27,7 @@ type Trie struct { // ordering. storageCache store.CacheKVStore - dataCache *lru.Cache + storeCache *lru.Cache // Store is an IAVL KV store that is part of a larger store except it used // for a specific prefix. It will either be an accountsCache or a @@ -69,11 +69,11 @@ func (t *Trie) TryGet(key []byte) ([]byte, error) { key = t.prefixKey(key) } keyStr := string(key) - if cached, ok := t.dataCache.Get(keyStr); ok { + if cached, ok := t.storeCache.Get(keyStr); ok { return cached.([]byte), nil } value := t.store.Get(key) - t.dataCache.Add(keyStr, value) + t.storeCache.Add(keyStr, value) return value, nil } @@ -92,7 +92,7 @@ func (t *Trie) TryUpdate(key, value []byte) error { } t.store.Set(key, value) - t.dataCache.Add(string(key), value) + t.storeCache.Add(string(key), value) return nil } @@ -108,7 +108,7 @@ func (t *Trie) TryDelete(key []byte) error { } t.store.Delete(key) - t.dataCache.Remove(string(key)) + t.storeCache.Remove(string(key)) return nil }