aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/state/peer.rs42
-rw-r--r--src/swarm/peer/handshake.rs2
2 files changed, 38 insertions, 6 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
+ );
}
}
diff --git a/src/swarm/peer/handshake.rs b/src/swarm/peer/handshake.rs
index 1ca955b..769b010 100644
--- a/src/swarm/peer/handshake.rs
+++ b/src/swarm/peer/handshake.rs
@@ -148,7 +148,7 @@ async fn transmit_hello(state: &AppState, peer: &str) -> Result<()> {
if peer_state.hello_state == HelloState::Sending {
topology::change_peer(state, &mut peer_state, |p| p.hello_sent(Instant::now()));
- let wait = peer_state.next_hello_wait();
+ let wait = peer_state.next_hello_wait(Instant::now());
let next_attempt = Instant::now() + wait;
peer_state.hello_state = HelloState::AwaitingReply { next_attempt };
state.peers_due.by(next_attempt);