eth: interrupt chain insertion on shutdown (#21114)
This adds a new API method on core.BlockChain to allow interrupting running data inserts, and calls the method before shutting down the downloader. The BlockChain interrupt checks are now done through a method instead of inlining the atomic load everywhere. There is no loss of efficiency from this and it makes the interrupt protocol a lot clearer because the check is defined next to the method that sets the flag.
This commit is contained in:
parent
4873a9d3c3
commit
9219e0fba4
@ -178,11 +178,10 @@ type BlockChain struct {
|
|||||||
txLookupCache *lru.Cache // Cache for the most recent transaction lookup data.
|
txLookupCache *lru.Cache // Cache for the most recent transaction lookup data.
|
||||||
futureBlocks *lru.Cache // future blocks are blocks added for later processing
|
futureBlocks *lru.Cache // future blocks are blocks added for later processing
|
||||||
|
|
||||||
quit chan struct{} // blockchain quit channel
|
quit chan struct{} // blockchain quit channel
|
||||||
running int32 // running must be called atomically
|
|
||||||
// procInterrupt must be atomically called
|
|
||||||
procInterrupt int32 // interrupt signaler for block processing
|
|
||||||
wg sync.WaitGroup // chain processing wait group for shutting down
|
wg sync.WaitGroup // chain processing wait group for shutting down
|
||||||
|
running int32 // 0 if chain is running, 1 when stopped
|
||||||
|
procInterrupt int32 // interrupt signaler for block processing
|
||||||
|
|
||||||
engine consensus.Engine
|
engine consensus.Engine
|
||||||
validator Validator // Block and state validator interface
|
validator Validator // Block and state validator interface
|
||||||
@ -239,7 +238,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||||||
bc.processor = NewStateProcessor(chainConfig, bc, engine)
|
bc.processor = NewStateProcessor(chainConfig, bc, engine)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.getProcInterrupt)
|
bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.insertStopped)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -332,10 +331,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||||||
return bc, nil
|
return bc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) getProcInterrupt() bool {
|
|
||||||
return atomic.LoadInt32(&bc.procInterrupt) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVMConfig returns the block chain VM config.
|
// GetVMConfig returns the block chain VM config.
|
||||||
func (bc *BlockChain) GetVMConfig() *vm.Config {
|
func (bc *BlockChain) GetVMConfig() *vm.Config {
|
||||||
return &bc.vmConfig
|
return &bc.vmConfig
|
||||||
@ -882,8 +877,7 @@ func (bc *BlockChain) Stop() {
|
|||||||
// Unsubscribe all subscriptions registered from blockchain
|
// Unsubscribe all subscriptions registered from blockchain
|
||||||
bc.scope.Close()
|
bc.scope.Close()
|
||||||
close(bc.quit)
|
close(bc.quit)
|
||||||
atomic.StoreInt32(&bc.procInterrupt, 1)
|
bc.StopInsert()
|
||||||
|
|
||||||
bc.wg.Wait()
|
bc.wg.Wait()
|
||||||
|
|
||||||
// Ensure that the entirety of the state snapshot is journalled to disk.
|
// Ensure that the entirety of the state snapshot is journalled to disk.
|
||||||
@ -928,6 +922,18 @@ func (bc *BlockChain) Stop() {
|
|||||||
log.Info("Blockchain stopped")
|
log.Info("Blockchain stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StopInsert interrupts all insertion methods, causing them to return
|
||||||
|
// errInsertionInterrupted as soon as possible. Insertion is permanently disabled after
|
||||||
|
// calling this method.
|
||||||
|
func (bc *BlockChain) StopInsert() {
|
||||||
|
atomic.StoreInt32(&bc.procInterrupt, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// insertStopped returns true after StopInsert has been called.
|
||||||
|
func (bc *BlockChain) insertStopped() bool {
|
||||||
|
return atomic.LoadInt32(&bc.procInterrupt) == 1
|
||||||
|
}
|
||||||
|
|
||||||
func (bc *BlockChain) procFutureBlocks() {
|
func (bc *BlockChain) procFutureBlocks() {
|
||||||
blocks := make([]*types.Block, 0, bc.futureBlocks.Len())
|
blocks := make([]*types.Block, 0, bc.futureBlocks.Len())
|
||||||
for _, hash := range bc.futureBlocks.Keys() {
|
for _, hash := range bc.futureBlocks.Keys() {
|
||||||
@ -1113,7 +1119,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||||||
var deleted []*numberHash
|
var deleted []*numberHash
|
||||||
for i, block := range blockChain {
|
for i, block := range blockChain {
|
||||||
// Short circuit insertion if shutting down or processing failed
|
// Short circuit insertion if shutting down or processing failed
|
||||||
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
|
if bc.insertStopped() {
|
||||||
return 0, errInsertionInterrupted
|
return 0, errInsertionInterrupted
|
||||||
}
|
}
|
||||||
// Short circuit insertion if it is required(used in testing only)
|
// Short circuit insertion if it is required(used in testing only)
|
||||||
@ -1260,7 +1266,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||||||
batch := bc.db.NewBatch()
|
batch := bc.db.NewBatch()
|
||||||
for i, block := range blockChain {
|
for i, block := range blockChain {
|
||||||
// Short circuit insertion if shutting down or processing failed
|
// Short circuit insertion if shutting down or processing failed
|
||||||
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
|
if bc.insertStopped() {
|
||||||
return 0, errInsertionInterrupted
|
return 0, errInsertionInterrupted
|
||||||
}
|
}
|
||||||
// Short circuit if the owner header is unknown
|
// Short circuit if the owner header is unknown
|
||||||
@ -1708,8 +1714,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
|
|||||||
// No validation errors for the first block (or chain prefix skipped)
|
// No validation errors for the first block (or chain prefix skipped)
|
||||||
for ; block != nil && err == nil || err == ErrKnownBlock; block, err = it.next() {
|
for ; block != nil && err == nil || err == ErrKnownBlock; block, err = it.next() {
|
||||||
// If the chain is terminating, stop processing blocks
|
// If the chain is terminating, stop processing blocks
|
||||||
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
|
if bc.insertStopped() {
|
||||||
log.Debug("Premature abort during blocks processing")
|
log.Debug("Abort during block processing")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// If the header is a banned one, straight out abort
|
// If the header is a banned one, straight out abort
|
||||||
@ -1996,8 +2002,8 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
|||||||
blocks, memory = blocks[:0], 0
|
blocks, memory = blocks[:0], 0
|
||||||
|
|
||||||
// If the chain is terminating, stop processing blocks
|
// If the chain is terminating, stop processing blocks
|
||||||
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
|
if bc.insertStopped() {
|
||||||
log.Debug("Premature abort during blocks processing")
|
log.Debug("Abort during blocks processing")
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,6 @@ func (cs *chainSyncer) loop() {
|
|||||||
cs.pm.txFetcher.Start()
|
cs.pm.txFetcher.Start()
|
||||||
defer cs.pm.blockFetcher.Stop()
|
defer cs.pm.blockFetcher.Stop()
|
||||||
defer cs.pm.txFetcher.Stop()
|
defer cs.pm.txFetcher.Stop()
|
||||||
defer cs.pm.downloader.Terminate()
|
|
||||||
|
|
||||||
// The force timer lowers the peer count threshold down to one when it fires.
|
// The force timer lowers the peer count threshold down to one when it fires.
|
||||||
// This ensures we'll always start sync even if there aren't enough peers.
|
// This ensures we'll always start sync even if there aren't enough peers.
|
||||||
@ -222,8 +221,13 @@ func (cs *chainSyncer) loop() {
|
|||||||
cs.forced = true
|
cs.forced = true
|
||||||
|
|
||||||
case <-cs.pm.quitSync:
|
case <-cs.pm.quitSync:
|
||||||
|
// Disable all insertion on the blockchain. This needs to happen before
|
||||||
|
// terminating the downloader because the downloader waits for blockchain
|
||||||
|
// inserts, and these can take a long time to finish.
|
||||||
|
cs.pm.blockchain.StopInsert()
|
||||||
|
cs.pm.downloader.Terminate()
|
||||||
if cs.doneCh != nil {
|
if cs.doneCh != nil {
|
||||||
cs.pm.downloader.Terminate() // Double term is fine, Cancel would block until queue is emptied
|
// Wait for the current sync to end.
|
||||||
<-cs.doneCh
|
<-cs.doneCh
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -314,10 +314,16 @@ func (lc *LightChain) Stop() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
close(lc.quit)
|
close(lc.quit)
|
||||||
atomic.StoreInt32(&lc.procInterrupt, 1)
|
lc.StopInsert()
|
||||||
|
|
||||||
lc.wg.Wait()
|
lc.wg.Wait()
|
||||||
log.Info("Blockchain manager stopped")
|
log.Info("Blockchain stopped")
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopInsert interrupts all insertion methods, causing them to return
|
||||||
|
// errInsertionInterrupted as soon as possible. Insertion is permanently disabled after
|
||||||
|
// calling this method.
|
||||||
|
func (lc *LightChain) StopInsert() {
|
||||||
|
atomic.StoreInt32(&lc.procInterrupt, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rollback is designed to remove a chain of links from the database that aren't
|
// Rollback is designed to remove a chain of links from the database that aren't
|
||||||
|
Loading…
Reference in New Issue
Block a user