mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
core, eth, miner, trie: miner optimizations and database migration
This commit is contained in:
parent
c22428ca97
commit
f40e3442a1
11 changed files with 239 additions and 81 deletions
|
|
@ -553,7 +553,7 @@ func (self *BlockChain) GetBlockByNumber(number uint64) *types.Block {
|
||||||
|
|
||||||
// IsCanonChainBlock checks whether the given block is in the current canonical chain.
|
// IsCanonChainBlock checks whether the given block is in the current canonical chain.
|
||||||
func (self *BlockChain) IsCanonChainBlock(number uint64, hash common.Hash) bool {
|
func (self *BlockChain) IsCanonChainBlock(number uint64, hash common.Hash) bool {
|
||||||
return number < uint64(self.currentBlock.Number().Int64()) && GetCanonicalHash(self.chainDb, number) == hash
|
return GetCanonicalHash(self.chainDb, number) == hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// [deprecated by eth/62]
|
// [deprecated by eth/62]
|
||||||
|
|
@ -937,6 +937,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
self.reportBlock(block, nil, err)
|
self.reportBlock(block, nil, err)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
self.stateCache.SetBlockContext(block.Hash(), block.NumberU64(), self)
|
||||||
// Process block using the parent state as reference point.
|
// Process block using the parent state as reference point.
|
||||||
receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, vm.Config{})
|
receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, vm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
|
||||||
if b.gasPool == nil {
|
if b.gasPool == nil {
|
||||||
b.SetCoinbase(common.Address{})
|
b.SetCoinbase(common.Address{})
|
||||||
}
|
}
|
||||||
b.statedb.SetTxContext(common.Hash{}, b.header.Number.Uint64(), tx.Hash(), len(b.txs), nil)
|
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
|
||||||
receipt, _, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
|
receipt, _, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ func TestNodeIteratorCoverage(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, key := range db.(*ethdb.MemDatabase).Keys() {
|
for _, key := range db.(*ethdb.MemDatabase).Keys() {
|
||||||
if bytes.HasPrefix(key, []byte("secure-key-")) || bytes.HasPrefix(key, CachePrefix) {
|
if bytes.HasPrefix(key, []byte("secure-key-")) || bytes.HasPrefix(key, DirectCachePrefix) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, ok := hashes[common.BytesToHash(key)]; !ok {
|
if _, ok := hashes[common.BytesToHash(key)]; !ok {
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ import (
|
||||||
// Trie cache generation limit after which to evic trie nodes from memory.
|
// Trie cache generation limit after which to evic trie nodes from memory.
|
||||||
var MaxTrieCacheGen = uint16(120)
|
var MaxTrieCacheGen = uint16(120)
|
||||||
|
|
||||||
var CachePrefix = []byte("accounthashcache:")
|
var DirectCachePrefix = []byte("accounthashcache:")
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Number of past tries to keep. This value is chosen such that
|
// Number of past tries to keep. This value is chosen such that
|
||||||
|
|
@ -62,6 +62,7 @@ type StateDB struct {
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
trie *trie.Trie
|
trie *trie.Trie
|
||||||
storage *trie.SecureTrie
|
storage *trie.SecureTrie
|
||||||
|
cacheComplete bool // True if we know that the directcache is complete
|
||||||
pastTries []*trie.Trie
|
pastTries []*trie.Trie
|
||||||
codeSizeCache *lru.Cache
|
codeSizeCache *lru.Cache
|
||||||
|
|
||||||
|
|
@ -103,7 +104,7 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
|
||||||
refund: new(big.Int),
|
refund: new(big.Int),
|
||||||
logs: make(map[common.Hash]vm.Logs),
|
logs: make(map[common.Hash]vm.Logs),
|
||||||
}
|
}
|
||||||
ret.SetTxContext(common.Hash{}, 0, common.Hash{}, 0, nil)
|
ret.SetBlockContext(common.Hash{}, 0, nil)
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,7 +127,7 @@ func (self *StateDB) New(root common.Hash) (*StateDB, error) {
|
||||||
refund: new(big.Int),
|
refund: new(big.Int),
|
||||||
logs: make(map[common.Hash]vm.Logs),
|
logs: make(map[common.Hash]vm.Logs),
|
||||||
}
|
}
|
||||||
ret.SetTxContext(common.Hash{}, 0, common.Hash{}, 0, nil)
|
ret.SetBlockContext(common.Hash{}, 0, nil)
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,8 +150,8 @@ func (self *StateDB) Reset(root common.Hash) error {
|
||||||
self.logs = make(map[common.Hash]vm.Logs)
|
self.logs = make(map[common.Hash]vm.Logs)
|
||||||
self.logSize = 0
|
self.logSize = 0
|
||||||
self.clearJournalAndRefund()
|
self.clearJournalAndRefund()
|
||||||
self.SetTxContext(common.Hash{}, 0, common.Hash{}, 0, nil)
|
self.SetBlockContext(common.Hash{}, 0, nil)
|
||||||
|
self.SetTxContext(common.Hash{}, 0)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -179,18 +180,28 @@ func (self *StateDB) pushTrie(t *trie.Trie) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) SetTxContext(blockHash common.Hash, blockNum uint64, txHash common.Hash, txIndex int, validator trie.CacheValidator) {
|
func (self *StateDB) SetBlockContext(blockHash common.Hash, blockNum uint64, validator trie.CacheValidator) {
|
||||||
self.bhash = blockHash
|
self.bhash = blockHash
|
||||||
self.thash = txHash
|
|
||||||
self.txIndex = txIndex
|
|
||||||
|
|
||||||
if validator == nil {
|
if validator == nil {
|
||||||
validator = &trie.NullCacheValidator{}
|
validator = &trie.NullCacheValidator{}
|
||||||
}
|
}
|
||||||
storage := trie.NewDirectCache(self.trie, self.db, CachePrefix, validator, true)
|
|
||||||
|
// Check if cache population has completed
|
||||||
|
if !self.cacheComplete {
|
||||||
|
if _, status := trie.GetMigrationState(DirectCachePrefix, self.db); status == trie.Complete {
|
||||||
|
self.cacheComplete = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storage := trie.NewDirectCache(self.trie, self.db, DirectCachePrefix, blockNum, blockHash, validator, self.cacheComplete)
|
||||||
self.storage = trie.NewSecure(storage, self.db)
|
self.storage = trie.NewSecure(storage, self.db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *StateDB) SetTxContext(txHash common.Hash, txIndex int) {
|
||||||
|
self.thash = txHash
|
||||||
|
self.txIndex = txIndex
|
||||||
|
}
|
||||||
|
|
||||||
func (self *StateDB) AddLog(log *vm.Log) {
|
func (self *StateDB) AddLog(log *vm.Log) {
|
||||||
self.journal = append(self.journal, addLogChange{txhash: self.thash})
|
self.journal = append(self.journal, addLogChange{txhash: self.thash})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,18 +47,23 @@ func NewStateSync(number uint64, hash common.Hash, root common.Hash, database et
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Populate the direct account caches in the database
|
// Populate the direct account caches in the database
|
||||||
|
if err := trie.DirectCacheTransaction(func() error {
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
if err := trie.WriteDirectCache(CachePrefix, key, leaf, number, hash, database); err != nil {
|
if err := trie.WriteDirectCache(DirectCachePrefix, key, leaf, number, hash, database); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// Schedule downloading all the dependencies of the account
|
// Schedule downloading all the dependencies of the account
|
||||||
syncer.AddSubTrie(obj.Root, 64, parent, nil, nil)
|
syncer.AddSubTrie(obj.Root, 64, parent, nil, nil)
|
||||||
syncer.AddRawEntry(common.BytesToHash(obj.CodeHash), 64, parent)
|
syncer.AddRawEntry(common.BytesToHash(obj.CodeHash), 64, parent)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
syncer = trie.NewTrieSync(root, database, callback, CachePrefix)
|
syncer = trie.NewTrieSync(root, database, callback, DirectCachePrefix)
|
||||||
return (*StateSync)(syncer)
|
return (*StateSync)(syncer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
}
|
}
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
statedb.SetTxContext(block.Hash(), block.NumberU64(), tx.Hash(), i, p.bc)
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
|
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
|
|
|
||||||
|
|
@ -255,6 +255,10 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams)
|
gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams)
|
||||||
eth.ApiBackend = &EthApiBackend{eth, gpo}
|
eth.ApiBackend = &EthApiBackend{eth, gpo}
|
||||||
|
|
||||||
|
if err := populateDirectCache(chainDb, eth.blockchain); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return eth, nil
|
return eth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,13 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
var useSequentialKeys = []byte("dbUpgrade_20160530sequentialKeys")
|
var useSequentialKeys = []byte("dbUpgrade_20160530sequentialKeys")
|
||||||
|
|
@ -354,3 +356,36 @@ func addMipmapBloomBins(db ethdb.Database) (err error) {
|
||||||
glog.V(logger.Info).Infoln("upgrade completed in", time.Since(tstart))
|
glog.V(logger.Info).Infoln("upgrade completed in", time.Since(tstart))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func populateDirectCache(db ethdb.Database, bc *core.BlockChain) error {
|
||||||
|
blockHash, status := trie.GetMigrationState(state.DirectCachePrefix, db)
|
||||||
|
if status == trie.NotStarted {
|
||||||
|
blockHash = core.GetHeadBlockHash(db)
|
||||||
|
status = trie.Running
|
||||||
|
trie.SetMigrationState(state.DirectCachePrefix, blockHash, status, db)
|
||||||
|
}
|
||||||
|
blockNum := core.GetBlockNumber(db, blockHash)
|
||||||
|
|
||||||
|
if status == trie.Running {
|
||||||
|
tr, err := trie.New(core.GetBlock(db, blockHash, blockNum).Root(), db, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dc := trie.NewDirectCache(tr, db, state.DirectCachePrefix, blockNum, blockHash, bc, false)
|
||||||
|
go func() {
|
||||||
|
for core.GetBlockNumber(db, core.GetHeadBlockHash(db)) < blockNum+8 {
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
}
|
||||||
|
glog.Infof("Beginning direct cache upgrade at block %v", blockNum)
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
if err := dc.Populate(); err != nil {
|
||||||
|
glog.Errorf("Direct cache upgrade failed: %v", err)
|
||||||
|
} else {
|
||||||
|
glog.Infof("Direct cache upgrade finished in %v", time.Since(start))
|
||||||
|
}
|
||||||
|
trie.SetMigrationState(state.DirectCachePrefix, blockHash, trie.Complete, db)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -617,7 +617,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Start executing the transaction
|
// Start executing the transaction
|
||||||
env.state.SetTxContext(common.Hash{}, env.header.Number.Uint64(), tx.Hash(), env.tcount, bc)
|
env.state.SetTxContext(tx.Hash(), env.tcount)
|
||||||
|
|
||||||
err, logs := env.commitTransaction(tx, bc, gp)
|
err, logs := env.commitTransaction(tx, bc, gp)
|
||||||
switch {
|
switch {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,10 @@
|
||||||
package trie
|
package trie
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
|
@ -26,36 +29,24 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var directCacheWrites = metrics.NewCounter("directcache/writes")
|
var (
|
||||||
var directCacheHitTimer = metrics.NewTimer("directcache/timer/hits")
|
directCacheLock = &sync.Mutex{}
|
||||||
var directCacheMissTimer = metrics.NewTimer("directcache/timer/misses")
|
directCacheLocked = false
|
||||||
|
directCacheWrites = metrics.NewCounter("directcache/writes")
|
||||||
|
directCacheHits = metrics.NewCounter("directcache/hits")
|
||||||
|
directCacheMisses = metrics.NewCounter("directcache/misses")
|
||||||
|
directCacheTimer = metrics.NewTimer("directcache/timer")
|
||||||
|
NotFound = errors.New("Cache entry not found")
|
||||||
|
MigrationPrefix = []byte("directstatecachemigration:")
|
||||||
|
)
|
||||||
|
|
||||||
// DirectCacheReads retrieves a global counter measuring the number of direct
|
type MigrationStatus int
|
||||||
// cache reads from the disk since process startup. This isn't useful for anything
|
|
||||||
// apart from trie debugging purposes.
|
|
||||||
func DirectCacheReads() int64 {
|
|
||||||
return directCacheHitTimer.Count() + directCacheMissTimer.Count()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirectCacheWrites retrieves a global counter measuring the number of direct
|
const (
|
||||||
// cache writes from the disk since process startup. This isn't useful for anything
|
NotStarted = iota
|
||||||
// apart from trie debugging purposes.
|
Running = iota
|
||||||
func DirectCacheWrites() int64 {
|
Complete = iota
|
||||||
return directCacheWrites.Count()
|
)
|
||||||
}
|
|
||||||
|
|
||||||
// DirectCacheMisses retrieves a global counter measuring the number of direct
|
|
||||||
// cache writes from the disk since process startup. This isn't useful for anything
|
|
||||||
// apart from trie debugging purposes.
|
|
||||||
func DirectCacheMisses() int64 {
|
|
||||||
return directCacheMissTimer.Count()
|
|
||||||
}
|
|
||||||
|
|
||||||
type cachedValue struct {
|
|
||||||
Value []byte
|
|
||||||
BlockNum uint64
|
|
||||||
BlockHash common.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// CacheValidator can check whether a certain block is in the current canonical chain.
|
// CacheValidator can check whether a certain block is in the current canonical chain.
|
||||||
type CacheValidator interface {
|
type CacheValidator interface {
|
||||||
|
|
@ -79,11 +70,13 @@ func (cv *NullCacheValidator) IsCanonChainBlock(num uint64, hash common.Hash) bo
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDirectCache(pm PersistentMap, db Database, keyPrefix []byte, validator CacheValidator, complete bool) *DirectCache {
|
func NewDirectCache(pm PersistentMap, db Database, keyPrefix []byte, blockNum uint64, blockHash common.Hash, validator CacheValidator, complete bool) *DirectCache {
|
||||||
return &DirectCache{
|
return &DirectCache{
|
||||||
data: pm,
|
data: pm,
|
||||||
db: db,
|
db: db,
|
||||||
keyPrefix: keyPrefix,
|
keyPrefix: keyPrefix,
|
||||||
|
blockNum: blockNum,
|
||||||
|
blockHash: blockHash,
|
||||||
validator: validator,
|
validator: validator,
|
||||||
complete: complete,
|
complete: complete,
|
||||||
dirty: make(map[string]bool),
|
dirty: make(map[string]bool),
|
||||||
|
|
@ -95,10 +88,6 @@ func (dc *DirectCache) Iterator() *Iterator {
|
||||||
return dc.data.Iterator()
|
return dc.data.Iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *DirectCache) makeKey(key []byte) []byte {
|
|
||||||
return append(dc.keyPrefix, key...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *DirectCache) Get(key []byte) []byte {
|
func (dc *DirectCache) Get(key []byte) []byte {
|
||||||
res, err := dc.TryGet(key)
|
res, err := dc.TryGet(key)
|
||||||
if err != nil && glog.V(logger.Error) {
|
if err != nil && glog.V(logger.Error) {
|
||||||
|
|
@ -108,15 +97,11 @@ func (dc *DirectCache) Get(key []byte) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *DirectCache) TryGet(key []byte) ([]byte, error) {
|
func (dc *DirectCache) TryGet(key []byte) ([]byte, error) {
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
dirty := dc.dirty[string(key)]
|
dirty := dc.dirty[string(key)]
|
||||||
|
|
||||||
// Use the underlying object for dirty keys
|
// Use the underlying object for dirty keys
|
||||||
if !dirty {
|
if !dirty {
|
||||||
cacheKey := dc.makeKey(key)
|
if cached, ok := dc.getCached(key); ok {
|
||||||
if cached, ok := dc.getCached(cacheKey); ok {
|
|
||||||
directCacheHitTimer.UpdateSince(start)
|
|
||||||
return cached, nil
|
return cached, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -133,26 +118,24 @@ func (dc *DirectCache) TryGet(key []byte) ([]byte, error) {
|
||||||
|
|
||||||
// Don't count fetches of dirty data as cache misses
|
// Don't count fetches of dirty data as cache misses
|
||||||
if !dirty {
|
if !dirty {
|
||||||
directCacheMissTimer.UpdateSince(start)
|
directCacheMisses.Inc(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *DirectCache) getCached(key []byte) ([]byte, bool) {
|
func (dc *DirectCache) getCached(key []byte) ([]byte, bool) {
|
||||||
enc, _ := dc.db.Get(key)
|
data, blockNum, blockHash, err := GetDirectCache(dc.keyPrefix, key, dc.db)
|
||||||
if len(enc) == 0 {
|
if err != nil {
|
||||||
|
if err == NotFound {
|
||||||
return nil, dc.complete
|
return nil, dc.complete
|
||||||
}
|
}
|
||||||
|
glog.Errorf("Error retrieving direct cache data: %v", err)
|
||||||
var data cachedValue
|
|
||||||
if err := rlp.DecodeBytes(enc, &data); err != nil && glog.V(logger.Error) {
|
|
||||||
glog.Errorf("Can't decode cached object at %x: %v", key, err)
|
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
canonical := dc.validator.IsCanonChainBlock(data.BlockNum, data.BlockHash)
|
canonical := dc.blockNum > 0 && blockNum < dc.blockNum && dc.validator.IsCanonChainBlock(blockNum, blockHash)
|
||||||
return data.Value, canonical
|
return data, canonical
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *DirectCache) Update(key, value []byte) {
|
func (dc *DirectCache) Update(key, value []byte) {
|
||||||
|
|
@ -178,21 +161,68 @@ func (dc *DirectCache) TryDelete(key []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *DirectCache) CommitTo(dbw DatabaseWriter) (root common.Hash, err error) {
|
func (dc *DirectCache) CommitTo(dbw DatabaseWriter) (root common.Hash, err error) {
|
||||||
|
if err := DirectCacheTransaction(func() error {
|
||||||
for k, _ := range dc.dirty {
|
for k, _ := range dc.dirty {
|
||||||
v, err := dc.data.TryGet([]byte(k))
|
v, err := dc.data.TryGet([]byte(k))
|
||||||
if err, ok := err.(*MissingNodeError); err != nil && !ok {
|
if _, ok := err.(*MissingNodeError); err != nil && !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := WriteDirectCache(dc.keyPrefix, []byte(k), v, dc.blockNum, dc.blockHash, dbw); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
if err := dc.putCache(dbw, []byte(k), v); err != nil {
|
|
||||||
return common.Hash{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dc.dirty = make(map[string]bool)
|
dc.dirty = make(map[string]bool)
|
||||||
return dc.data.CommitTo(dbw)
|
return dc.data.CommitTo(dbw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *DirectCache) putCache(dbw DatabaseWriter, key, value []byte) error {
|
// Populate iterates over the underlying trie, filling in any unset cache entries.
|
||||||
return WriteDirectCache(dc.keyPrefix, key, value, dc.blockNum, dc.blockHash, dbw)
|
// After Populate has completed, future DirectCache instances can have `complete`
|
||||||
|
// set to true, for better efficiency on cache misses.
|
||||||
|
func (dc *DirectCache) Populate() (err error) {
|
||||||
|
i := 0
|
||||||
|
writes := 0
|
||||||
|
it := dc.Iterator()
|
||||||
|
for it.Next() {
|
||||||
|
if err := DirectCacheTransaction(func() error {
|
||||||
|
if !HasDirectCache(dc.keyPrefix, it.Key, dc.db) {
|
||||||
|
writes += 1
|
||||||
|
if err = WriteDirectCache(dc.keyPrefix, it.Key, it.Value, dc.blockNum, dc.blockHash, dc.db); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
if i % 10000 == 0 && glog.V(logger.Info) {
|
||||||
|
glog.V(logger.Info).Infof("Constructing direct cache: processed %v entries, written %v", i, writes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type cachedValue struct {
|
||||||
|
Value []byte
|
||||||
|
BlockNum uint64
|
||||||
|
BlockHash common.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
func DirectCacheTransaction(tx func() (error)) error {
|
||||||
|
directCacheLock.Lock()
|
||||||
|
directCacheLocked = true
|
||||||
|
defer func() {
|
||||||
|
directCacheLocked = false
|
||||||
|
directCacheLock.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return tx()
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteDirectCache places a value node directly into the database along with
|
// WriteDirectCache places a value node directly into the database along with
|
||||||
|
|
@ -202,6 +232,11 @@ func (dc *DirectCache) putCache(dbw DatabaseWriter, key, value []byte) error {
|
||||||
// and its integrated cache, namely during fast sync and database upgrades.
|
// and its integrated cache, namely during fast sync and database upgrades.
|
||||||
func WriteDirectCache(prefix, key, value []byte, number uint64, hash common.Hash, dbw DatabaseWriter) error {
|
func WriteDirectCache(prefix, key, value []byte, number uint64, hash common.Hash, dbw DatabaseWriter) error {
|
||||||
directCacheWrites.Inc(1)
|
directCacheWrites.Inc(1)
|
||||||
|
|
||||||
|
if !directCacheLocked {
|
||||||
|
return fmt.Errorf("WriteDirectCache may only be called in a DirectCacheTransaction")
|
||||||
|
}
|
||||||
|
|
||||||
enc, _ := rlp.EncodeToBytes(cachedValue{value, number, hash})
|
enc, _ := rlp.EncodeToBytes(cachedValue{value, number, hash})
|
||||||
return dbw.Put(append(prefix, key...), enc)
|
return dbw.Put(append(prefix, key...), enc)
|
||||||
}
|
}
|
||||||
|
|
@ -211,7 +246,74 @@ func WriteDirectCache(prefix, key, value []byte, number uint64, hash common.Hash
|
||||||
//
|
//
|
||||||
// The method is meant to be used by code that circumvents the state database
|
// The method is meant to be used by code that circumvents the state database
|
||||||
// and its integrated cache, namely during fast sync and database upgrades.
|
// and its integrated cache, namely during fast sync and database upgrades.
|
||||||
func GetDirectCache(prefix, key []byte, db Database) ([]byte, error) {
|
func GetDirectCache(prefix, key []byte, db Database) ([]byte, uint64, common.Hash, error) {
|
||||||
defer func(start time.Time) { directCacheHitTimer.UpdateSince(start) }(time.Now())
|
defer func(start time.Time) { directCacheTimer.UpdateSince(start) }(time.Now())
|
||||||
return db.Get(append(prefix, key...))
|
|
||||||
|
enc, _ := db.Get(append(prefix, key...))
|
||||||
|
if len(enc) == 0 {
|
||||||
|
return nil, 0, common.Hash{}, NotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
var data cachedValue
|
||||||
|
if err := rlp.DecodeBytes(enc, &data); err != nil {
|
||||||
|
return nil, 0, common.Hash{}, fmt.Errorf("Can't decode cached object at %x: %v", key, err)
|
||||||
|
}
|
||||||
|
return data.Value, data.BlockNum, data.BlockHash, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasDirectCache returns true iff a direct cache node exists for the specified key
|
||||||
|
func HasDirectCache(prefix, key []byte, db Database) bool {
|
||||||
|
if enc, err := db.Get(append(prefix, key...)); err == nil && len(enc) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type migrationState struct {
|
||||||
|
Status MigrationStatus
|
||||||
|
Hash common.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMigrationState returns the block number of the migration to the direct cache, and
|
||||||
|
// whether or not it's complete.
|
||||||
|
func GetMigrationState(prefix []byte, db Database) (common.Hash, MigrationStatus) {
|
||||||
|
enc, _ := db.Get(append(MigrationPrefix, prefix...))
|
||||||
|
if len(enc) == 0 {
|
||||||
|
return common.Hash{}, NotStarted
|
||||||
|
}
|
||||||
|
|
||||||
|
var data migrationState
|
||||||
|
if err := rlp.DecodeBytes(enc, &data); err != nil {
|
||||||
|
glog.Errorf("Could not decode migration status: %v", err)
|
||||||
|
return common.Hash{}, NotStarted
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.Hash, data.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMigrationState updates the migration state in the database
|
||||||
|
func SetMigrationState(prefix []byte, blockHash common.Hash, status MigrationStatus, db Database) error {
|
||||||
|
enc, _ := rlp.EncodeToBytes(migrationState{status, blockHash})
|
||||||
|
return db.Put(append(MigrationPrefix, prefix...), enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirectCacheReads retrieves a global counter measuring the number of direct
|
||||||
|
// cache reads from the disk since process startup. This isn't useful for anything
|
||||||
|
// apart from trie debugging purposes.
|
||||||
|
func DirectCacheReads() int64 {
|
||||||
|
return directCacheTimer.Count()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirectCacheWrites retrieves a global counter measuring the number of direct
|
||||||
|
// cache writes from the disk since process startup. This isn't useful for anything
|
||||||
|
// apart from trie debugging purposes.
|
||||||
|
func DirectCacheWrites() int64 {
|
||||||
|
return directCacheWrites.Count()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirectCacheMisses retrieves a global counter measuring the number of direct
|
||||||
|
// cache misses from the disk since process startup. This isn't useful for anything
|
||||||
|
// apart from trie debugging purposes.
|
||||||
|
func DirectCacheMisses() int64 {
|
||||||
|
return directCacheMisses.Count()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -298,7 +298,7 @@ func (s *TrieSync) children(req *request, object node) ([]*request, error) {
|
||||||
// If this branch was already visited, abort iteration
|
// If this branch was already visited, abort iteration
|
||||||
keys := req.keys(append(child.path, it.Nibbles...))
|
keys := req.keys(append(child.path, it.Nibbles...))
|
||||||
if !unknown {
|
if !unknown {
|
||||||
if val, err := GetDirectCache(req.dcPrefix, keys[0], s.database); val != nil && err == nil {
|
if HasDirectCache(req.dcPrefix, keys[0], s.database) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Branch is unknown, mark as so to avoid repeated db lookups
|
// Branch is unknown, mark as so to avoid repeated db lookups
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue