diff options
Diffstat (limited to 'src/state/peer.rs')
| -rw-r--r-- | src/state/peer.rs | 42 |
1 files changed, 37 insertions, 5 deletions
diff --git a/src/state/peer.rs b/src/state/peer.rs index ba8379a..24f9188 100644 --- a/src/state/peer.rs +++ b/src/state/peer.rs @@ -76,6 +76,11 @@ pub(crate) enum HelloState { /// comes. const HELLO_RETRY: Duration = Duration::from_secs(1); const HELLO_RETRY_MAX: Duration = Duration::from_secs(30); +/// How long after it became known (`added_at`) a peer never heard from is HELLOed that +/// often, unreachable or not, rather than probed (`dormant_probe`, ten keepalives): +/// members that restart together may fail to reach each other at first, as the others +/// aren't up yet, and would otherwise wait for a probe to find each other. +const FIRST_CONTACT: Duration = Duration::from_secs(300); #[derive(Clone)] pub(crate) struct PeerState { @@ -341,9 +346,12 @@ impl PeerState { ) } - /// The wait before the next HELLO: a probe's if unreachable. - pub(crate) fn next_hello_wait(&mut self) -> Duration { - if self.is_unreachable() { + /// The wait before the next HELLO: a probe's if unreachable, but while a peer never + /// heard from is new (`FIRST_CONTACT`). + pub(crate) fn next_hello_wait(&mut self, now: Instant) -> Duration { + let new = self.last_seen.is_none() + && now.saturating_duration_since(self.added_at) < FIRST_CONTACT; + if self.is_unreachable() && !new { self.probe_backoff.next() } else { self.hello_backoff.next() @@ -691,7 +699,9 @@ mod tests { } assert!(peer.is_unreachable()); - let probes: Vec<Duration> = (0..4).map(|_| peer.next_hello_wait()).collect(); + // Known for a while: probed. + let later = start + FIRST_CONTACT; + let probes: Vec<Duration> = (0..4).map(|_| peer.next_hello_wait(later)).collect(); let probe = timing.dormant_probe; assert_eq!(probes, [probe, probe * 2, probe * 4, probe * 6]); peer.hello_sent(start + probe * 20); @@ -702,6 +712,28 @@ mod tests { peer.hello_sent(start + probe * 22); peer.hello_sent(start + probe * 22); peer.hello_sent(start + probe * 22); - assert_eq!(peer.next_hello_wait(), probe); + assert_eq!(peer.next_hello_wait(start + probe * 22), probe); + } + + /// A peer never heard from is HELLOed at the HELLO backoff while it is new + /// (`FIRST_CONTACT`), unreachable or not; probed after. + #[test] + fn new_peers_retried_before_probed() { + let timing = Timing::from_keepalive(Duration::from_secs(60), Duration::from_secs(300)); + let mut peer = PeerState::new(true, &timing); + let start = peer.added_at; + for _ in 0..UNREACHABLE_AFTER_HELLOS { + peer.hello_sent(start); + } + assert!(peer.is_unreachable()); + let waits: Vec<Duration> = (0..7).map(|_| peer.next_hello_wait(start)).collect(); + assert!( + waits.iter().all(|wait| *wait <= HELLO_RETRY_MAX), + "{waits:?}" + ); + assert_eq!( + peer.next_hello_wait(start + FIRST_CONTACT), + timing.dormant_probe + ); } } |
