diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 0582e842c3..bf85df85e0 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -200,6 +200,32 @@ func WriteLastPivotNumber(db ethdb.KeyValueWriter, pivot uint64) { } } +// ReadPartialSyncComplete reports whether the partial-state initial sync +// completed successfully on this datadir. Returns false if the flag is +// unset or absent (fresh database, non-partial-state node, or sync in +// progress). +func ReadPartialSyncComplete(db ethdb.KeyValueReader) bool { + data, _ := db.Get(partialSyncCompleteKey) + return len(data) > 0 && data[0] == 1 +} + +// WritePartialSyncComplete marks the partial-state initial sync as finished. +// The downloader uses this on restart to skip redundant sync cycles. +func WritePartialSyncComplete(db ethdb.KeyValueWriter) { + if err := db.Put(partialSyncCompleteKey, []byte{1}); err != nil { + log.Crit("Failed to store partial-sync-complete flag", "err", err) + } +} + +// DeletePartialSyncComplete clears the partial-state sync completion flag. +// Used when the node is reset to genesis or rewound behind the pivot so a +// fresh partial sync can run. +func DeletePartialSyncComplete(db ethdb.KeyValueWriter) { + if err := db.Delete(partialSyncCompleteKey); err != nil { + log.Crit("Failed to delete partial-sync-complete flag", "err", err) + } +} + // ReadTxIndexTail retrieves the number of oldest indexed block // whose transaction indices has been indexed. func ReadTxIndexTail(db ethdb.KeyValueReader) *uint64 { diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index 7731e24d1c..2d11c72647 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -104,6 +104,12 @@ var ( // snapSyncStatusFlagKey flags that status of snap sync. snapSyncStatusFlagKey = []byte("SnapSyncStatus") + // partialSyncCompleteKey flags that the partial-state initial sync + // (snap sync + second state sync to HEAD + AdvancePartialHead) has + // finished successfully on this datadir. Consumed by the downloader + // so beaconBackfiller.resume() keeps short-circuiting across restarts. + partialSyncCompleteKey = []byte("PartialSyncComplete") + // Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes). headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td (deprecated) diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 5444f97f2b..ed3531845e 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -268,6 +268,13 @@ func New(stateDb ethdb.Database, mode ethconfig.SyncMode, mux *event.TypeMux, ch stateSyncStart: make(chan *stateSync), syncStartBlock: chain.CurrentSnapBlock().Number.Uint64(), } + // Rehydrate the partial-state completion flag across restarts. Without + // this, a freshly-started process would re-enter the downloader loop for + // every beacon forkchoice update, defeating beaconBackfiller.resume()'s + // short-circuit. + if partialFilter != nil && rawdb.ReadPartialSyncComplete(stateDb) { + dl.partialSyncComplete.Store(true) + } // Create the post-merge skeleton syncer and start the process dl.skeleton = newSkeleton(stateDb, dl.peers, dropPeer, newBeaconBackfiller(dl, success), chain) @@ -1027,6 +1034,9 @@ func (d *Downloader) processSnapSyncContent() error { return err } d.partialSyncComplete.Store(true) + // Persist the completion flag so a restart does not + // re-run the sync cycle on every beacon forkchoice. + rawdb.WritePartialSyncComplete(d.stateDB) log.Info("Partial state initial sync complete") } }