diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/config/mod.rs | 13 | ||||
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/state/timing.rs | 21 | ||||
| -rw-r--r-- | src/units.rs | 11 |
4 files changed, 43 insertions, 5 deletions
diff --git a/src/config/mod.rs b/src/config/mod.rs index 85d71d2..99690ae 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -229,6 +229,13 @@ pub(crate) const OPTIONS: &[OptionSpec] = &[ "Keepalive interval; dormancy, resync and probe timings derive from it.", ), option( + "dormant-probe-max", + Kind::Duration, + "DURATION", + None, + "Longest wait between HELLOs to a member that doesn't answer (dormant, or unanswered HELLOs): they start at 10 keepalives and double up to this. It bounds how long a returning member, or the other side of a partition, takes to be noticed. Default: 60 keepalives (1h at 1m). Shorter than 10 keepalives, it is every wait.", + ), + option( "neighbours", Kind::Integer, "COUNT", @@ -443,6 +450,12 @@ pub(crate) struct Settings { pub(crate) alias: Option<String>, #[serde(default = "default_keepalive", with = "crate::units::duration_option")] pub(crate) keepalive: Duration, + #[serde( + default, + deserialize_with = "crate::units::optional_duration", + serialize_with = "crate::units::serialize_optional_duration" + )] + pub(crate) dormant_probe_max: Option<Duration>, #[serde(default = "default_neighbours")] pub(crate) neighbours: usize, #[serde( diff --git a/src/main.rs b/src/main.rs index 1cfd173..7481f38 100644 --- a/src/main.rs +++ b/src/main.rs @@ -253,7 +253,8 @@ async fn main() -> Result<()> { initial_epoch, initial_doc, earlier_actors, - timing: state::Timing::from_keepalive(args.keepalive, delivery_limit), + timing: state::Timing::from_keepalive(args.keepalive, delivery_limit) + .with_probe_max(args.dormant_probe_max), neighbours_k: args.neighbours, alias: args.alias.clone(), electorate_window: args.electorate_window, diff --git a/src/state/timing.rs b/src/state/timing.rs index 9055ed6..988e430 100644 --- a/src/state/timing.rs +++ b/src/state/timing.rs @@ -11,8 +11,10 @@ const SYNC_REPLY_TIMEOUT: Duration = Duration::from_secs(10); /// 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. +/// `dormant_probe`, unless `--dormant-probe-max` says otherwise: an hour at the default +/// keepalive (1m). A returning node HELLOs its own neighbours, and once one pair across +/// a partition meets, the rest are probed at once (`handshake::probe_now`), so this +/// bounds how long one we never hear from stays out. const PROBE_MAX_FACTOR: u32 = 6; #[derive(Clone, Copy, Debug)] @@ -25,6 +27,8 @@ pub(crate) struct Timing { pub(crate) dormant_after: Duration, pub(crate) resync_after: Duration, pub(crate) dormant_probe: Duration, + /// The longest wait between probes (`probe_backoff`). + pub(crate) probe_max: 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 @@ -81,6 +85,7 @@ impl Timing { // 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 + probe_max: keepalive * 10 * PROBE_MAX_FACTOR, // 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)), @@ -89,10 +94,18 @@ impl Timing { } } + /// `--dormant-probe-max`, if given: the longest wait between probes. + pub(crate) fn with_probe_max(mut self, max: Option<Duration>) -> Self { + if let Some(max) = max { + self.probe_max = max; + } + self + } + /// Waits between probes of an unreachable peer: `dormant_probe`, doubling up to - /// `PROBE_MAX_FACTOR` times as much. + /// `probe_max` (all of them `probe_max` if that is shorter). pub(crate) fn probe_backoff(&self) -> Backoff { - Backoff::new(self.dormant_probe, self.dormant_probe * PROBE_MAX_FACTOR) + Backoff::new(self.dormant_probe.min(self.probe_max), self.probe_max) } /// Waits for sync answers: `sync_reply_timeout`, doubling up to 30 times as much. diff --git a/src/units.rs b/src/units.rs index ac08ebf..28adab9 100644 --- a/src/units.rs +++ b/src/units.rs @@ -171,6 +171,17 @@ pub(crate) fn optional_duration<'de, D: serde::Deserializer<'de>>( duration_option::deserialize(d).map(Some) } +/// Writes an optional `Duration` as [`duration_option`] does; `None` as null. +pub(crate) fn serialize_optional_duration<S: serde::Serializer>( + value: &Option<Duration>, + s: S, +) -> Result<S::Ok, S::Error> { + match value { + Some(value) => duration_option::serialize(value, s), + None => s.serialize_none(), + } +} + /// Serde for a size option: a string as [`parse_size`] reads it, or a number of bytes; /// written as [`format_size`]. pub(crate) mod size_option { |
