From c95ad6d60733c1c97076cf075fa60a1b0178ad65 Mon Sep 17 00:00:00 2001 From: imterryyy Date: Wed, 13 Nov 2024 16:31:31 +0700 Subject: [PATCH 1/2] feat(miner/worker): remove contention on worker.currentMu for pending API requests --- miner/worker.go | 51 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/miner/worker.go b/miner/worker.go index 0116808b5..4a65937a1 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -136,6 +136,10 @@ type worker struct { currentMu sync.Mutex current *Work + snapshotMu sync.RWMutex + snapshotBlock *types.Block + snapshotState *state.StateDB + uncleMu sync.Mutex possibleUncles map[common.Hash]*types.Block @@ -195,32 +199,28 @@ func (self *worker) setExtra(extra []byte) { } func (self *worker) pending() (*types.Block, *state.StateDB) { - self.currentMu.Lock() - defer self.currentMu.Unlock() - if atomic.LoadInt32(&self.mining) == 0 { - return types.NewBlock( - self.current.header, - self.current.txs, - nil, - self.current.receipts, - ), self.current.state.Copy() + // return a snapshot to avoid contention on currentMu mutex + self.snapshotMu.RLock() + defer self.snapshotMu.RUnlock() + return self.snapshotBlock, self.snapshotState.Copy() } - return self.current.Block, self.current.state.Copy() -} -func (self *worker) pendingBlock() *types.Block { self.currentMu.Lock() defer self.currentMu.Unlock() + return self.current.Block, self.current.state.Copy() +} +func (self *worker) pendingBlock() *types.Block { if atomic.LoadInt32(&self.mining) == 0 { - return types.NewBlock( - self.current.header, - self.current.txs, - nil, - self.current.receipts, - ) + // return a snapshot to avoid contention on currentMu mutex + self.snapshotMu.RLock() + defer self.snapshotMu.RUnlock() + return self.snapshotBlock } + + self.currentMu.Lock() + defer self.currentMu.Unlock() return self.current.Block } @@ -316,6 +316,7 @@ func (self *worker) update() { feeCapacity := state.GetTRC21FeeCapacityFromState(self.current.state) txset, specialTxs := types.NewTransactionsByPriceAndNonce(self.current.signer, txs, nil, feeCapacity) self.current.commitTransactions(self.mux, feeCapacity, txset, specialTxs, self.chain, self.coinbase) + self.updateSnapshot() self.currentMu.Unlock() } else { // If we're mining, but nothing is being processed, wake on new transactions @@ -816,6 +817,7 @@ func (self *worker) commitNewWork() { self.lastParentBlockCommit = parent.Hash().Hex() } self.push(work) + self.updateSnapshot() } func (self *worker) commitUncle(work *Work, uncle *types.Header) error { @@ -1076,6 +1078,19 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad } } +func (self *worker) updateSnapshot() { + self.snapshotMu.Lock() + defer self.snapshotMu.Unlock() + + self.snapshotBlock = types.NewBlock( + self.current.header, + self.current.txs, + nil, + self.current.receipts, + ) + self.snapshotState = self.current.state.Copy() +} + func (env *Work) commitTransaction(balanceFee map[common.Address]*big.Int, tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log, bool, uint64) { snap := env.state.Snapshot() From 9641c6b095452f3dc38adf42c555505bedc9be45 Mon Sep 17 00:00:00 2001 From: imterryyy Date: Fri, 15 Nov 2024 10:44:31 +0700 Subject: [PATCH 2/2] fix(eth/api_backend): handle pending block is not available --- eth/api_backend.go | 9 +++++++++ miner/worker.go | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/eth/api_backend.go b/eth/api_backend.go index 7fd7aac3b..91770c1ac 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -76,6 +76,9 @@ func (b *EthApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNum // Pending block is only known by the miner if blockNr == rpc.PendingBlockNumber { block := b.eth.miner.PendingBlock() + if block == nil { + return nil, errors.New("pending block is not available") + } return block.Header(), nil } // Otherwise resolve and return the block @@ -89,6 +92,9 @@ func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumb // Pending block is only known by the miner if blockNr == rpc.PendingBlockNumber { block := b.eth.miner.PendingBlock() + if block == nil { + return nil, errors.New("pending block is not available") + } return block, nil } // Otherwise resolve and return the block @@ -102,6 +108,9 @@ func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc. // Pending state is only known by the miner if blockNr == rpc.PendingBlockNumber { block, state := b.eth.miner.Pending() + if block == nil { + return nil, nil, errors.New("pending block is not available") + } return state, block.Header(), nil } // Otherwise resolve the block number and return its state diff --git a/miner/worker.go b/miner/worker.go index 4a65937a1..6e3d12e14 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -203,11 +203,17 @@ func (self *worker) pending() (*types.Block, *state.StateDB) { // return a snapshot to avoid contention on currentMu mutex self.snapshotMu.RLock() defer self.snapshotMu.RUnlock() + if self.snapshotBlock == nil { + return nil, nil + } return self.snapshotBlock, self.snapshotState.Copy() } self.currentMu.Lock() defer self.currentMu.Unlock() + if self.current == nil { + return nil, nil + } return self.current.Block, self.current.state.Copy() }