Skip to main content

tuwunel_service/federation/
mod.rs

1//! Sends federation requests and tracks per-peer reachability.
2//!
3//! The service resolves and signs outbound requests, records selected outcomes,
4//! ranks fallback candidates, and exposes bounded multi-destination fanout.
5
6mod execute;
7pub mod feds;
8mod format;
9mod peer;
10mod rank;
11pub mod scheme;
12#[cfg(test)]
13mod tests;
14
15use std::{sync::Arc, time::Duration};
16
17use tuwunel_core::{Result, utils::exponential_backoff_streak_cap};
18use tuwunel_database::Map;
19
20use self::peer::MAX_BACKOFF;
21/// Re-exports peer reachability verdicts and candidate-ranking types.
22///
23/// These items classify failures, expose retry eligibility, and preserve the
24/// ranking policy shared by federation request paths.
25pub use self::{
26	peer::{Classification, PeerBackoff, ShouldAttempt, is_content_rejection},
27	rank::{Candidates, WhenAllBackedOff},
28};
29use crate::services::OnceServices;
30
31/// Executes outbound federation traffic and maintains peer status.
32///
33/// Request entry points choose whether to consult or update reachability state.
34/// Fanout and candidate-ranking helpers build on the same transport policy.
35pub struct Service {
36	services: Arc<OnceServices>,
37	statuses: Arc<Map>,
38
39	/// Width of one peer-status bucket in seconds, aligned with
40	/// `sender_timeout` so the streak (the window span between a peer's oldest
41	/// and newest recorded failure) tracks the sender's `consecutive_failures`
42	/// notion at the cutover.
43	window_secs: u64,
44
45	/// Streak cap = `ceil(sqrt(MAX_BACKOFF / window_secs))`. Past this span the
46	/// quadratic curve `window * n²` saturates at [`MAX_BACKOFF`], so a longer
47	/// streak cannot change the verdict.
48	n_max: u32,
49
50	/// Grace before the first retry of a once-failed peer, snapshot from
51	/// `sender_retry_grace`. Zero disables the grace tier so the plain bucket
52	/// curve governs from the first failure.
53	grace: Duration,
54}
55
56impl crate::Service for Service {
57	fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
58		let window_secs = args.server.config.sender_timeout.max(1);
59		let n_max = exponential_backoff_streak_cap(Duration::from_secs(window_secs), MAX_BACKOFF);
60		let grace = Duration::from_secs(args.server.config.sender_retry_grace);
61
62		Ok(Arc::new(Self {
63			services: args.services.clone(),
64			statuses: args.db["servername_status"].clone(),
65			window_secs,
66			n_max,
67			grace,
68		}))
69	}
70
71	fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
72}