aboutsummaryrefslogtreecommitdiffstats
path: root/src/swarm/topology
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-03 05:20:59 +0100
committertv <tv@krebsco.de>2026-11-03 05:20:59 +0100
commit96d7744dd29a3c783b43a7a6697af7d5d5719a10 (patch)
treeddc154200b6086f5f9832237b6a620670ba8b4c3 /src/swarm/topology
parent9ee8bae8cd740fc038ac6ca14eae4fad458cda31 (diff)
add writer trees
Diffstat (limited to 'src/swarm/topology')
-rw-r--r--src/swarm/topology/mod.rs56
1 files changed, 55 insertions, 1 deletions
diff --git a/src/swarm/topology/mod.rs b/src/swarm/topology/mod.rs
index 1c1a6c4..b876fd0 100644
--- a/src/swarm/topology/mod.rs
+++ b/src/swarm/topology/mod.rs
@@ -26,6 +26,7 @@
//! transport. With one transport, it is the one graph as before.
use std::collections::{BTreeMap, BTreeSet};
+use std::sync::Arc;
use tracing::info;
@@ -58,6 +59,42 @@ pub(crate) struct NeighbourView {
neighbours: BTreeSet<String>,
/// Every reachable member is a neighbour.
full_mesh: bool,
+ /// The graph the member list gives, as if every member reached every other
+ /// (`member_list_graph`): computed when first asked for, kept while the member list
+ /// stays the same.
+ whole: std::sync::OnceLock<Arc<BTreeMap<String, BTreeSet<String>>>>,
+}
+
+/// Every member's neighbours as the member list gives them, as if every member reached
+/// every other: the same on every node that knows the same member list, whatever each
+/// reaches (`swarm::tree` builds writers' trees on it). On each transport among the
+/// members on it, all together. None until the view is known.
+pub(crate) fn member_list_graph(
+ state: &AppState,
+) -> Option<Arc<BTreeMap<String, BTreeSet<String>>>> {
+ let view = state.neighbours.read_or_recover();
+ let view = view.as_ref()?;
+ Some(Arc::clone(view.whole.get_or_init(|| {
+ let mut whole: BTreeMap<String, BTreeSet<String>> = view
+ .members
+ .iter()
+ .map(|member| (member.clone(), BTreeSet::new()))
+ .collect();
+ let transports: BTreeSet<&String> = view.transports.values().flatten().collect();
+ for transport in transports {
+ let on_it: Vec<String> = view
+ .transports
+ .iter()
+ .filter(|(_, on)| on.contains(transport))
+ .map(|(member, _)| member.clone())
+ .collect();
+ let graph = graph(&on_it, view.k, &|_| false, &|m| view.stable.contains(m));
+ for (member, neighbours) in graph {
+ whole.entry(member).or_default().extend(neighbours);
+ }
+ }
+ Arc::new(whole)
+ })))
}
/// Whether the neighbours are computed from the member list yet (before, everyone
@@ -342,7 +379,23 @@ pub(crate) async fn refresh(state: &AppState) -> Option<NeighbourChange> {
}) {
return None;
}
- view.as_ref().map(|view| view.neighbours.clone())
+ view.as_ref().map(|view| {
+ // The member list's graph stays while the member list does.
+ let whole = if view.members == current
+ && view.k == k
+ && view.stable == stable
+ && view.transports == transports
+ {
+ view.whole.clone()
+ } else {
+ Default::default()
+ };
+ (view.neighbours.clone(), whole)
+ })
+ };
+ let (previous, whole) = match previous {
+ Some((previous, whole)) => (Some(previous), whole),
+ None => (None, Default::default()),
};
// Before the first view everyone known counted as a neighbour.
@@ -400,6 +453,7 @@ pub(crate) async fn refresh(state: &AppState) -> Option<NeighbourChange> {
unlisted,
outbound,
neighbours: computed,
+ whole,
});
// New neighbours are kept alive and judged.
state.peers_due.now();