aboutsummaryrefslogtreecommitdiffstats
path: root/src/api/dashboard/mod.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-31 12:21:10 +0100
committertv <tv@krebsco.de>2026-10-31 12:21:10 +0100
commit3594477fee41d15437a74a17ec5e07edf6b65518 (patch)
treebb1a3d6463d5ed80fb68e486d5e894a59ecaf7ed /src/api/dashboard/mod.rs
parent23fdb16885d8f4d8a8d95d77d4c5daf1403e0f0f (diff)
dashboard: judge peers's clocks
Diffstat (limited to 'src/api/dashboard/mod.rs')
-rw-r--r--src/api/dashboard/mod.rs43
1 files changed, 33 insertions, 10 deletions
diff --git a/src/api/dashboard/mod.rs b/src/api/dashboard/mod.rs
index c284f6f..310846a 100644
--- a/src/api/dashboard/mod.rs
+++ b/src/api/dashboard/mod.rs
@@ -12,7 +12,10 @@
//! The map: the graph the member list makes (`topology::intended_graph`, as this node
//! sees it), and the links every member measures: subscribed to with LINKS while anyone
//! looks (renewed every `LINKS_EVERY`, ended when nobody does; `swarm::links`), so a
-//! member tells of a change as it happens; the same answers for all clients.
+//! member tells of a change as it happens; the same answers for all clients. With the
+//! clocks the links carry, every member's clock against the swarm's (`clocks`).
+
+mod clocks;
use std::collections::{BTreeMap, BTreeSet};
use std::convert::Infallible;
@@ -271,19 +274,33 @@ async fn build(state: &AppState, graph: &Graph) -> JsonValue {
.iter()
.map(|(member, neighbours)| (member.clone(), json!(neighbours)))
.collect();
+ let own = links::own_links(state).await;
+ let replies: Vec<_> = state
+ .links
+ .replies()
+ .into_iter()
+ .filter(|(_, age, _)| *age <= LINKS_KEPT_FOR)
+ .collect();
let mut measured = Map::new();
measured.insert(
local.to_owned(),
- json!({ "secsAgo": 0, "links": links_json(&links::own_links(state).await) }),
+ json!({ "secsAgo": 0, "links": links_json(&own) }),
);
- for (member, age, member_links) in state.links.replies() {
- if age <= LINKS_KEPT_FOR {
- measured.insert(
- member,
- json!({ "secsAgo": age.as_secs(), "links": links_json(&member_links) }),
- );
- }
+ for (member, age, member_links) in &replies {
+ measured.insert(
+ member.clone(),
+ json!({ "secsAgo": age.as_secs(), "links": links_json(member_links) }),
+ );
}
+ let clocks = clocks::swarm(
+ local,
+ std::iter::once((local, &own[..])).chain(
+ replies
+ .iter()
+ .map(|(member, _, member_links)| (member.as_str(), &member_links[..])),
+ ),
+ u64::try_from(state.clock_skew_warn.as_millis()).unwrap_or(u64::MAX),
+ );
// Everyone on the map: in the member list's graph, or measured.
let mut members_json = Map::new();
for member in graph.intended.keys().chain(measured.keys()) {
@@ -297,7 +314,12 @@ async fn build(state: &AppState, graph: &Graph) -> JsonValue {
"page": page().0,
"stats": stats,
"peers": peers,
- "graph": { "members": members_json, "intended": intended, "measured": measured },
+ "graph": {
+ "members": members_json,
+ "intended": intended,
+ "measured": measured,
+ "clocks": clocks,
+ },
})
}
@@ -311,6 +333,7 @@ fn links_json(links: &[Link]) -> JsonValue {
"relayed": link.relayed,
"rttMs": link.rtt_us.map(|us| f64::from(us) / 1000.0),
"dormant": link.dormant,
+ "clock": link.clock,
"others": link.others.iter().map(|other| json!({
"transport": other.transport,
"rttMs": other.rtt_us.map(|us| f64::from(us) / 1000.0),