@@ -61,6 +61,11 @@ const (
6161 // For non-archive nodes, this limit _will_ be overblown, as disk-backed tries
6262 // will only be found every ~15K blocks or so.
6363 defaultTracechainMemLimit = common .StorageSize (500 * 1024 * 1024 )
64+
65+ // maximumPendingTraceStates is the maximum number of states allowed waiting
66+ // for tracing. The creation of trace state will be paused if the unused
67+ // trace states exceed this limit.
68+ maximumPendingTraceStates = 128
6469)
6570
6671// StateReleaseFunc is used to deallocate resources held by constructing a
@@ -251,30 +256,6 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf
251256 return sub , nil
252257}
253258
254- // releaser is a helper tool responsible for caching the release
255- // callbacks of tracing state.
256- type releaser struct {
257- releases []StateReleaseFunc
258- lock sync.Mutex
259- }
260-
261- func (r * releaser ) add (release StateReleaseFunc ) {
262- r .lock .Lock ()
263- defer r .lock .Unlock ()
264-
265- r .releases = append (r .releases , release )
266- }
267-
268- func (r * releaser ) call () {
269- r .lock .Lock ()
270- defer r .lock .Unlock ()
271-
272- for _ , release := range r .releases {
273- release ()
274- }
275- r .releases = r .releases [:0 ]
276- }
277-
278259// traceChain configures a new tracer according to the provided configuration, and
279260// executes all the transactions contained within. The tracing chain range includes
280261// the end block but excludes the start one. The return value will be one item per
@@ -291,11 +272,11 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
291272 threads = blocks
292273 }
293274 var (
294- pend = new (sync.WaitGroup )
295- ctx = context .Background ()
296- taskCh = make (chan * blockTraceTask , threads )
297- resCh = make (chan * blockTraceTask , threads )
298- reler = new ( releaser )
275+ pend = new (sync.WaitGroup )
276+ ctx = context .Background ()
277+ taskCh = make (chan * blockTraceTask , threads )
278+ resCh = make (chan * blockTraceTask , threads )
279+ tracker = newStateTracker ( maximumPendingTraceStates , start . NumberU64 () )
299280 )
300281 for th := 0 ; th < threads ; th ++ {
301282 pend .Add (1 )
@@ -326,8 +307,10 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
326307 task .statedb .Finalise (api .backend .ChainConfig ().IsEIP158 (task .block .Number ()))
327308 task .results [i ] = & txTraceResult {Result : res }
328309 }
329- // Tracing state is used up, queue it for de-referencing
330- reler .add (task .release )
310+ // Tracing state is used up, queue it for de-referencing. Note the
311+ // state is the parent state of trace block, use block.number-1 as
312+ // the state number.
313+ tracker .releaseState (task .block .NumberU64 ()- 1 , task .release )
331314
332315 // Stream the result back to the result catcher or abort on teardown
333316 select {
@@ -354,8 +337,8 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
354337 close (taskCh )
355338 pend .Wait ()
356339
357- // Clean out any pending derefs .
358- reler . call ()
340+ // Clean out any pending release functions of trace states .
341+ tracker . callReleases ()
359342
360343 // Log the chain result
361344 switch {
@@ -392,6 +375,13 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
392375 failed = err
393376 break
394377 }
378+ // Make sure the state creator doesn't go too far. Too many unprocessed
379+ // trace state may cause the oldest state to become stale(e.g. in
380+ // path-based scheme).
381+ if err = tracker .wait (number ); err != nil {
382+ failed = err
383+ break
384+ }
395385 // Prepare the statedb for tracing. Don't use the live database for
396386 // tracing to avoid persisting state junks into the database. Switch
397387 // over to `preferDisk` mode only if the memory usage exceeds the
@@ -407,18 +397,18 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
407397 failed = err
408398 break
409399 }
410- // Clean out any pending derefs. Note this step must be done after
411- // constructing tracing state, because the tracing state of block
412- // next depends on the parent state and construction may fail if
413- // we release too early.
414- reler . call ()
400+ // Clean out any pending release functions of trace state. Note this
401+ // step must be done after constructing tracing state, because the
402+ // tracing state of block next depends on the parent state and construction
403+ // may fail if we release too early.
404+ tracker . callReleases ()
415405
416406 // Send the block over to the concurrent tracers (if not in the fast-forward phase)
417407 txs := next .Transactions ()
418408 select {
419409 case taskCh <- & blockTraceTask {statedb : statedb .Copy (), block : next , release : release , results : make ([]* txTraceResult , len (txs ))}:
420410 case <- closed :
421- reler . add ( release )
411+ tracker . releaseState ( number , release )
422412 return
423413 }
424414 traced += uint64 (len (txs ))
0 commit comments