@@ -20,6 +20,7 @@ import (
2020 "bytes"
2121 "math/big"
2222 "strconv"
23+ "strings"
2324 "sync"
2425 "sync/atomic"
2526 "time"
@@ -46,8 +47,12 @@ import (
4647 . "github.com/ethereum/go-ethereum/statediff/types"
4748)
4849
49- const chainEventChanSize = 20000
50- const genesisBlockNumber = 0
50+ const (
51+ chainEventChanSize = 20000
52+ genesisBlockNumber = 0
53+ defaultRetryLimit = 3 // default retry limit once deadlock is detected.
54+ deadlockDetected = "deadlock detected" // 40P01 https://www.postgresql.org/docs/current/errcodes-appendix.html
55+ )
5156
5257var writeLoopParams = Params {
5358 IntermediateStateNodes : true ,
@@ -131,6 +136,8 @@ type Service struct {
131136 enableWriteLoop bool
132137 // Size of the worker pool
133138 numWorkers uint
139+ // Number of retry for aborted transactions due to deadlock.
140+ maxRetry uint
134141}
135142
136143// Wrap the cached last block for safe access from different service loops
@@ -189,6 +196,7 @@ func New(stack *node.Node, ethServ *eth.Ethereum, cfg *ethconfig.Config, params
189196 indexer : indexer ,
190197 enableWriteLoop : params .EnableWriteLoop ,
191198 numWorkers : workers ,
199+ maxRetry : defaultRetryLimit ,
192200 }
193201 stack .RegisterLifecycle (sds )
194202 stack .RegisterAPIs (sds .APIs ())
@@ -270,7 +278,7 @@ func (sds *Service) WriteLoop(chainEventCh chan core.ChainEvent) {
270278func (sds * Service ) writeGenesisStateDiff (currBlock * types.Block , workerId uint ) {
271279 // For genesis block we need to return the entire state trie hence we diff it with an empty trie.
272280 log .Info ("Writing state diff" , "block height" , genesisBlockNumber , "worker" , workerId )
273- err := sds .writeStateDiff (currBlock , common.Hash {}, writeLoopParams )
281+ err := sds .writeStateDiffWithRetry (currBlock , common.Hash {}, writeLoopParams )
274282 if err != nil {
275283 log .Error ("statediff.Service.WriteLoop: processing error" , "block height" ,
276284 genesisBlockNumber , "error" , err .Error (), "worker" , workerId )
@@ -299,7 +307,7 @@ func (sds *Service) writeLoopWorker(params workerParams) {
299307 }
300308
301309 log .Info ("Writing state diff" , "block height" , currentBlock .Number ().Uint64 (), "worker" , params .id )
302- err := sds .writeStateDiff (currentBlock , parentBlock .Root (), writeLoopParams )
310+ err := sds .writeStateDiffWithRetry (currentBlock , parentBlock .Root (), writeLoopParams )
303311 if err != nil {
304312 log .Error ("statediff.Service.WriteLoop: processing error" , "block height" , currentBlock .Number ().Uint64 (), "error" , err .Error (), "worker" , params .id )
305313 continue
@@ -638,7 +646,7 @@ func (sds *Service) WriteStateDiffAt(blockNumber uint64, params Params) error {
638646 parentBlock := sds .BlockChain .GetBlockByHash (currentBlock .ParentHash ())
639647 parentRoot = parentBlock .Root ()
640648 }
641- return sds .writeStateDiff (currentBlock , parentRoot , params )
649+ return sds .writeStateDiffWithRetry (currentBlock , parentRoot , params )
642650}
643651
644652// WriteStateDiffFor writes a state diff for the specific blockhash directly to the database
@@ -651,7 +659,7 @@ func (sds *Service) WriteStateDiffFor(blockHash common.Hash, params Params) erro
651659 parentBlock := sds .BlockChain .GetBlockByHash (currentBlock .ParentHash ())
652660 parentRoot = parentBlock .Root ()
653661 }
654- return sds .writeStateDiff (currentBlock , parentRoot , params )
662+ return sds .writeStateDiffWithRetry (currentBlock , parentRoot , params )
655663}
656664
657665// Writes a state diff from the current block, parent state root, and provided params
@@ -691,3 +699,17 @@ func (sds *Service) writeStateDiff(block *types.Block, parentRoot common.Hash, p
691699 }
692700 return nil
693701}
702+
703+ // Wrapper function on writeStateDiff to retry when the deadlock is detected.
704+ func (sds * Service ) writeStateDiffWithRetry (block * types.Block , parentRoot common.Hash , params Params ) error {
705+ var err error
706+ for i := uint (0 ); i < sds .maxRetry ; i ++ {
707+ err = sds .writeStateDiff (block , parentRoot , params )
708+ if err != nil && strings .Contains (err .Error (), deadlockDetected ) {
709+ // Retry only when the deadlock is detected.
710+ continue
711+ }
712+ break
713+ }
714+ return err
715+ }
0 commit comments