Skip to content

Commit ef3f270

Browse files
authored
Merge pull request #166 from vulcanize/retry-on-deadlock
Retry aborted transaction when the deadlock is detected.
2 parents a21abdd + 8a3fba7 commit ef3f270

File tree

1 file changed

+28
-6
lines changed

1 file changed

+28
-6
lines changed

statediff/service.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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

5257
var 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) {
270278
func (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

Comments
 (0)