From caf09e234a4bd7a330c0595bfea932d5ebfa865f Mon Sep 17 00:00:00 2001 From: tv Date: Sun, 25 Oct 2026 23:35:18 +0100 Subject: split state module --- src/state/timing.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/state/timing.rs (limited to 'src/state/timing.rs') diff --git a/src/state/timing.rs b/src/state/timing.rs new file mode 100644 index 0000000..406e207 --- /dev/null +++ b/src/state/timing.rs @@ -0,0 +1,75 @@ +//! 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 Mycelium delivers large messages in +/// many small packets. +const SYNC_REPLY_TIMEOUT: Duration = Duration::from_secs(10); +/// Upper bound, as a multiple of the first wait: 5 minutes (Mycelium gives up on a +/// message after five minutes). +const SYNC_REPLY_TIMEOUT_MAX_FACTOR: u32 = 30; +/// How long Mycelium tries to deliver a message before dropping it. +const MYCELIUM_DELIVERY_LIMIT: Duration = Duration::from_secs(300); + +#[derive(Clone, Copy, Debug)] +pub(crate) struct Timing { + pub(crate) keepalive: Duration, + pub(crate) dormant_after: Duration, + pub(crate) resync_after: Duration, + pub(crate) dormant_probe: Duration, + /// While catching up: PING the source 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 (Mycelium: + /// five minutes): 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 + } + + // Recommended values + // Environment | Keepalive | Notes + // --------------------------------------------+-----------+------------------------------------------------------------ + // Test harness | 1-2 s | Dormancy after about 4–8 s and healing within seconds keep tests short. + // LAN or small swarm (up to ~50 nodes) | 15-30 s | Under 3.5 messages/s per node at 50 nodes and 30 s. Current setting. + // WAN, mobile or metered links, or 100+ nodes | 60-120 s | At 100 nodes and 30 s it’s about 6–7 messages/s per node. Fine, but rarely worth it on battery or metered links. + // Several hundred nodes or more | - | The full mesh becomes the problem in itself, not the interval. Message counts grow O(N²) across the swarm, which calls for partial views (SWIM-style random probing and syncing with a subset of peers). + pub(crate) fn from_keepalive(keepalive: Duration) -> Self { + Self::new(keepalive, SYNC_REPLY_TIMEOUT) + } + + /// [`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) -> Self { + Self { + keepalive, + dormant_after: keepalive * 4, // dormant_after >= 4* keepalive to tolerate three lost PINGs 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: MYCELIUM_DELIVERY_LIMIT, + } + } + + /// 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, + ) + } +} -- cgit v1.3.1