//! The intervals the protocol runs on, derived from the keepalive. use std::time::Duration; use crate::backoff::Backoff; /// First wait for a sync answer; generous, since a transport may take a while with large /// messages (Mycelium sends them in many small packets). const SYNC_REPLY_TIMEOUT: Duration = Duration::from_secs(10); /// Upper bound, as a multiple of the first wait: 5 minutes, about as long as a transport /// tries to deliver a message (`Transport::delivery_limit`). const SYNC_REPLY_TIMEOUT_MAX_FACTOR: u32 = 30; /// Upper bound on the wait between probes of an unreachable peer, as a multiple of /// `dormant_probe`: an hour at the default keepalive (1m). A returning node HELLOs its own /// neighbours, so this only bounds how long one we never hear from stays out. const PROBE_MAX_FACTOR: u32 = 6; #[derive(Clone, Copy, Debug)] pub(crate) struct Timing { pub(crate) keepalive: Duration, /// How often a quiet pair's keepalive is a STATUS rather than a PING /// (`PeerState::take_keepalive_due`): its state (heads, readiness, epoch) for the heal /// and against lost announcements. Changes go out as they happen anyway. pub(crate) status_refresh: Duration, pub(crate) dormant_after: Duration, pub(crate) resync_after: Duration, pub(crate) dormant_probe: Duration, /// While catching up: send the source a STATUS after this much silence (`swarm::source`). pub(crate) catch_up_probe: Duration, /// First wait for an answer to a sync message before sending again; doubles while /// answers stay out (`SyncSession::reply_backoff`). pub(crate) sync_reply_timeout: Duration, /// How long the transport tries to deliver a message before dropping it /// (`Transport::delivery_limit`): a peer silent for longer has nothing on the way to /// us any more. pub(crate) delivery_limit: Duration, } impl Timing { /// How long a round-trip sample stays meaningful (`swarm::rtt`): a few keepalive /// intervals, since nodes move. pub(crate) fn rtt_fresh_for(&self) -> Duration { self.keepalive * 5 } /// The timings for `--keepalive`. /// /// Each node keeps about `k` neighbours (`--neighbours`) whatever the swarm's size, /// and only one side of a pair sends them, so idle, a node sends about `k` messages and /// receives about `k` per keepalive interval (more on a stable member others prefer): /// PINGs and PONGs (a few bytes), every third a STATUS and STATUS_REPLY (heads, /// readiness, epoch) instead. The interval trades that against how soon a silent peer is /// noticed (`dormant_after`, 4 intervals) and a gap closes (within an interval). /// Recommended: /// /// | Environment | Keepalive | Notes | /// |---|---|---| /// | Tests | 250 ms – 2 s | dormancy within seconds keeps tests short | /// | LAN, servers | 15–30 s | a returning or silent peer is noticed within a minute or two | /// | WAN, mobile, metered (the default, 1m) | 60–120 s | about 0.4 messages per second per node at `k` = 12 and 1m | /// /// `delivery_limit`: the transport's (`Transport::delivery_limit`). pub(crate) fn from_keepalive(keepalive: Duration, delivery_limit: Duration) -> Self { Self::new(keepalive, SYNC_REPLY_TIMEOUT, delivery_limit) } /// [`from_keepalive`](Self::from_keepalive) with another first sync reply timeout: /// for tests, whose messages don't travel for seconds. pub(crate) fn new( keepalive: Duration, sync_reply_timeout: Duration, delivery_limit: Duration, ) -> Self { Self { keepalive, // Every third keepalive; with `resync_after` at two keepalives, a heal needs // two STATUSes that differ: within about two refreshes. status_refresh: keepalive * 3, dormant_after: keepalive * 4, // dormant_after >= 4* keepalive to tolerate three lost keepalives in a row // >= 2 keepalives, so a second exchange confirms the difference; and >= twice // the first sync reply timeout, so a lost message's sender resends it first. resync_after: (keepalive * 2).max(sync_reply_timeout * 2), dormant_probe: keepalive * 10, // >= 10*: dormant peers cost almost nothing, but still rejoin within minutes // Seconds, not a keepalive interval: a dead source should cost little time. // Only the source gets these, and only while catching up. catch_up_probe: (keepalive / 30).clamp(Duration::from_secs(1), Duration::from_secs(5)), sync_reply_timeout, delivery_limit, } } /// Waits between probes of an unreachable peer: `dormant_probe`, doubling up to /// `PROBE_MAX_FACTOR` times as much. pub(crate) fn probe_backoff(&self) -> Backoff { Backoff::new(self.dormant_probe, self.dormant_probe * PROBE_MAX_FACTOR) } /// Waits for sync answers: `sync_reply_timeout`, doubling up to 30 times as much. pub(crate) fn sync_reply_backoff(&self) -> Backoff { Backoff::new( self.sync_reply_timeout, self.sync_reply_timeout * SYNC_REPLY_TIMEOUT_MAX_FACTOR, ) } }