@@ -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 types2 "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 ,
@@ -122,6 +127,8 @@ type Service struct {
122127 enableWriteLoop bool
123128 // Size of the worker pool
124129 numWorkers uint
130+ // Number of retry for aborted transactions due to deadlock.
131+ maxRetry uint
125132}
126133
127134// BlockCache caches the last block for safe access from different service loops
@@ -174,6 +181,7 @@ func New(stack *node.Node, ethServ *eth.Ethereum, cfg *ethconfig.Config, params
174181 indexer : indexer ,
175182 enableWriteLoop : params .EnableWriteLoop ,
176183 numWorkers : workers ,
184+ maxRetry : defaultRetryLimit ,
177185 }
178186 stack .RegisterLifecycle (sds )
179187 stack .RegisterAPIs (sds .APIs ())
@@ -266,7 +274,7 @@ func (sds *Service) WriteLoop(chainEventCh chan core.ChainEvent) {
266274func (sds * Service ) writeGenesisStateDiff (currBlock * types.Block , workerId uint ) {
267275 // For genesis block we need to return the entire state trie hence we diff it with an empty trie.
268276 log .Info ("Writing state diff" , "block height" , genesisBlockNumber , "worker" , workerId )
269- err := sds .writeStateDiff (currBlock , common.Hash {}, writeLoopParams )
277+ err := sds .writeStateDiffWithRetry (currBlock , common.Hash {}, writeLoopParams )
270278 if err != nil {
271279 log .Error ("statediff.Service.WriteLoop: processing error" , "block height" ,
272280 genesisBlockNumber , "error" , err .Error (), "worker" , workerId )
@@ -295,7 +303,7 @@ func (sds *Service) writeLoopWorker(params workerParams) {
295303 }
296304
297305 log .Info ("Writing state diff" , "block height" , currentBlock .Number ().Uint64 (), "worker" , params .id )
298- err := sds .writeStateDiff (currentBlock , parentBlock .Root (), writeLoopParams )
306+ err := sds .writeStateDiffWithRetry (currentBlock , parentBlock .Root (), writeLoopParams )
299307 if err != nil {
300308 log .Error ("statediff.Service.WriteLoop: processing error" , "block height" , currentBlock .Number ().Uint64 (), "error" , err .Error (), "worker" , params .id )
301309 continue
@@ -632,7 +640,7 @@ func (sds *Service) WriteStateDiffAt(blockNumber uint64, params Params) error {
632640 parentBlock := sds .BlockChain .GetBlockByHash (currentBlock .ParentHash ())
633641 parentRoot = parentBlock .Root ()
634642 }
635- return sds .writeStateDiff (currentBlock , parentRoot , params )
643+ return sds .writeStateDiffWithRetry (currentBlock , parentRoot , params )
636644}
637645
638646// WriteStateDiffFor writes a state diff for the specific blockhash directly to the database
@@ -645,7 +653,7 @@ func (sds *Service) WriteStateDiffFor(blockHash common.Hash, params Params) erro
645653 parentBlock := sds .BlockChain .GetBlockByHash (currentBlock .ParentHash ())
646654 parentRoot = parentBlock .Root ()
647655 }
648- return sds .writeStateDiff (currentBlock , parentRoot , params )
656+ return sds .writeStateDiffWithRetry (currentBlock , parentRoot , params )
649657}
650658
651659// Writes a state diff from the current block, parent state root, and provided params
@@ -689,3 +697,17 @@ func (sds *Service) writeStateDiff(block *types.Block, parentRoot common.Hash, p
689697 }
690698 return nil
691699}
700+
701+ // Wrapper function on writeStateDiff to retry when the deadlock is detected.
702+ func (sds * Service ) writeStateDiffWithRetry (block * types.Block , parentRoot common.Hash , params Params ) error {
703+ var err error
704+ for i := uint (0 ); i < sds .maxRetry ; i ++ {
705+ err = sds .writeStateDiff (block , parentRoot , params )
706+ if err != nil && strings .Contains (err .Error (), deadlockDetected ) {
707+ // Retry only when the deadlock is detected.
708+ continue
709+ }
710+ break
711+ }
712+ return err
713+ }
0 commit comments