Skip to content

Commit c94e07e

Browse files
committed
if statediffing is on, lock tries in triedb until the statediffing service signals they are done using them
1 parent e004e9b commit c94e07e

File tree

6 files changed

+68
-14
lines changed

6 files changed

+68
-14
lines changed

cmd/geth/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
131131
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
132132
}
133133
utils.SetShhConfig(ctx, stack, &cfg.Shh)
134+
if ctx.GlobalBool(utils.StateDiffFlag.Name) {
135+
cfg.Eth.Diffing = true
136+
}
134137

135138
return stack, cfg
136139
}

core/blockchain.go

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import (
4343
"github.com/ethereum/go-ethereum/params"
4444
"github.com/ethereum/go-ethereum/rlp"
4545
"github.com/ethereum/go-ethereum/trie"
46-
lru "github.com/hashicorp/golang-lru"
46+
"github.com/hashicorp/golang-lru"
4747
)
4848

4949
var (
@@ -115,6 +115,7 @@ type CacheConfig struct {
115115
TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk
116116
TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node)
117117
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
118+
StateDiffing bool // Whether or not the statediffing service is running
118119
}
119120

120121
// BlockChain represents the canonical chain given a database with a genesis
@@ -177,6 +178,10 @@ type BlockChain struct {
177178
badBlocks *lru.Cache // Bad block cache
178179
shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block.
179180
terminateInsert func(common.Hash, uint64) bool // Testing hook used to terminate ancient receipt chain insertion.
181+
182+
// Locked roots and their mutex
183+
trieLock sync.Mutex
184+
lockedRoots map[common.Hash]bool
180185
}
181186

182187
// NewBlockChain returns a fully initialised block chain using information
@@ -214,6 +219,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
214219
engine: engine,
215220
vmConfig: vmConfig,
216221
badBlocks: badBlocks,
222+
lockedRoots: make(map[common.Hash]bool),
217223
}
218224
bc.validator = NewBlockValidator(chainConfig, bc, engine)
219225
bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
@@ -857,7 +863,10 @@ func (bc *BlockChain) Stop() {
857863
}
858864
}
859865
for !bc.triegc.Empty() {
860-
triedb.Dereference(bc.triegc.PopItem().(common.Hash))
866+
pruneRoot := bc.triegc.PopItem().(common.Hash)
867+
if !bc.TrieLocked(pruneRoot) {
868+
triedb.Dereference(pruneRoot)
869+
}
861870
}
862871
if size, _ := triedb.Size(); size != 0 {
863872
log.Error("Dangling trie nodes after full cleanup")
@@ -1342,6 +1351,11 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
13421351
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
13431352
bc.triegc.Push(root, -int64(block.NumberU64()))
13441353

1354+
// If we are statediffing, lock the trie until the statediffing service is done using it
1355+
if bc.cacheConfig.StateDiffing {
1356+
bc.LockTrie(root)
1357+
}
1358+
13451359
if current := block.NumberU64(); current > TriesInMemory {
13461360
// If we exceeded our memory allowance, flush matured singleton nodes to disk
13471361
var (
@@ -1380,8 +1394,11 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
13801394
bc.triegc.Push(root, number)
13811395
break
13821396
}
1383-
log.Debug("Dereferencing", "root", root.(common.Hash).Hex())
1384-
triedb.Dereference(root.(common.Hash))
1397+
pruneRoot := root.(common.Hash)
1398+
if !bc.TrieLocked(pruneRoot) {
1399+
log.Debug("Dereferencing", "root", root.(common.Hash).Hex())
1400+
triedb.Dereference(pruneRoot)
1401+
}
13851402
}
13861403
}
13871404
}
@@ -2254,3 +2271,30 @@ func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscript
22542271
func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscription {
22552272
return bc.scope.Track(bc.blockProcFeed.Subscribe(ch))
22562273
}
2274+
2275+
// TrieLocked returns whether the trie associated with the provided root is locked for use
2276+
func (bc *BlockChain) TrieLocked(root common.Hash) bool {
2277+
bc.trieLock.Lock()
2278+
locked, ok := bc.lockedRoots[root]
2279+
bc.trieLock.Unlock()
2280+
if !ok {
2281+
return false
2282+
}
2283+
return locked
2284+
}
2285+
2286+
// LockTrie prevents dereferencing of the provided root
2287+
func (bc *BlockChain) LockTrie(root common.Hash) {
2288+
bc.trieLock.Lock()
2289+
bc.lockedRoots[root] = true
2290+
bc.trieLock.Unlock()
2291+
return
2292+
}
2293+
2294+
// UnlockTrie allows dereferencing of the provided root- provided it was previously locked
2295+
func (bc *BlockChain) UnlockTrie(root common.Hash) {
2296+
bc.trieLock.Lock()
2297+
bc.lockedRoots[root] = false
2298+
bc.trieLock.Unlock()
2299+
return
2300+
}

eth/backend.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
184184
TrieDirtyLimit: config.TrieDirtyCache,
185185
TrieDirtyDisabled: config.NoPruning,
186186
TrieTimeLimit: config.TrieTimeout,
187+
StateDiffing: config.Diffing,
187188
}
188189
)
189190
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve)

eth/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,8 @@ type Config struct {
164164

165165
// MuirGlacier block override (TODO: remove after the fork)
166166
OverrideMuirGlacier *big.Int `toml:",omitempty"`
167+
168+
// Signify whether or not we are producing statediffs
169+
// If we are, do not dereference state roots until the statediffing service is done with them
170+
Diffing bool
167171
}

statediff/builder.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ func (sdb *builder) buildStateTrie(it trie.NodeIterator) ([]StateNode, error) {
128128
return nil, fmt.Errorf("unexpected node type %s", ty)
129129
}
130130
}
131-
return stateNodes, nil
131+
return stateNodes, it.Error()
132132
}
133133

134134
// BuildStateDiffObject builds a statediff object from two blocks and the provided parameters
@@ -301,7 +301,7 @@ func (sdb *builder) createdAndUpdatedState(a, b trie.NodeIterator, watchedAddres
301301
// add both intermediate and leaf node paths to the list of diffPathsAtB
302302
diffPathsAtB[common.Bytes2Hex(nodePath)] = true
303303
}
304-
return diffAcountsAtB, diffPathsAtB, nil
304+
return diffAcountsAtB, diffPathsAtB, it.Error()
305305
}
306306

307307
// createdAndUpdatedStateWithIntermediateNodes returns
@@ -368,7 +368,7 @@ func (sdb *builder) createdAndUpdatedStateWithIntermediateNodes(a, b trie.NodeIt
368368
// add both intermediate and leaf node paths to the list of diffPathsAtB
369369
diffPathsAtB[common.Bytes2Hex(nodePath)] = true
370370
}
371-
return createdOrUpdatedIntermediateNodes, diffAcountsAtB, diffPathsAtB, nil
371+
return createdOrUpdatedIntermediateNodes, diffAcountsAtB, diffPathsAtB, it.Error()
372372
}
373373

374374
// deletedOrUpdatedState returns a slice of all the pathes that are emptied at B
@@ -433,7 +433,7 @@ func (sdb *builder) deletedOrUpdatedState(a, b trie.NodeIterator, diffPathsAtB m
433433
return nil, nil, fmt.Errorf("unexpected node type %s", ty)
434434
}
435435
}
436-
return emptiedPaths, diffAccountAtA, nil
436+
return emptiedPaths, diffAccountAtA, it.Error()
437437
}
438438

439439
// buildAccountUpdates uses the account diffs maps for A => B and B => A and the known intersection of their leafkeys
@@ -559,7 +559,7 @@ func (sdb *builder) buildStorageNodesFromTrie(it trie.NodeIterator, watchedStora
559559
return nil, fmt.Errorf("unexpected node type %s", ty)
560560
}
561561
}
562-
return storageDiffs, nil
562+
return storageDiffs, it.Error()
563563
}
564564

565565
// buildStorageNodesIncremental builds the storage diff node objects for all nodes that exist in a different state at B than A
@@ -641,7 +641,7 @@ func (sdb *builder) createdAndUpdatedStorage(a, b trie.NodeIterator, watchedKeys
641641
}
642642
diffPathsAtB[common.Bytes2Hex(nodePath)] = true
643643
}
644-
return createdOrUpdatedStorage, diffPathsAtB, nil
644+
return createdOrUpdatedStorage, diffPathsAtB, it.Error()
645645
}
646646

647647
func (sdb *builder) deletedOrUpdatedStorage(a, b trie.NodeIterator, diffPathsAtB map[string]bool, watchedKeys []common.Hash, intermediateNodes bool) ([]StorageNode, error) {
@@ -700,7 +700,7 @@ func (sdb *builder) deletedOrUpdatedStorage(a, b trie.NodeIterator, diffPathsAtB
700700
return nil, fmt.Errorf("unexpected node type %s", ty)
701701
}
702702
}
703-
return deletedStorage, nil
703+
return deletedStorage, it.Error()
704704
}
705705

706706
// isWatchedAddress is used to check if a state account corresponds to one of the addresses the builder is configured to watch

statediff/service.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ type blockChain interface {
4444
GetBlockByNumber(number uint64) *types.Block
4545
GetReceiptsByHash(hash common.Hash) types.Receipts
4646
GetTdByHash(hash common.Hash) *big.Int
47+
UnlockTrie(root common.Hash)
4748
}
4849

4950
// IService is the state-diffing service interface
@@ -53,7 +54,7 @@ type IService interface {
5354
// Main event loop for processing state diffs
5455
Loop(chainEventCh chan core.ChainEvent)
5556
// Method to subscribe to receive state diff processing output
56-
Subscribe(id rpc.ID, sub chan<- Payload, quitChan chan<- bool, params Params)
57+
Subscribe(id rpc.ID, sub chan<- Payload, quitChanogr chan<- bool, params Params)
5758
// Method to unsubscribe from state diff processing
5859
Unsubscribe(id rpc.ID) error
5960
// Method to get state diff object at specific block
@@ -165,8 +166,7 @@ func (sds *Service) streamStateDiff(currentBlock *types.Block, parentRoot common
165166
// create payload for this subscription type
166167
payload, err := sds.processStateDiff(currentBlock, parentRoot, params)
167168
if err != nil {
168-
log.Error(fmt.Sprintf("statediff processing error for subscriptions with parameters: %+v", params))
169-
sds.closeType(ty)
169+
log.Error(fmt.Sprintf("statediff processing error a blockheight %d for subscriptions with parameters: %+v err: %s", currentBlock.Number().Uint64(), params, err.Error()))
170170
continue
171171
}
172172
for id, sub := range subs {
@@ -201,6 +201,8 @@ func (sds *Service) processStateDiff(currentBlock *types.Block, parentRoot commo
201201
BlockHash: currentBlock.Hash(),
202202
BlockNumber: currentBlock.Number(),
203203
}, params)
204+
// allow dereferencing of parent, keep current locked as it should be the next parent
205+
sds.BlockChain.UnlockTrie(parentRoot)
204206
if err != nil {
205207
return nil, err
206208
}

0 commit comments

Comments
 (0)