Skip to content

Commit

Permalink
eth/tracers: avoid unsyncronized mutations on trie database
Browse files Browse the repository at this point in the history
  • Loading branch information
holiman committed Sep 23, 2021
1 parent ca9bce9 commit bb4f533
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 9 deletions.
3 changes: 2 additions & 1 deletion eth/state_accessor.go
Expand Up @@ -119,7 +119,8 @@ func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state
// Finalize the state so any modifications are written to the trie
root, err := statedb.Commit(eth.blockchain.Config().IsEIP158(current.Number()))
if err != nil {
return nil, err
return nil, fmt.Errorf("stateAtBlock commit failed, number %d root %v: %w",
current.NumberU64(), current.Root().Hex(), err)
}
statedb, err = state.New(root, database, nil)
if err != nil {
Expand Down
28 changes: 20 additions & 8 deletions eth/tracers/api.go
Expand Up @@ -290,7 +290,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
}()
}
// Start a goroutine to feed all the blocks into the tracers
begin := time.Now()
var (
begin = time.Now()
derefTodo []common.Hash // list of hashes to dereference from the db
derefsMu sync.Mutex // mutex for the derefs
)

go func() {
var (
Expand Down Expand Up @@ -324,6 +328,14 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
return
default:
}
// clean out any derefs
derefsMu.Lock()
for _, h := range derefTodo {
statedb.Database().TrieDB().Dereference(h)
}
derefTodo = derefTodo[:0]
derefsMu.Unlock()

// Print progress logs if long enough time elapsed
if time.Since(logged) > 8*time.Second {
logged = time.Now()
Expand All @@ -337,10 +349,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
}
// Prepare the statedb for tracing. Don't use the live database for
// tracing to avoid persisting state junks into the database.
statedb, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false)
if err != nil {
if _statedb, err := api.backend.StateAtBlock(localctx, block, reexec, statedb, false); err != nil {
failed = err
break
} else {
statedb = _statedb
}
if statedb.Database().TrieDB() != nil {
// Hold the reference for tracer, will be released at the final stage
Expand Down Expand Up @@ -382,12 +395,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
Hash: res.block.Hash(),
Traces: res.results,
}
// Schedule any parent tries held in memory by this task for dereferencing
done[uint64(result.Block)] = result

// Dereference any parent tries held in memory by this task
if res.statedb.Database().TrieDB() != nil {
res.statedb.Database().TrieDB().Dereference(res.rootref)
}
derefsMu.Lock()
derefTodo = append(derefTodo, res.rootref)
derefsMu.Unlock()
// Stream completed traces to the user, aborting on the first error
for result, ok := done[next]; ok; result, ok = done[next] {
if len(result.Traces) > 0 || next == end.NumberU64() {
Expand Down

0 comments on commit bb4f533

Please sign in to comment.