aboutsummaryrefslogtreecommitdiffstats
path: root/src/swarm/links.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-31 12:18:52 +0100
committertv <tv@krebsco.de>2026-10-31 12:18:52 +0100
commit8d0dc5de241882fac9f8b821d758b038ba1ebd7a (patch)
tree6fab63f6a088e06c75c68ef083f5c863e8c913de /src/swarm/links.rs
parent6c48039bbbc2c6bd564237920fd58d20d8bcde76 (diff)
links replies carry clock offsets
Diffstat (limited to 'src/swarm/links.rs')
-rw-r--r--src/swarm/links.rs27
1 files changed, 24 insertions, 3 deletions
diff --git a/src/swarm/links.rs b/src/swarm/links.rs
index 7585d3b..3e4d717 100644
--- a/src/swarm/links.rs
+++ b/src/swarm/links.rs
@@ -25,7 +25,7 @@ use tracing::debug;
use crate::locks::MutexExt;
use crate::state::AppState;
use crate::swarm::{members, topology};
-use crate::transport::paths;
+use crate::transport::paths::{self, ClockOffset};
use crate::transport::{
Connection, Destination, Link, LinksReply, LinksRequest, OtherPath, ParsedMessage,
};
@@ -128,6 +128,7 @@ pub(crate) async fn own_links(state: &AppState) -> Vec<Link> {
.rtt(&peer)
.map(|rtt| u32::try_from(rtt.as_micros()).unwrap_or(u32::MAX)),
dormant,
+ clock: state.transport.clock_offset(&peer),
peer,
});
}
@@ -367,9 +368,16 @@ pub(crate) async fn watch(state: AppState) {
}
/// Whether `new` says something `old` didn't: another peer, transport, relay or
-/// dormancy, or a round trip measured or moved by more than a fifth (and 1 ms). Round
-/// trips wobble; small moves aren't news.
+/// dormancy, a round trip measured or moved by more than a fifth (and 1 ms), or a clock
+/// measured or moved beyond both bounds. Round trips and clock samples wobble; small
+/// moves aren't news.
fn changed(old: &[Link], new: &[Link]) -> bool {
+ let clock_moved = |a: Option<ClockOffset>, b: Option<ClockOffset>| match (a, b) {
+ (Some(a), Some(b)) => {
+ a.offset_ms.abs_diff(b.offset_ms) > a.within_ms.saturating_add(b.within_ms)
+ }
+ (a, b) => a.is_some() != b.is_some(),
+ };
let moved = |a: Option<u32>, b: Option<u32>| match (a, b) {
(Some(a), Some(b)) => {
let by = a.abs_diff(b);
@@ -384,6 +392,7 @@ fn changed(old: &[Link], new: &[Link]) -> bool {
|| a.relayed != b.relayed
|| a.dormant != b.dormant
|| moved(a.rtt_us, b.rtt_us)
+ || clock_moved(a.clock, b.clock)
|| a.others.len() != b.others.len()
|| a.others.iter().zip(&b.others).any(|(x, y)| {
x.transport != y.transport
@@ -405,6 +414,7 @@ mod tests {
rtt_us,
dormant: false,
others: Vec::new(),
+ clock: None,
}
}
@@ -421,5 +431,16 @@ mod tests {
assert!(changed(&old, &[dormant]));
// Small round trips: a millisecond's wobble isn't news.
assert!(!changed(&[link("a", Some(400))], &[link("a", Some(1_300))]));
+ // A clock: news when measured, and when it moved beyond both bounds.
+ let clocked = |offset_ms| Link {
+ clock: Some(ClockOffset {
+ offset_ms,
+ within_ms: 50,
+ }),
+ ..link("a", Some(10_000))
+ };
+ assert!(changed(&old, &[clocked(0)]));
+ assert!(!changed(&[clocked(0)], &[clocked(80)]));
+ assert!(changed(&[clocked(0)], &[clocked(5_000)]));
}
}