From 20653ac0d48f6803900555592f10b75a2e1c3bd6 Mon Sep 17 00:00:00 2001 From: Arijit Das Date: Thu, 9 Dec 2021 13:24:29 +0530 Subject: [PATCH 1/2] Retry aborted transaction when the deadlock is detected. --- statediff/service.go | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/statediff/service.go b/statediff/service.go index 1154e475056d..63a0e38e16c2 100644 --- a/statediff/service.go +++ b/statediff/service.go @@ -20,6 +20,7 @@ import ( "bytes" "math/big" "strconv" + "strings" "sync" "sync/atomic" "time" @@ -46,8 +47,12 @@ import ( . "github.com/ethereum/go-ethereum/statediff/types" ) -const chainEventChanSize = 20000 -const genesisBlockNumber = 0 +const ( + chainEventChanSize = 20000 + genesisBlockNumber = 0 + defaultRetryLimit = 3 // default retry limit once deadlock is detected. + deadlockDetected = "deadlock detected" // 40P01 https://www.postgresql.org/docs/current/errcodes-appendix.html +) var writeLoopParams = Params{ IntermediateStateNodes: true, @@ -131,6 +136,8 @@ type Service struct { enableWriteLoop bool // Size of the worker pool numWorkers uint + // Number of retry for aborted transactions due to deadlock. + maxRetry uint } // 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 indexer: indexer, enableWriteLoop: params.EnableWriteLoop, numWorkers: workers, + maxRetry: defaultRetryLimit, } stack.RegisterLifecycle(sds) stack.RegisterAPIs(sds.APIs()) @@ -270,7 +278,7 @@ func (sds *Service) WriteLoop(chainEventCh chan core.ChainEvent) { func (sds *Service) writeGenesisStateDiff(currBlock *types.Block, workerId uint) { // For genesis block we need to return the entire state trie hence we diff it with an empty trie. log.Info("Writing state diff", "block height", genesisBlockNumber, "worker", workerId) - err := sds.writeStateDiff(currBlock, common.Hash{}, writeLoopParams) + err := sds.writeStateDiffWithRetry(currBlock, common.Hash{}, writeLoopParams) if err != nil { log.Error("statediff.Service.WriteLoop: processing error", "block height", genesisBlockNumber, "error", err.Error(), "worker", workerId) @@ -299,7 +307,7 @@ func (sds *Service) writeLoopWorker(params workerParams) { } log.Info("Writing state diff", "block height", currentBlock.Number().Uint64(), "worker", params.id) - err := sds.writeStateDiff(currentBlock, parentBlock.Root(), writeLoopParams) + err := sds.writeStateDiffWithRetry(currentBlock, parentBlock.Root(), writeLoopParams) if err != nil { log.Error("statediff.Service.WriteLoop: processing error", "block height", currentBlock.Number().Uint64(), "error", err.Error(), "worker", params.id) continue @@ -638,7 +646,7 @@ func (sds *Service) WriteStateDiffAt(blockNumber uint64, params Params) error { parentBlock := sds.BlockChain.GetBlockByHash(currentBlock.ParentHash()) parentRoot = parentBlock.Root() } - return sds.writeStateDiff(currentBlock, parentRoot, params) + return sds.writeStateDiffWithRetry(currentBlock, parentRoot, params) } // 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 parentBlock := sds.BlockChain.GetBlockByHash(currentBlock.ParentHash()) parentRoot = parentBlock.Root() } - return sds.writeStateDiff(currentBlock, parentRoot, params) + return sds.writeStateDiffWithRetry(currentBlock, parentRoot, params) } // 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 } return nil } + +// Wrapper function on writeStateDiff to retry when the deadlock is detected. +func (sds *Service) writeStateDiffWithRetry(block *types.Block, parentRoot common.Hash, params Params) error { + var err error + for i := uint(0); i < sds.maxRetry; i++ { + err = sds.writeStateDiff(block, parentRoot, params) + if err != nil && strings.Contains(err.Error(), deadlockDetected) { + // Retry only when the deadlock is detected. + continue + } + break + } + return err +} \ No newline at end of file From 8a3fba7970a48ddf16ebc39ec56492ac74fb64ea Mon Sep 17 00:00:00 2001 From: Arijit Das Date: Thu, 9 Dec 2021 14:54:18 +0530 Subject: [PATCH 2/2] Fix lint error. --- statediff/service.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/statediff/service.go b/statediff/service.go index 63a0e38e16c2..6411ba68efb6 100644 --- a/statediff/service.go +++ b/statediff/service.go @@ -50,8 +50,8 @@ import ( const ( chainEventChanSize = 20000 genesisBlockNumber = 0 - defaultRetryLimit = 3 // default retry limit once deadlock is detected. - deadlockDetected = "deadlock detected" // 40P01 https://www.postgresql.org/docs/current/errcodes-appendix.html + defaultRetryLimit = 3 // default retry limit once deadlock is detected. + deadlockDetected = "deadlock detected" // 40P01 https://www.postgresql.org/docs/current/errcodes-appendix.html ) var writeLoopParams = Params{ @@ -712,4 +712,4 @@ func (sds *Service) writeStateDiffWithRetry(block *types.Block, parentRoot commo break } return err -} \ No newline at end of file +}