diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/state/peer.rs | 11 | ||||
| -rw-r--r-- | src/swarm/peer/handshake.rs | 11 | ||||
| -rw-r--r-- | src/swarm/peer/worker.rs | 2 | ||||
| -rw-r--r-- | src/swarm/topology/mod.rs | 10 |
4 files changed, 25 insertions, 9 deletions
diff --git a/src/state/peer.rs b/src/state/peer.rs index 063b72b..ba8379a 100644 --- a/src/state/peer.rs +++ b/src/state/peer.rs @@ -146,7 +146,8 @@ pub(crate) struct PeerState { pub(crate) hello_attempts: u32, /// Since when we consider the peer unreachable (three unanswered HELLOs, or /// dormant). Only a message from it clears it (`mark_seen`): until then it is no - /// neighbour candidate, and a member gets probes (`probe_backoff`) instead. + /// neighbour candidate, and a member (by its HELLO_REPLY, or by the member list) gets + /// probes (`probe_backoff`) instead. pub(crate) unreachable_since: Option<Instant>, /// What went wrong with this peer so far (counted only, for /peers and /admin/stats). @@ -352,8 +353,12 @@ impl PeerState { /// No more HELLO retries (the peer isn't our neighbour any more). A HELLO in flight /// finishes; a later handshake starts over. An unreachable member keeps its probes: /// it is no neighbour because it is unreachable, and only its answer can change that. - pub(crate) fn stop_hello_retries(&mut self) { - let probed = self.is_unreachable() && matches!(self.standing, Standing::Member { .. }); + /// A member is one whose HELLO_REPLY said so, or one `listed` in the member list: after + /// a restart, none has answered yet, and members that restart together may each fail + /// to reach the others at first; without probes, none would try again. + pub(crate) fn stop_hello_retries(&mut self, listed: bool) { + let probed = + self.is_unreachable() && (listed || matches!(self.standing, Standing::Member { .. })); if !probed && matches!(self.hello_state, HelloState::AwaitingReply { .. }) { self.hello_state = HelloState::NotSent; } diff --git a/src/swarm/peer/handshake.rs b/src/swarm/peer/handshake.rs index 072545b..1ca955b 100644 --- a/src/swarm/peer/handshake.rs +++ b/src/swarm/peer/handshake.rs @@ -330,15 +330,16 @@ pub(crate) async fn probe_when_reachable(state: AppState, reachable: Arc<tokio:: /// Brings `peer`'s next probe forward to now, if it is a member we can't reach (dormant, /// or unanswered HELLOs) whose probe is pending: the peer worker sends it (woken for -/// it). Whether it was. +/// it). A member by its HELLO_REPLY, or by the member list (`stop_hello_retries`). +/// Whether it was. async fn probe_now(state: &AppState, peer: &str) -> bool { let Some(peer_state) = state.get_peer_state(peer).await else { return false; }; let mut peer_state = peer_state.lock().await; - if !matches!(peer_state.standing, Standing::Member { .. }) - || !(peer_state.liveness.is_dormant() || peer_state.is_unreachable()) - { + let member = + matches!(peer_state.standing, Standing::Member { .. }) || topology::is_listed(state, peer); + if !member || !(peer_state.liveness.is_dormant() || peer_state.is_unreachable()) { return false; } match &mut peer_state.hello_state { @@ -372,7 +373,7 @@ pub(crate) async fn apply_neighbour_change(state: &AppState, change: topology::N for peer in &change.removed { if let Some(peer_state) = state.get_peer_state(peer).await { let mut peer_state = peer_state.lock().await; - peer_state.stop_hello_retries(); + peer_state.stop_hello_retries(topology::is_listed(state, peer)); peer_state.reset_sync(); } } diff --git a/src/swarm/peer/worker.rs b/src/swarm/peer/worker.rs index 6196571..a803922 100644 --- a/src/swarm/peer/worker.rs +++ b/src/swarm/peer/worker.rs @@ -99,7 +99,7 @@ pub(crate) async fn run_peer_worker(state: AppState) { None }; if !neighbour { - peer_state.stop_hello_retries(); + peer_state.stop_hello_retries(topology::is_listed(&state, peer)); } let unanswered = peer_state.take_overdue_sync_reply(now); (became_dormant, keepalive, unanswered) diff --git a/src/swarm/topology/mod.rs b/src/swarm/topology/mod.rs index 992c448..e5dacdd 100644 --- a/src/swarm/topology/mod.rs +++ b/src/swarm/topology/mod.rs @@ -113,6 +113,16 @@ pub(crate) fn summary(state: &AppState) -> Option<Summary> { }) } +/// Whether `peer` is in the member list the neighbours were computed from (false until +/// the view is known). +pub(crate) fn is_listed(state: &AppState, peer: &str) -> bool { + state + .neighbours + .read_or_recover() + .as_ref() + .is_some_and(|view| view.members.iter().any(|member| member == peer)) +} + /// Whether we initiate contact with `peer`: HELLOs, keepalives, dormancy checks, sync /// openings. Everyone is a neighbour until the view is known (while joining, or before /// any member registered), so joining works as in a full mesh. We always *answer* |
