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

Interfacing stateDB cache #646

Merged
merged 2 commits into from
Sep 1, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about changing this file to localcache.go?


import "github.com/VictoriaMetrics/fastcache"

type LocalCache struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name LocalCache suggests that there is a RemoteCache struct, but there isn't.
Also, LocalCache sounds like an interface, not a struct.
How about just wrapping fastcache.cache as statedb.FastCache?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. RemoteCache will be introduced later soon.
  2. When fastcache.cache is wrapped to statedb.FastCache, type assertion is needed in every method of statedb.FastCache to use methods of fastcache.cache. In that case, newly introduced interfaces should returns an error also.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am gonna try to find a new name replacing LocalCache later. If you have any idea, please let me know.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant for "wrapping fastcache.cache as statedb.FastCache" is to change the name of current LocalCache to FastCache, not changing the current interfaces. Let's proceed as what you intended and check if additional change is needed later. I think it is good for now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I will consider your comment in the next PR.

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