Skip to main content

tuwunel_service/federation/
mod.rs

1mod execute;
2mod format;
3mod peer;
4mod rank;
5pub mod scheme;
6#[cfg(test)]
7mod tests;
8
9use std::{sync::Arc, time::Duration};
10
11use tuwunel_core::{Result, utils::exponential_backoff_streak_cap};
12use tuwunel_database::Map;
13
14use self::peer::MAX_BACKOFF;
15pub use self::{
16	peer::{Classification, PeerBackoff, ShouldAttempt},
17	rank::{Candidates, WhenAllBackedOff},
18};
19use crate::services::OnceServices;
20
21pub struct Service {
22	services: Arc<OnceServices>,
23	statuses: Arc<Map>,
24
25	/// Width of one peer-status bucket in seconds, aligned with
26	/// `sender_timeout` so the streak (the window span between a peer's oldest
27	/// and newest recorded failure) tracks the sender's `consecutive_failures`
28	/// notion at the cutover.
29	window_secs: u64,
30
31	/// Streak cap = `ceil(sqrt(MAX_BACKOFF / window_secs))`. Past this span the
32	/// quadratic curve `window * n²` saturates at [`MAX_BACKOFF`], so a longer
33	/// streak cannot change the verdict.
34	n_max: u32,
35
36	/// Grace before the first retry of a once-failed peer, snapshot from
37	/// `sender_retry_grace`. Zero disables the grace tier so the plain bucket
38	/// curve governs from the first failure.
39	grace: Duration,
40}
41
42impl crate::Service for Service {
43	fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
44		let window_secs = args.server.config.sender_timeout.max(1);
45		let n_max = exponential_backoff_streak_cap(Duration::from_secs(window_secs), MAX_BACKOFF);
46		let grace = Duration::from_secs(args.server.config.sender_retry_grace);
47
48		Ok(Arc::new(Self {
49			services: args.services.clone(),
50			statuses: args.db["servername_status"].clone(),
51			window_secs,
52			n_max,
53			grace,
54		}))
55	}
56
57	fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
58}