From ddfcf2d396ab20fb41d96809f3df437a2ecd925a Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Sat, 7 Sep 2024 22:11:01 +0800 Subject: [PATCH] Add `pending_state_sync_attempt` There is an edge case where the finalized block notification is received, but the conditions required to initiate the state sync are not fully met. In such cases, state sync would fail to start as expected and remain stalled. This fixes it by storing the pending attempt and trying to start it later. --- .../network/sync/src/strategy/chain_sync.rs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/substrate/client/network/sync/src/strategy/chain_sync.rs b/substrate/client/network/sync/src/strategy/chain_sync.rs index fb0a2ab962e0a..719cd8812d88e 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync.rs @@ -254,6 +254,14 @@ pub struct ChainSync { /// A set of hashes of blocks that are being downloaded or have been /// downloaded and are queued for import. queue_blocks: HashSet, + /// A pending attempt to start the state sync. + /// + /// The initiation of state sync may be deferred in cases where other conditions + /// are not yet met when the finalized block notification is received, such as + /// when `queue_blocks` is not empty or there are no peers. This field holds the + /// necessary information to attempt the state sync at a later point when + /// conditions are satisfied. + pending_state_sync_attempt: Option<(B::Hash, NumberFor, bool)>, /// Fork sync targets. fork_targets: HashMap>, /// A set of peers for which there might be potential block requests @@ -376,6 +384,7 @@ where extra_justifications: ExtraRequests::new("justification", metrics_registry), mode, queue_blocks: Default::default(), + pending_state_sync_attempt: None, fork_targets: Default::default(), allowed_requests: Default::default(), max_parallel_downloads, @@ -1013,8 +1022,12 @@ where }); if let ChainSyncMode::LightState { skip_proofs, .. } = &self.mode { - if self.state_sync.is_none() && !self.peers.is_empty() && self.queue_blocks.is_empty() { - self.attempt_state_sync(*hash, number, *skip_proofs) + if self.state_sync.is_none() { + if !self.peers.is_empty() && self.queue_blocks.is_empty() { + self.attempt_state_sync(*hash, number, *skip_proofs) + } else { + self.pending_state_sync_attempt.replace((*hash, number, *skip_proofs)) + } } } @@ -1886,6 +1899,12 @@ where /// Get pending actions to perform. #[must_use] pub fn actions(&mut self) -> impl Iterator> { + if !self.peers.is_empty() && self.queue_blocks.is_empty() { + if let Some((hash, number, skip_proofs)) = self.pending_state_sync_attempt.take() { + self.attempt_state_sync(hash, number, skip_proofs); + } + } + let block_requests = self .block_requests() .into_iter()