mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-21 04:06:44 +00:00
core: improve chain reset head
This commit is contained in:
parent
07dd8e052f
commit
135927b5c9
1 changed files with 35 additions and 19 deletions
|
|
@ -966,14 +966,11 @@ func (bc *BlockChain) rewindPathHead(head *types.Header, root common.Hash) (*typ
|
||||||
return head, rootNumber
|
return head, rootNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Recover if the target state if it's not available yet.
|
// Note, the state of the located head may not be physically present yet if
|
||||||
if !bc.HasState(head.Root) {
|
// it's only recoverable. The actual recovery is intentionally deferred once
|
||||||
if err := bc.triedb.Recover(head.Root); err != nil {
|
// the new head is finalized, so that a deep rewind rolls the state back in
|
||||||
log.Error("Failed to rollback state, resetting to genesis", "err", err)
|
// one shot.
|
||||||
return bc.genesisBlock.Header(), rootNumber
|
log.Info("Rewound to block with available state", "number", head.Number, "hash", head.Hash())
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Info("Rewound to block with state", "number", head.Number, "hash", head.Hash())
|
|
||||||
return head, rootNumber
|
return head, rootNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1034,17 +1031,11 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||||
bc.currentBlock.Store(newHeadBlock)
|
bc.currentBlock.Store(newHeadBlock)
|
||||||
headBlockGauge.Update(int64(newHeadBlock.Number.Uint64()))
|
headBlockGauge.Update(int64(newHeadBlock.Number.Uint64()))
|
||||||
|
|
||||||
// The head state is missing, which is only possible in the path-based
|
// Note, the located head state might not be physically present yet; in
|
||||||
// scheme. This situation occurs when the chain head is rewound below
|
// the path-based scheme a recoverable state is materialized in a single
|
||||||
// the pivot point. In this scenario, there is no possible recovery
|
// shot once the rewind is finalized (see below). The stateless-head
|
||||||
// approach except for rerunning a snap sync. Do nothing here until the
|
// handling is therefore deferred until then, also avoiding a per-block
|
||||||
// state syncer picks it up.
|
// recoverability check whose cost would grow as the rewind goes deeper.
|
||||||
if !bc.HasState(newHeadBlock.Root) {
|
|
||||||
if newHeadBlock.Number.Uint64() != 0 {
|
|
||||||
log.Crit("Chain is stateless at a non-genesis block")
|
|
||||||
}
|
|
||||||
log.Info("Chain is stateless, wait state sync", "number", newHeadBlock.Number, "hash", newHeadBlock.Hash())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Rewind the snap block in a simpleton way to the target head
|
// Rewind the snap block in a simpleton way to the target head
|
||||||
if currentSnapBlock := bc.CurrentSnapBlock(); currentSnapBlock != nil && header.Number.Uint64() < currentSnapBlock.Number.Uint64() {
|
if currentSnapBlock := bc.CurrentSnapBlock(); currentSnapBlock != nil && header.Number.Uint64() < currentSnapBlock.Number.Uint64() {
|
||||||
|
|
@ -1113,6 +1104,31 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||||
bc.hc.SetHead(head, updateFn, delFn)
|
bc.hc.SetHead(head, updateFn, delFn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// In the path-based scheme, the rewind loop above only locates the new head
|
||||||
|
// without materializing its state, so the potentially deep rollback is done
|
||||||
|
// here in a single shot. This rolls back the whole rewound range at once,
|
||||||
|
// performing a single fsync rather than one per block, which is critical when
|
||||||
|
// rewinding a large number of blocks.
|
||||||
|
if newHeadBlock := bc.CurrentBlock(); !bc.HasState(newHeadBlock.Root) {
|
||||||
|
switch {
|
||||||
|
case bc.stateRecoverable(newHeadBlock.Root):
|
||||||
|
if err := bc.triedb.Recover(newHeadBlock.Root); err != nil {
|
||||||
|
// The state was confirmed recoverable just above, so a failure here
|
||||||
|
// can only stem from an unexpected I/O error. There is no safe way to
|
||||||
|
// continue with a half-rolled-back state, hence crash hard.
|
||||||
|
log.Crit("Failed to recover state", "number", newHeadBlock.Number, "hash", newHeadBlock.Hash(), "err", err)
|
||||||
|
}
|
||||||
|
case newHeadBlock.Number.Uint64() != 0:
|
||||||
|
// rewindHead only returns a non-genesis head when its state is present
|
||||||
|
// or recoverable, so this branch should be unreachable.
|
||||||
|
log.Crit("Chain is stateless at a non-genesis block", "number", newHeadBlock.Number, "hash", newHeadBlock.Hash())
|
||||||
|
default:
|
||||||
|
// The chain head was rewound below the snap-sync pivot to a stateless
|
||||||
|
// genesis. There is no recovery approach except rerunning a snap sync;
|
||||||
|
// do nothing here until the state syncer picks it up.
|
||||||
|
log.Info("Chain is stateless, wait state sync", "number", newHeadBlock.Number, "hash", newHeadBlock.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
// Clear out any stale content from the caches
|
// Clear out any stale content from the caches
|
||||||
bc.bodyCache.Purge()
|
bc.bodyCache.Purge()
|
||||||
bc.bodyRLPCache.Purge()
|
bc.bodyRLPCache.Purge()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue