mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
core: add graceful shutdown checks during block processing
Fixes issue where node would continue processing blocks for several seconds after receiving shutdown signal (Ctrl+C), ignoring user interruption. The problem occurred because shutdown signals were only checked at the beginning of each block processing loop in insertChain(), but not during the actual block processing in processBlock(). Heavy blocks with high gas usage or many transactions could take several seconds to process, during which shutdown signals were completely ignored. This change adds shutdown signal checks at strategic points in processBlock(): - Before starting block processing - After state setup but before processing - After processing but before validation - After validation but before writing to disk These checks allow block processing to be interrupted gracefully at safe points, providing immediate response to shutdown signals. Fixes #31508
This commit is contained in:
parent
a92f2b86e3
commit
aacd081b5f
2 changed files with 60 additions and 0 deletions
|
|
@ -1956,6 +1956,11 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
|
||||||
)
|
)
|
||||||
defer interrupt.Store(true) // terminate the prefetch at the end
|
defer interrupt.Store(true) // terminate the prefetch at the end
|
||||||
|
|
||||||
|
// Short circuit if shutting down
|
||||||
|
if bc.insertStopped() {
|
||||||
|
return nil, fmt.Errorf("block processing interrupted during shutdown")
|
||||||
|
}
|
||||||
|
|
||||||
if bc.cfg.NoPrefetch {
|
if bc.cfg.NoPrefetch {
|
||||||
statedb, err = state.New(parentRoot, bc.statedb)
|
statedb, err = state.New(parentRoot, bc.statedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -2006,6 +2011,10 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
|
||||||
}(time.Now(), throwaway, block)
|
}(time.Now(), throwaway, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bc.insertStopped() {
|
||||||
|
return nil, fmt.Errorf("block processing interrupted during shutdown")
|
||||||
|
}
|
||||||
|
|
||||||
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||||
// while processing transactions. Before Byzantium the prefetcher is mostly
|
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||||
// useless due to the intermediate root hashing after each transaction.
|
// useless due to the intermediate root hashing after each transaction.
|
||||||
|
|
@ -2046,6 +2055,10 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
|
||||||
}
|
}
|
||||||
ptime := time.Since(pstart)
|
ptime := time.Since(pstart)
|
||||||
|
|
||||||
|
if bc.insertStopped() {
|
||||||
|
return nil, fmt.Errorf("block processing interrupted during shutdown")
|
||||||
|
}
|
||||||
|
|
||||||
vstart := time.Now()
|
vstart := time.Now()
|
||||||
if err := bc.validator.ValidateState(block, statedb, res, false); err != nil {
|
if err := bc.validator.ValidateState(block, statedb, res, false); err != nil {
|
||||||
bc.reportBlock(block, res, err)
|
bc.reportBlock(block, res, err)
|
||||||
|
|
@ -2053,6 +2066,10 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
|
||||||
}
|
}
|
||||||
vtime := time.Since(vstart)
|
vtime := time.Since(vstart)
|
||||||
|
|
||||||
|
if bc.insertStopped() {
|
||||||
|
return nil, fmt.Errorf("block processing interrupted during shutdown")
|
||||||
|
}
|
||||||
|
|
||||||
// If witnesses was generated and stateless self-validation requested, do
|
// If witnesses was generated and stateless self-validation requested, do
|
||||||
// that now. Self validation should *never* run in production, it's more of
|
// that now. Self validation should *never* run in production, it's more of
|
||||||
// a tight integration to enable running *all* consensus tests through the
|
// a tight integration to enable running *all* consensus tests through the
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -4407,3 +4408,45 @@ func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestBlockProcessingInterrupt tests graceful shutdown during block processing
|
||||||
|
func TestBlockProcessingInterrupt(t *testing.T) {
|
||||||
|
testBlockProcessingInterrupt(t, rawdb.HashScheme)
|
||||||
|
testBlockProcessingInterrupt(t, rawdb.PathScheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBlockProcessingInterrupt(t *testing.T, scheme string) {
|
||||||
|
var (
|
||||||
|
genesis = &Genesis{
|
||||||
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
|
Config: params.AllEthashProtocolChanges,
|
||||||
|
}
|
||||||
|
engine = ethash.NewFaker()
|
||||||
|
)
|
||||||
|
|
||||||
|
options := DefaultConfig().WithStateScheme(scheme)
|
||||||
|
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), genesis, engine, options)
|
||||||
|
defer blockchain.Stop()
|
||||||
|
|
||||||
|
genDb, blocks := makeBlockChainWithGenesis(genesis, 1, engine, canonicalSeed)
|
||||||
|
defer genDb.Close()
|
||||||
|
|
||||||
|
// Test interrupt during block processing
|
||||||
|
blockchain.InterruptInsert(true)
|
||||||
|
parent := blockchain.genesisBlock.Header()
|
||||||
|
_, err := blockchain.processBlock(parent.Root, blocks[0], true, false)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected block processing to be interrupted, but it succeeded")
|
||||||
|
}
|
||||||
|
if !strings.Contains(fmt.Sprintf("%v", err), "block processing interrupted during shutdown") {
|
||||||
|
t.Errorf("Expected 'block processing interrupted during shutdown' error, got: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test processing works after reset
|
||||||
|
blockchain.InterruptInsert(false)
|
||||||
|
_, err = blockchain.processBlock(parent.Root, blocks[0], true, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to process block after reset: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue