diff options
| author | tv <tv@krebsco.de> | 2026-10-28 11:57:54 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-28 11:57:54 +0100 |
| commit | e13d027567f781c9729a518913b21ec4ee273039 (patch) | |
| tree | ada25e93f6164e0ce80282616937f19fc8f1aec0 /src/state/peer.rs | |
| parent | 8a4e5bab13069a298fa1d8c756e1a6c0bd719dfb (diff) | |
peers from member list start as unknown
Diffstat (limited to 'src/state/peer.rs')
| -rw-r--r-- | src/state/peer.rs | 39 |
1 files changed, 33 insertions, 6 deletions
diff --git a/src/state/peer.rs b/src/state/peer.rs index b723ccd..9f2da0f 100644 --- a/src/state/peer.rs +++ b/src/state/peer.rs @@ -39,6 +39,11 @@ impl Standing { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub(crate) enum Liveness { + /// Nothing heard from it since this node started: known only from the member list + /// or another peer. It counts as active (sync, keepalives and dormancy treat it so) + /// until its first message makes it active, or `Timing::dormant_after` of silence + /// dormant. + Unknown, Active, /// No inbound message for Timing::dormant_after. Kept indefinitely, probed slowly. Dormant { @@ -46,6 +51,12 @@ pub(crate) enum Liveness { }, } +impl Liveness { + pub(crate) fn is_dormant(self) -> bool { + matches!(self, Self::Dormant { .. }) + } +} + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub(crate) enum HelloState { #[default] @@ -184,7 +195,7 @@ impl PeerState { hello_backoff: Backoff::new(HELLO_RETRY, HELLO_RETRY_MAX), probe_backoff: timing.probe_backoff(), sync: SyncSession::new(timing.sync_reply_backoff()), - liveness: Liveness::Active, + liveness: Liveness::Unknown, added_at: Instant::now(), last_seen: None, last_keepalive_at: None, @@ -219,7 +230,7 @@ impl PeerState { /// An overdue sync answer from an established, active peer (see /// `SyncSession::take_overdue_reply`). pub(crate) fn take_overdue_sync_reply(&mut self, now: Instant) -> Option<usize> { - if self.liveness != Liveness::Active || self.hello_state != HelloState::Established { + if self.liveness.is_dormant() || self.hello_state != HelloState::Established { return None; } let overdue = self.sync.take_overdue_reply(now); @@ -240,6 +251,9 @@ impl PeerState { self.unreachable_since = None; self.probe_backoff.reset(); + if self.liveness == Liveness::Unknown { + self.liveness = Liveness::Active; + } if let Liveness::Dormant { since } = self.liveness { self.incidents.dormant_ms += now.saturating_duration_since(since).as_millis() as u64; self.liveness = Liveness::Active; @@ -287,7 +301,7 @@ impl PeerState { /// Moves a silent peer to Dormant. Returns true on the transition. pub(crate) fn check_dormancy(&mut self, timing: &Timing, now: Instant) -> bool { - if self.liveness != Liveness::Active || self.quiet_for(now) < timing.dormant_after { + if self.liveness.is_dormant() || self.quiet_for(now) < timing.dormant_after { return false; } @@ -340,7 +354,7 @@ impl PeerState { } else { timing.keepalive + timing.keepalive / 2 }; - let due = self.liveness == Liveness::Active + let due = !self.liveness.is_dormant() && self.hello_state == HelloState::Established && now.saturating_duration_since(self.added_at) >= timing.keepalive && self.quiet_for(now) >= quiet_enough @@ -387,7 +401,7 @@ impl PeerState { // While an answer to our last sync message is due, a lost message is the reply // timeout's to resend; healing is for when neither side has anything outstanding. - if self.liveness != Liveness::Active + if self.liveness.is_dormant() || self.hello_state != HelloState::Established || self.sync.reply.is_some() { @@ -413,7 +427,7 @@ impl PeerState { /// Heard from recently. A returning node counts as alive from its first message, /// before the handshake finishes, so a swarm-wide forget can't make it flap. pub(crate) fn is_alive(&self, timing: &Timing, now: Instant) -> bool { - self.liveness == Liveness::Active + !self.liveness.is_dormant() && self .last_seen .is_some_and(|t| now.saturating_duration_since(t) < timing.dormant_after) @@ -424,6 +438,19 @@ impl PeerState { mod tests { use super::*; + /// Unknown until heard from; silent long enough, dormant like an active peer. + #[test] + fn unknown_until_heard_from() { + let timing = Timing::from_keepalive(Duration::from_secs(60), Duration::from_secs(300)); + let mut heard = PeerState::new(true, &timing); + assert_eq!(heard.liveness, Liveness::Unknown); + assert!(!heard.mark_seen(heard.added_at), "no re-handshake: none yet"); + assert_eq!(heard.liveness, Liveness::Active); + let mut silent = PeerState::new(true, &timing); + assert!(silent.check_dormancy(&timing, silent.added_at + timing.dormant_after)); + assert!(silent.liveness.is_dormant()); + } + /// The two sides of a pair take turns: each sends every other interval. #[test] fn keepalives_take_turns() { |
