From c2468964b11bd3606f03375c370fd181a1723113 Mon Sep 17 00:00:00 2001 From: tv Date: Sat, 24 Oct 2026 04:20:21 +0200 Subject: find reachable neighbours --- src/state.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'src/state.rs') diff --git a/src/state.rs b/src/state.rs index 64fe6a4..7574e35 100644 --- a/src/state.rs +++ b/src/state.rs @@ -137,8 +137,18 @@ pub(crate) struct PeerState { /// How many heads its most recent keepalive listed (None if it sent a digest). pub(crate) reported_heads: Option, + + /// HELLOs sent in the current handshake attempt without hearing anything back. + pub(crate) hello_attempts: u32, + /// Since when we consider the peer unreachable (three unanswered HELLOs, or + /// dormant). Cleared by any message from it; ignored after `dormant_probe`, so the + /// peer becomes a neighbour candidate again and gets tried once more. + pub(crate) unreachable_since: Option, } +/// Unanswered HELLOs after which a peer counts as unreachable (1 + 2 + 4 s apart). +const UNREACHABLE_AFTER_HELLOS: u32 = 3; + /// One Automerge sync with a peer, from a reset to the next. Everything a reset must /// forget lives here, so a reset replaces it whole and can't miss a field. #[derive(Clone)] @@ -284,6 +294,8 @@ impl PeerState { reports_ready: None, syncs_with_us: None, reported_heads: None, + hello_attempts: 0, + unreachable_since: None, } } @@ -309,6 +321,8 @@ impl PeerState { /// Records inbound activity. Returns true if the peer was dormant. pub(crate) fn mark_seen(&mut self, now: Instant) -> bool { self.last_seen = Some(now); + self.hello_attempts = 0; + self.unreachable_since = None; if matches!(self.liveness, Liveness::Dormant { .. }) { self.liveness = Liveness::Active; @@ -321,6 +335,21 @@ impl PeerState { false } + /// A HELLO went out (or failed to). After the third unanswered one, the peer counts + /// as unreachable. + pub(crate) fn hello_sent(&mut self, now: Instant) { + self.hello_attempts += 1; + if self.hello_attempts >= UNREACHABLE_AFTER_HELLOS { + self.unreachable_since.get_or_insert(now); + } + } + + /// Whether to leave the peer out when choosing neighbours (`swarm::topology`). + pub(crate) fn is_unreachable(&self, timing: &Timing, now: Instant) -> bool { + self.unreachable_since + .is_some_and(|since| now.saturating_duration_since(since) < timing.dormant_probe) + } + /// No more HELLO retries (the peer isn't our neighbour any more). A HELLO in flight /// finishes; a later handshake starts over. pub(crate) fn stop_hello_retries(&mut self) { @@ -336,6 +365,7 @@ impl PeerState { } self.liveness = Liveness::Dormant { since: now }; + self.unreachable_since.get_or_insert(now); self.reset_sync(); if self.hello_state != HelloState::Sending { -- cgit v1.3.1