Skip to content

Commit

Permalink
Merge pull request #646 from aidan-kwon/0827-trieCache
Browse files Browse the repository at this point in the history
Interfacing stateDB cache
  • Loading branch information
aidan-kwon committed Sep 1, 2020
2 parents 13f1934 + 7e00843 commit f99b4a2
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 44 deletions.
4 changes: 2 additions & 2 deletions blockchain/state/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ package state

import (
"fmt"
"github.com/VictoriaMetrics/fastcache"

"github.com/klaytn/klaytn/common"
"github.com/klaytn/klaytn/storage/database"
"github.com/klaytn/klaytn/storage/statedb"
Expand Down Expand Up @@ -138,7 +138,7 @@ func NewDatabaseWithNewCache(db database.DBManager, cacheSize int) Database {
// NewDatabaseWithExistingCache creates a backing store for state with given cache. The returned database
// is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a
// large memory cache.
func NewDatabaseWithExistingCache(db database.DBManager, cache *fastcache.Cache) Database {
func NewDatabaseWithExistingCache(db database.DBManager, cache statedb.Cache) Database {
return &cachingDB{
db: statedb.NewDatabaseWithExistingCache(db, cache),
codeSizeCache: getCodeSizeCache(),
Expand Down
37 changes: 22 additions & 15 deletions blockchain/state_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ package blockchain
import (
"errors"
"fmt"
"github.com/VictoriaMetrics/fastcache"
"runtime"
"strconv"
"strings"
"time"

"github.com/alecthomas/units"
lru "github.com/hashicorp/golang-lru"
"github.com/klaytn/klaytn/blockchain/state"
Expand All @@ -28,10 +32,6 @@ import (
"github.com/klaytn/klaytn/log"
"github.com/klaytn/klaytn/storage/database"
"github.com/klaytn/klaytn/storage/statedb"
"runtime"
"strconv"
"strings"
"time"
)

type stateTrieMigrationDB struct {
Expand Down Expand Up @@ -405,22 +405,29 @@ func (bc *BlockChain) concurrentIterateTrie(root common.Hash, db state.Database,
return nil
}

func (bc *BlockChain) warmUpLoop(cache *fastcache.Cache, mainTrieCacheLimit uint64, children []common.Hash, resultHashCh chan common.Hash, resultErrCh chan error) {
func (bc *BlockChain) warmUpLoop(cache statedb.Cache, mainTrieCacheLimit uint64, children []common.Hash,
resultHashCh chan common.Hash, resultErrCh chan error) {
logged := time.Now()
var context []interface{}
var stats fastcache.Stats
var percent uint64
var cnt int

updateContext := func() {
stats = fastcache.Stats{}
cache.UpdateStats(&stats)
percent = stats.BytesSize * 100 / mainTrieCacheLimit
context = []interface{}{
"warmUpCnt", cnt,
"cacheLimit", units.Base2Bytes(mainTrieCacheLimit).String(),
"cachedSize", units.Base2Bytes(stats.BytesSize).String(),
"percent", percent,
switch c := cache.(type) {
case *statedb.LocalCache:
stats := c.UpdateStats()
percent = stats.BytesSize * 100 / mainTrieCacheLimit
context = []interface{}{
"warmUpCnt", cnt,
"cacheLimit", units.Base2Bytes(mainTrieCacheLimit).String(),
"cachedSize", units.Base2Bytes(stats.BytesSize).String(),
"percent", percent,
}
default:
context = []interface{}{
"warmUpCnt", cnt,
"cacheLimit", units.Base2Bytes(mainTrieCacheLimit).String(),
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions storage/statedb/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package statedb

// Cache interface the cache of stateDB
type Cache interface {
Set(k, v []byte)
Get(k []byte) []byte
Has(k []byte) ([]byte, bool)
}
32 changes: 32 additions & 0 deletions storage/statedb/cache_fastcache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package statedb

import "github.com/VictoriaMetrics/fastcache"

type LocalCache struct {
cache *fastcache.Cache
}

func NewLocalCache(maxBytes int) Cache {
return &LocalCache{
cache: fastcache.New(maxBytes),
}
}

func (l *LocalCache) Get(k []byte) []byte {
return l.cache.Get(nil, k)
}

func (l *LocalCache) Set(k, v []byte) {
l.cache.Set(k, v)
}

func (l *LocalCache) Has(k []byte) ([]byte, bool) {
return l.cache.HasGet(nil, k)
}

func (l *LocalCache) UpdateStats() fastcache.Stats {
var stats fastcache.Stats
l.cache.UpdateStats(&stats)

return stats
}
53 changes: 28 additions & 25 deletions storage/statedb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ package statedb

import (
"fmt"
"github.com/VictoriaMetrics/fastcache"
"io"
"sync"
"time"

"github.com/klaytn/klaytn/common"
"github.com/klaytn/klaytn/log"
"github.com/klaytn/klaytn/ser/rlp"
"github.com/klaytn/klaytn/storage/database"
"github.com/pbnjay/memory"
"github.com/rcrowley/go-metrics"
"io"
"sync"
"time"
)

var (
Expand Down Expand Up @@ -124,8 +124,8 @@ type Database struct {

lock sync.RWMutex

trieNodeCache *fastcache.Cache // GC friendly memory cache of trie node RLPs
trieNodeCacheLimit int // byte size of trieNodeCache
trieNodeCache Cache // GC friendly memory cache of trie node RLPs
trieNodeCacheLimit int // byte size of trieNodeCache
}

// rawNode is a simple binary blob used to differentiate between collapsed trie
Expand Down Expand Up @@ -318,15 +318,15 @@ func NewDatabase(diskDB database.DBManager) *Database {
// before its written out to disk or garbage collected. It also acts as a read cache
// for nodes loaded from disk.
func NewDatabaseWithNewCache(diskDB database.DBManager, cacheSizeMB int) *Database {
var trieNodeCache *fastcache.Cache
var trieNodeCache Cache
var cacheSizeByte int

if cacheSizeMB == AutoScaling {
cacheSizeMB = getTrieNodeCacheSizeMB()
}
if cacheSizeMB > 0 {
cacheSizeByte = cacheSizeMB * 1024 * 1024
trieNodeCache = fastcache.New(cacheSizeByte)
trieNodeCache = NewLocalCache(cacheSizeByte)

logger.Info("Initialize trie node cache with fastcache", "MaxMB", cacheSizeMB)
}
Expand All @@ -342,7 +342,7 @@ func NewDatabaseWithNewCache(diskDB database.DBManager, cacheSizeMB int) *Databa
// NewDatabaseWithExistingCache creates a new trie database to store ephemeral trie content
// before its written out to disk or garbage collected. It also acts as a read cache
// for nodes loaded from disk.
func NewDatabaseWithExistingCache(diskDB database.DBManager, cache *fastcache.Cache) *Database {
func NewDatabaseWithExistingCache(diskDB database.DBManager, cache Cache) *Database {
return &Database{
diskDB: diskDB,
nodes: map[common.Hash]*cachedNode{{}: {}},
Expand Down Expand Up @@ -374,7 +374,7 @@ func (db *Database) DiskDB() database.DBManager {
}

// TrieNodeCache retrieves the trieNodeCache of the trie database.
func (db *Database) TrieNodeCache() *fastcache.Cache {
func (db *Database) TrieNodeCache() Cache {
return db.trieNodeCache
}

Expand Down Expand Up @@ -493,7 +493,7 @@ func (db *Database) insertPreimage(hash common.Hash, preimage []byte) {
// getCachedNode finds an encoded node in the trie node cache if enabled.
func (db *Database) getCachedNode(hash common.Hash) []byte {
if db.trieNodeCache != nil {
if enc := db.trieNodeCache.Get(nil, hash[:]); enc != nil {
if enc := db.trieNodeCache.Get(hash[:]); enc != nil {
memcacheCleanHitMeter.Mark(1)
memcacheCleanReadMeter.Mark(int64(len(enc)))
return enc
Expand Down Expand Up @@ -1120,19 +1120,22 @@ func (db *Database) getLastNodeHashInFlushList() common.Hash {
func (db *Database) UpdateMetricNodes() {
memcacheNodesGauge.Update(int64(len(db.nodes)))
if db.trieNodeCache != nil {
var stats fastcache.Stats
db.trieNodeCache.UpdateStats(&stats)

memcacheFastMisses.Update(int64(stats.Misses))
memcacheFastCollisions.Update(int64(stats.Collisions))
memcacheFastCorruptions.Update(int64(stats.Corruptions))
memcacheFastEntriesCount.Update(int64(stats.EntriesCount))
memcacheFastBytesSize.Update(int64(stats.BytesSize))
memcacheFastGetBigCalls.Update(int64(stats.GetBigCalls))
memcacheFastSetBigCalls.Update(int64(stats.SetBigCalls))
memcacheFastTooBigKeyErrors.Update(int64(stats.TooBigKeyErrors))
memcacheFastInvalidMetavalueErrors.Update(int64(stats.InvalidMetavalueErrors))
memcacheFastInvalidValueLenErrors.Update(int64(stats.InvalidValueLenErrors))
memcacheFastInvalidValueHashErrors.Update(int64(stats.InvalidValueHashErrors))
switch c := db.trieNodeCache.(type) {
case *LocalCache:
stats := c.UpdateStats()

memcacheFastMisses.Update(int64(stats.Misses))
memcacheFastCollisions.Update(int64(stats.Collisions))
memcacheFastCorruptions.Update(int64(stats.Corruptions))
memcacheFastEntriesCount.Update(int64(stats.EntriesCount))
memcacheFastBytesSize.Update(int64(stats.BytesSize))
memcacheFastGetBigCalls.Update(int64(stats.GetBigCalls))
memcacheFastSetBigCalls.Update(int64(stats.SetBigCalls))
memcacheFastTooBigKeyErrors.Update(int64(stats.TooBigKeyErrors))
memcacheFastInvalidMetavalueErrors.Update(int64(stats.InvalidMetavalueErrors))
memcacheFastInvalidValueLenErrors.Update(int64(stats.InvalidValueLenErrors))
memcacheFastInvalidValueHashErrors.Update(int64(stats.InvalidValueHashErrors))
default:
}
}
}
5 changes: 3 additions & 2 deletions storage/statedb/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
package statedb

import (
"testing"

"github.com/klaytn/klaytn/common"
"github.com/klaytn/klaytn/storage/database"
"github.com/stretchr/testify/assert"
"testing"
)

var childHash = common.HexToHash("1341655") // 20190805 in hexadecimal
Expand Down Expand Up @@ -131,7 +132,7 @@ func TestCache(t *testing.T) {
for i := 0; i < 100; i++ {
key, value := common.MakeRandomBytes(256), common.MakeRandomBytes(63*1024) // fastcache can store entrie under 64KB
db.trieNodeCache.Set(key, value)
rValue, found := db.trieNodeCache.HasGet(nil, key)
rValue, found := db.trieNodeCache.Has(key)

assert.Equal(t, true, found)
assert.Equal(t, value, rValue)
Expand Down

0 comments on commit f99b4a2

Please sign in to comment.