mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 04:36:42 +00:00
verified concurrency fixes in beacon sync and general downloader stability
This commit is contained in:
parent
bf141fbfb1
commit
d39b42fd32
2 changed files with 26 additions and 36 deletions
|
|
@ -1156,28 +1156,29 @@ func (bc *BlockChain) SnapSyncComplete(hash common.Hash) error {
|
||||||
if !bc.chainmu.TryLock() {
|
if !bc.chainmu.TryLock() {
|
||||||
return errChainStopped
|
return errChainStopped
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
|
||||||
|
|
||||||
// Reset the trie database with the fresh snap synced state.
|
// Reset the trie database with the fresh snap synced state.
|
||||||
root := block.Root()
|
root := block.Root()
|
||||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
if bc.triedb.Scheme() == rawdb.PathScheme {
|
||||||
if err := bc.triedb.Enable(root); err != nil {
|
if err := bc.triedb.Enable(root); err != nil {
|
||||||
|
bc.chainmu.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !bc.HasState(root) {
|
if !bc.HasState(root) {
|
||||||
|
bc.chainmu.Unlock()
|
||||||
return fmt.Errorf("non existent state [%x..]", root[:4])
|
return fmt.Errorf("non existent state [%x..]", root[:4])
|
||||||
}
|
}
|
||||||
|
// If all checks out, manually set the head block.
|
||||||
|
bc.currentBlock.Store(block.Header())
|
||||||
|
headBlockGauge.Update(int64(block.NumberU64()))
|
||||||
|
bc.chainmu.Unlock() // Unlock specificly before Rebuild to avoid deadlock
|
||||||
|
|
||||||
// Destroy any existing state snapshot and regenerate it in the background,
|
// Destroy any existing state snapshot and regenerate it in the background,
|
||||||
// also resuming the normal maintenance of any previously paused snapshot.
|
// also resuming the normal maintenance of any previously paused snapshot.
|
||||||
if bc.snaps != nil {
|
if bc.snaps != nil {
|
||||||
bc.snaps.Rebuild(root)
|
bc.snaps.Rebuild(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If all checks out, manually set the head block.
|
|
||||||
bc.currentBlock.Store(block.Header())
|
|
||||||
headBlockGauge.Update(int64(block.NumberU64()))
|
|
||||||
|
|
||||||
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
|
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,56 +49,44 @@ func newBeaconBackfiller(dl *Downloader, success func()) backfiller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// suspend cancels any background downloader threads and returns the last header
|
||||||
|
// that has been successfully backfilled (potentially in a previous run), or the
|
||||||
|
// genesis.
|
||||||
// suspend cancels any background downloader threads and returns the last header
|
// suspend cancels any background downloader threads and returns the last header
|
||||||
// that has been successfully backfilled (potentially in a previous run), or the
|
// that has been successfully backfilled (potentially in a previous run), or the
|
||||||
// genesis.
|
// genesis.
|
||||||
func (b *beaconBackfiller) suspend() *types.Header {
|
func (b *beaconBackfiller) suspend() *types.Header {
|
||||||
// If no filling is running, don't waste cycles
|
|
||||||
b.lock.Lock()
|
b.lock.Lock()
|
||||||
filling := b.filling
|
defer b.lock.Unlock()
|
||||||
filled := b.filled
|
|
||||||
started := b.started
|
|
||||||
b.lock.Unlock()
|
|
||||||
|
|
||||||
if !filling {
|
if !b.filling {
|
||||||
log.Debug("Backfiller was inactive")
|
return b.filled
|
||||||
return filled // Return the filled header on the previous sync completion
|
|
||||||
}
|
}
|
||||||
// A previous filling should be running, though it may happen that it hasn't
|
// Stop the downloader. It is safe to call Cancel concurrently with synchronise
|
||||||
// yet started (being done on a new goroutine). Many concurrent beacon head
|
// (which might be running in resume's goroutine), as Cancel is thread-safe.
|
||||||
// announcements can lead to sync start/stop thrashing. In that case we need
|
|
||||||
// to wait for initialization before we can safely cancel it. It is safe to
|
|
||||||
// read this channel multiple times, it gets closed on startup.
|
|
||||||
<-started
|
|
||||||
|
|
||||||
// Now that we're sure the downloader successfully started up, we can cancel
|
|
||||||
// it safely without running the risk of data races.
|
|
||||||
b.downloader.Cancel()
|
b.downloader.Cancel()
|
||||||
log.Debug("Backfiller has been suspended")
|
b.filling = false
|
||||||
|
|
||||||
// Sync cycle was just terminated, retrieve and return the last filled header.
|
// Wait for the sync routine to finish to ensure no zombie processes
|
||||||
// Can't use `filled` as that contains a stale value from before cancellation.
|
<-b.started
|
||||||
|
|
||||||
|
log.Debug("Backfiller suspended")
|
||||||
return b.downloader.blockchain.CurrentSnapBlock()
|
return b.downloader.blockchain.CurrentSnapBlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// resume starts the downloader threads for backfilling state and chain data.
|
// resume starts the downloader threads for backfilling state and chain data.
|
||||||
func (b *beaconBackfiller) resume() {
|
func (b *beaconBackfiller) resume() {
|
||||||
b.lock.Lock()
|
b.lock.Lock()
|
||||||
|
defer b.lock.Unlock()
|
||||||
|
|
||||||
if b.filling {
|
if b.filling {
|
||||||
// If a previous filling cycle is still running, just ignore this start
|
|
||||||
// request. // TODO(karalabe): We should make this channel driven
|
|
||||||
b.lock.Unlock()
|
|
||||||
log.Debug("Backfiller is running")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.filling = true
|
b.filling = true
|
||||||
b.filled = nil
|
b.filled = nil
|
||||||
b.started = make(chan struct{})
|
b.started = make(chan struct{})
|
||||||
b.lock.Unlock()
|
|
||||||
|
|
||||||
// Start the backfilling on its own thread since the downloader does not have
|
go func(started chan struct{}) {
|
||||||
// its own lifecycle runloop.
|
|
||||||
go func() {
|
|
||||||
// Set the backfiller to non-filling when download completes
|
// Set the backfiller to non-filling when download completes
|
||||||
defer func() {
|
defer func() {
|
||||||
b.lock.Lock()
|
b.lock.Lock()
|
||||||
|
|
@ -108,7 +96,7 @@ func (b *beaconBackfiller) resume() {
|
||||||
}()
|
}()
|
||||||
// If the downloader fails, report an error as in beacon chain mode there
|
// If the downloader fails, report an error as in beacon chain mode there
|
||||||
// should be no errors as long as the chain we're syncing to is valid.
|
// should be no errors as long as the chain we're syncing to is valid.
|
||||||
if err := b.downloader.synchronise(b.started); err != nil {
|
if err := b.downloader.synchronise(started); err != nil {
|
||||||
log.Error("Beacon backfilling failed", "err", err)
|
log.Error("Beacon backfilling failed", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +106,8 @@ func (b *beaconBackfiller) resume() {
|
||||||
b.success()
|
b.success()
|
||||||
}
|
}
|
||||||
log.Debug("Backfilling completed")
|
log.Debug("Backfilling completed")
|
||||||
}()
|
}(b.started)
|
||||||
|
|
||||||
log.Debug("Backfilling started")
|
log.Debug("Backfilling started")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue