diff --git a/core/blockchain.go b/core/blockchain.go index ea3fdedef3..bc836510d8 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -207,7 +207,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par return nil, err } - bc.loadStateCache() // Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain //for hash := range BadHashes { // if header := bc.GetHeaderByHash(hash); header != nil { @@ -294,31 +293,6 @@ func (bc *BlockChain) loadLastState() error { return nil } -func (bc *BlockChain) loadStateCache() { - go func() { - log.Debug("Start load state cache") - t := time.Now() - tr, err := bc.stateCache.OpenTrie(bc.CurrentBlock().Root()) - if err != nil { - log.Error("Failed to open trie", "err", err) - return - } - - limit := (uint64(bc.cacheConfig.TrieDBCache*1024*1024) / 8) - 1024 - c := 0 - it := tr.NodeIterator(nil) - for it.Next(true) { - c++ - - if bc.stateCache.TrieDB().CacheCapacity() >= limit || time.Since(t) >= 30*time.Second { - break - } - } - bc.stateCache.TrieDB().ResetCacheStats() - log.Debug("Load state cache", "count", c, "limit", limit, "duration", time.Since(t)) - }() -} - // SetHead rewinds the local chain to a new head. In the case of headers, everything // above the new head will be deleted and the new one set. In the case of blocks // though, the head may be further rewound if block bodies are missing (non-archive diff --git a/go.mod b/go.mod index 77a80c9e78..ae588cc8c6 100644 --- a/go.mod +++ b/go.mod @@ -7,13 +7,14 @@ require ( github.com/Azure/azure-storage-blob-go v0.0.0-20180712005634-eaae161d9d5e github.com/PlatONnetwork/wagon v0.6.1-0.20200422074910-d2bccc71e673 github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect + github.com/VictoriaMetrics/fastcache v1.5.7 github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847 github.com/btcsuite/btcd v0.20.1-beta github.com/btcsuite/btcutil v1.0.2 github.com/cespare/cp v0.1.0 github.com/cespare/xxhash v1.1.0 github.com/davecgh/go-spew v1.1.1 - github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea + github.com/deckarep/golang-set v1.7.1 github.com/docker/docker v17.12.0-ce-rc1.0.20180625184442-8e610b2b55bf+incompatible github.com/edsrzf/mmap-go v1.0.1-0.20190108065903-904c4ced31cd // indirect github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa @@ -26,7 +27,7 @@ require ( github.com/golang/protobuf v1.2.1-0.20181128192352-1d3f30b51784 github.com/golang/snappy v0.0.1 github.com/google/go-cmp v0.4.0 // indirect - github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad + github.com/hashicorp/golang-lru v0.5.4 github.com/herumi/bls v0.0.0-20200517120024-accfc25c06c0 github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3 github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883 @@ -57,8 +58,8 @@ require ( github.com/stretchr/testify v1.4.0 github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d - golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 - golang.org/x/sys v0.0.0-20190412213103-97732733099d + golang.org/x/net v0.0.0-20200602114024-627f9648deb9 + golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984 // indirect golang.org/x/tools v0.0.0-20170215214335-be0fcc31ae23 gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405 diff --git a/trie/bigcache.go b/trie/bigcache.go deleted file mode 100644 index 36b18d99d2..0000000000 --- a/trie/bigcache.go +++ /dev/null @@ -1,300 +0,0 @@ -package trie - -import ( - "container/list" - "errors" - "sync" - "sync/atomic" -) - -const ( - // offset64 FNVa offset basis. See https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function#FNV-1a_hash - offset64 = 14695981039346656037 - // prime64 FNVa prime value. See https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function#FNV-1a_hash - prime64 = 1099511628211 - - // maxValueSize The maximum size for entry value. - maxValueSize = 1024 -) - -// Sum64 gets the string and returns its uint64 hash value. -func Sum64(key string) uint64 { - var hash uint64 = offset64 - for i := 0; i < len(key); i++ { - hash ^= uint64(key[i]) - hash *= prime64 - } - - return hash -} - -// LRU implements a non-thread safe fixed size LRU cache -type LRU struct { - capacity uint64 - maxCapacity uint64 - evictList *list.List - items map[string]*list.Element -} - -// entry is used to hold a value in the evictList -type entry struct { - key string - value []byte -} - -// NewLRU constructs an LRU of the given size -func NewLRU(size uint64) (*LRU, error) { - if size <= 0 { - return nil, errors.New("Must provide a positive size") - } - c := &LRU{ - capacity: 0, - maxCapacity: size, - evictList: list.New(), - items: make(map[string]*list.Element), - } - return c, nil -} - -// Add adds a value to the cache. Returns true if an eviction occurred. -func (c *LRU) Add(key string, value []byte) bool { - // Check for existing item - if ent, ok := c.items[key]; ok { - c.evictList.MoveToFront(ent) - c.capacity -= uint64(len(ent.Value.(*entry).value)) - ent.Value.(*entry).value = value - c.capacity += uint64(len(value)) - return false - } - - // Add new item - ent := &entry{key, value} - entry := c.evictList.PushFront(ent) - c.items[key] = entry - c.capacity += uint64(len(value)) - c.capacity += uint64(len(key)) - - evict := c.capacity > c.maxCapacity - // Verify size not exceeded - if evict { - c.removeOldest() - } - return evict -} - -// Get looks up a key's value from the cache. -func (c *LRU) Get(key string) (value []byte, ok bool) { - if ent, ok := c.items[key]; ok { - c.evictList.MoveToFront(ent) - return ent.Value.(*entry).value, true - } - return -} - -// Remove removes the provided key from the cache, returning if the -// key was contained. -func (c *LRU) Remove(key string) bool { - if ent, ok := c.items[key]; ok { - c.removeElement(ent) - return true - } - return false -} - -// Len returns the number of items in the cache. -func (c *LRU) Len() int { - return c.evictList.Len() -} - -func (c *LRU) Capacity() uint64 { - return c.capacity -} - -// removeOldest removes the oldest item from the cache. -func (c *LRU) removeOldest() { - for c.capacity > c.maxCapacity { - ent := c.evictList.Back() - if ent != nil { - c.removeElement(ent) - } - } -} - -// removeElement is used to remove a given list element from the cache -func (c *LRU) removeElement(e *list.Element) { - c.evictList.Remove(e) - kv := e.Value.(*entry) - delete(c.items, kv.key) - c.capacity -= uint64(len(kv.value)) - c.capacity -= uint64(len(kv.key)) -} - -type Stats struct { - Hits int64 // Hits is a number of successfully found keys - Misses int64 // Misses is a number of not found keys - DelHits int64 // DelHits is a number of successfully deleted keys - DelMisses int64 // DelMisses is a number of not deleted keys -} - -type BigCache struct { - shards []*shard - shardMask uint64 - lock sync.Mutex - lru *LRU - - hits int64 -} - -type shard struct { - entries *LRU - lock sync.Mutex - stats Stats -} - -func NewBigCache(capacity uint64, size int) *BigCache { - shards := make([]*shard, size) - shardCap := (7 * capacity / 8) / uint64(size) - for i := 0; i < size; i++ { - shards[i] = newShard(shardCap) - } - lru, _ := NewLRU(capacity / 8) - - return &BigCache{ - shards: shards, - shardMask: uint64(size) - 1, - lru: lru, - } -} - -func (c *BigCache) Set(key string, value []byte) { - if len(value) > maxValueSize { - return - } - - hash := Sum64(key) - shard := c.getShard(hash) - shard.set(key, value) -} - -func (c *BigCache) SetLru(key string, value []byte) { - if len(value) > maxValueSize { - return - } - - c.lock.Lock() - defer c.lock.Unlock() - c.lru.Add(key, value) -} - -func (c *BigCache) Get(key string) ([]byte, bool) { - c.lock.Lock() - if val, ok := c.lru.Get(key); ok { - atomic.AddInt64(&c.hits, 1) - c.lock.Unlock() - return val, true - } - c.lock.Unlock() - - hash := Sum64(key) - shard := c.getShard(hash) - return shard.get(key) -} - -func (c *BigCache) Delele(key string) { - hash := Sum64(key) - shard := c.getShard(hash) - shard.delete(key) -} - -func (c *BigCache) Len() uint64 { - len := c.lru.Len() - for _, shard := range c.shards { - len += shard.entries.Len() - } - return uint64(len) -} - -func (c *BigCache) Capacity() uint64 { - var cap uint64 = 0 - c.lock.Lock() - cap = c.lru.Capacity() - c.lock.Unlock() - - for _, shard := range c.shards { - cap += shard.entries.Capacity() - } - return cap -} - -func (c *BigCache) Stats() Stats { - var stats Stats - stats.Hits = atomic.LoadInt64(&c.hits) - for _, shard := range c.shards { - s := shard.Stats() - stats.Hits += s.Hits - stats.Misses += s.Misses - stats.DelHits += s.DelHits - stats.DelMisses += s.DelMisses - } - return stats -} - -func (c *BigCache) ResetStats() { - c.lock.Lock() - c.hits = 0 - c.lock.Unlock() - - for _, shard := range c.shards { - shard.resetStats() - } -} - -func (c *BigCache) getShard(hash uint64) *shard { - return c.shards[c.shardMask&hash] -} - -func newShard(capacity uint64) *shard { - entries, _ := NewLRU(capacity) - return &shard{ - entries: entries, - } -} - -func (s *shard) set(key string, value []byte) { - s.lock.Lock() - defer s.lock.Unlock() - s.entries.Add(key, value) -} - -func (s *shard) get(key string) ([]byte, bool) { - s.lock.Lock() - defer s.lock.Unlock() - if val, ok := s.entries.Get(key); ok { - s.stats.Hits++ - return val, ok - } - s.stats.Misses++ - return nil, false -} - -func (s *shard) delete(key string) { - s.lock.Lock() - defer s.lock.Unlock() - if s.entries.Remove(key) { - s.stats.DelHits++ - return - } - s.stats.DelMisses++ -} - -func (s *shard) Stats() Stats { - s.lock.Lock() - defer s.lock.Unlock() - return s.stats -} - -func (s *shard) resetStats() { - s.lock.Lock() - defer s.lock.Unlock() - s.stats = Stats{} -} diff --git a/trie/bigcache_test.go b/trie/bigcache_test.go deleted file mode 100644 index 8c751f2a0f..0000000000 --- a/trie/bigcache_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package trie - -import ( - "fmt" - "runtime" - "sync" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestBigCache(t *testing.T) { - bc := NewBigCache(1000000, 6) - assert.NotNil(t, bc) - - assert.True(t, bc.getShard(0) == bc.shards[0]) - assert.True(t, bc.getShard(5) == bc.shards[5]) - - bc.Set("test", []byte("111111")) - v, ok := bc.Get("test") - assert.Equal(t, v, []byte("111111")) - assert.True(t, ok) - - assert.True(t, bc.Len() == 1) - assert.True(t, bc.Capacity() == 10) - - stats := bc.Stats() - assert.True(t, stats.Hits == 1) - assert.True(t, stats.Misses == 0) - - bc.Delele("test") - _, ok = bc.Get("test") - assert.False(t, ok) - - stats = bc.Stats() - assert.True(t, stats.DelHits == 1) - assert.True(t, stats.Misses == 1) - - bc.SetLru("test", []byte("111111")) - v, ok = bc.Get("test") - assert.True(t, ok) - assert.Equal(t, v, []byte("111111")) - - _, ok = bc.Get("11") - assert.False(t, ok) - - val := make([]byte, 1024) - bc.Set("key1024", val) - v, ok = bc.Get("key1024") - assert.True(t, ok) - assert.Equal(t, v, val) - - val = make([]byte, 1025) - bc.Set("key1025", val) - _, ok = bc.Get("key1025") - assert.False(t, ok) - - bc.SetLru("lru1025", val) - _, ok = bc.Get("lru1025") - assert.False(t, ok) - - bc.SetLru("test", []byte("123")) - v, _ = bc.Get("test") - assert.Equal(t, v, []byte("123")) - - bc = NewBigCache(20, 1) - - bc.Set("test", []byte("111111")) - v, _ = bc.Get("test") - assert.Equal(t, v, []byte("111111")) - bc.Set("test1", []byte("123")) - _, ok = bc.Get("test") - assert.False(t, ok) - _, ok = bc.Get("test1") - assert.True(t, ok) - -} - -func TestBigCacheConcurrency(t *testing.T) { - bc := NewBigCache(32, 10) - - threads := runtime.NumCPU() - var wg sync.WaitGroup - wg.Add(threads) - for i := 0; i < threads; i++ { - go func() { - for j := 0; j < 100; j++ { - bc.SetLru(fmt.Sprintf("%d", j), []byte(fmt.Sprintf("%d", j))) - bc.Set(fmt.Sprintf("%d", j+1), []byte(fmt.Sprintf("%d", j+1))) - bc.Get(fmt.Sprintf("%d", j-1)) - bc.Get(fmt.Sprintf("%d", j)) - } - wg.Done() - }() - } - wg.Wait() -} diff --git a/trie/database.go b/trie/database.go index 9f665832fd..4f59fb86f2 100644 --- a/trie/database.go +++ b/trie/database.go @@ -22,6 +22,8 @@ import ( "sync" "time" + "github.com/VictoriaMetrics/fastcache" + "github.com/PlatONnetwork/PlatON-Go/common" "github.com/PlatONnetwork/PlatON-Go/ethdb" "github.com/PlatONnetwork/PlatON-Go/log" @@ -68,7 +70,7 @@ type Database struct { nodes map[common.Hash]*cachedNode // Data and references relationships of a node oldest common.Hash // Oldest tracked node, flush-list head newest common.Hash // Newest tracked node, flush-list tail - cleans *BigCache + cleans *fastcache.Cache useless []map[string]struct{} preimages map[common.Hash][]byte // Preimages of nodes from the secure trie @@ -281,9 +283,9 @@ func NewDatabase(diskdb ethdb.Database) *Database { } func NewDatabaseWithCache(diskdb ethdb.Database, cache int) *Database { - var cleans *BigCache + var cleans *fastcache.Cache if cache > 0 { - cleans = NewBigCache(uint64(cache)*1024*1024, 1024) + cleans = fastcache.New(cache * 1024 * 1024) } return &Database{ diskdb: diskdb, @@ -372,7 +374,7 @@ func (db *Database) node(hash common.Hash, cachegen uint16) node { } if db.cleans != nil { - if enc, ok := db.cleans.Get(string(hash[:])); ok { + if enc := db.cleans.Get(nil, hash[:]); enc != nil { return mustDecodeNode(hash[:], enc, cachegen) } } @@ -383,7 +385,7 @@ func (db *Database) node(hash common.Hash, cachegen uint16) node { return nil } if db.cleans != nil { - db.cleans.SetLru(string(hash[:]), enc) + db.cleans.Set(hash[:], enc) } return mustDecodeNode(hash[:], enc, cachegen) } @@ -402,7 +404,7 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) { } if db.cleans != nil { - if enc, ok := db.cleans.Get(string(hash[:])); ok { + if enc := db.cleans.Get(nil, hash[:]); enc != nil { return enc, nil } } @@ -413,7 +415,7 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) { return nil, err } if db.cleans != nil { - db.cleans.SetLru(string(hash[:]), val) + db.cleans.Set(hash[:], val) } return val, nil } @@ -502,7 +504,7 @@ func (db *Database) DereferenceDB(root common.Hash) { clearFn := func(hash []byte) { useless[string(hash)] = struct{}{} if db.cleans != nil { - db.cleans.Delele(string(hash[:])) + db.cleans.Del(hash[:]) } } db.dereference(root, common.Hash{}, clearFn) @@ -586,7 +588,7 @@ func (db *Database) Dereference(root common.Hash) { cleanFn := func(hash []byte) { if db.cleans != nil { - db.cleans.Delele(string(hash)) + db.cleans.Del(hash) } } @@ -861,16 +863,6 @@ func (db *Database) Commit(node common.Hash, report bool, uncache bool) error { if !report { logger = log.Debug } - if db.cleans != nil { - stats := db.cleans.Stats() - rate := 0.0 - if stats.Hits > 0 { - rate = float64(stats.Hits) / float64(stats.Hits+stats.Misses) - } - logger("Clean stats", "len", db.cleans.Len(), "capacity", db.cleans.Capacity(), - "hits", stats.Hits, "misses", stats.Misses, "delHits", stats.DelHits, - "delMisses", stats.DelMisses, "rate", rate) - } logger("Persisted trie from memory database", "nodes", nodes-len(db.nodes)+int(db.flushnodes), "size", storage-db.nodesSize+db.flushsize, "time", time.Since(start)+db.flushtime, "gcnodes", db.gcnodes, "gcsize", db.gcsize, "gctime", db.gctime, "livenodes", len(db.nodes), "livesize", db.nodesSize) @@ -899,7 +891,7 @@ func (db *Database) commit(hash common.Hash, batch ethdb.Batch) error { return err } if db.cleans != nil { - db.cleans.Set(string(hash[:]), enc) + db.cleans.Set(hash[:], enc) } // If we've reached an optimal batch size, commit and start over if batch.ValueSize() >= ethdb.IdealBatchSize { @@ -995,16 +987,3 @@ func (db *Database) accumulate(hash common.Hash, reachable map[common.Hash]struc db.accumulate(child, reachable) } } - -func (db *Database) CacheCapacity() uint64 { - if db.cleans != nil { - return db.cleans.Capacity() - } - return 0 -} - -func (db *Database) ResetCacheStats() { - if db.cleans != nil { - db.cleans.ResetStats() - } -}