aboutsummaryrefslogtreecommitdiffstats
path: root/src/swarm/topology/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/swarm/topology/mod.rs')
-rw-r--r--src/swarm/topology/mod.rs93
1 files changed, 83 insertions, 10 deletions
diff --git a/src/swarm/topology/mod.rs b/src/swarm/topology/mod.rs
index 29e460f..2c41f0b 100644
--- a/src/swarm/topology/mod.rs
+++ b/src/swarm/topology/mod.rs
@@ -66,6 +66,12 @@ pub(crate) struct NeighbourView {
/// (`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>>>>,
+ /// Where members recorded their links differ from that graph (`swarm::working_links`),
+ /// those with a record.
+ recorded: BTreeMap<String, members::RecordedLinks>,
+ /// That graph with the links members recorded (`working_graph`): computed when first
+ /// asked for, kept while the member list and the records stay the same.
+ working: std::sync::OnceLock<Arc<BTreeMap<String, BTreeSet<String>>>>,
}
/// Every member's neighbours as the member list gives them, as if every member reached
@@ -100,6 +106,59 @@ pub(crate) fn member_list_graph(
})))
}
+/// The member list's graph (`member_list_graph`) with the links members recorded
+/// (`swarm::working_links`): without those either end recorded as unreached, with those
+/// one recorded as reached (`with_recorded`). The same on every node that has the same
+/// document; what the writers' trees are built on (`swarm::tree`). None until the view
+/// is known.
+pub(crate) fn working_graph(state: &AppState) -> Option<Arc<BTreeMap<String, BTreeSet<String>>>> {
+ let whole = member_list_graph(state)?;
+ let view = state.neighbours.read_or_recover();
+ let view = view.as_ref()?;
+ Some(Arc::clone(view.working.get_or_init(|| {
+ if view.recorded.is_empty() {
+ whole
+ } else {
+ Arc::new(with_recorded(&whole, &view.recorded))
+ }
+ })))
+}
+
+/// `graph` with the links members recorded (`recorded`): those one recorded as reached
+/// added, between members `graph` has; then those either end recorded as unreached
+/// taken out (a link one end reaches and the other doesn't counts as down).
+pub(crate) fn with_recorded(
+ graph: &BTreeMap<String, BTreeSet<String>>,
+ recorded: &BTreeMap<String, members::RecordedLinks>,
+) -> BTreeMap<String, BTreeSet<String>> {
+ let mut graph = graph.clone();
+ for (member, links) in recorded {
+ for other in &links.reached {
+ if member != other && graph.contains_key(member) && graph.contains_key(other) {
+ graph
+ .entry(member.clone())
+ .or_default()
+ .insert(other.clone());
+ graph
+ .entry(other.clone())
+ .or_default()
+ .insert(member.clone());
+ }
+ }
+ }
+ for (member, links) in recorded {
+ for other in &links.unreached {
+ if let Some(neighbours) = graph.get_mut(member) {
+ neighbours.remove(other);
+ }
+ if let Some(neighbours) = graph.get_mut(other) {
+ neighbours.remove(member);
+ }
+ }
+ }
+ graph
+}
+
/// Whether `peer` is in the member list, as the view was last computed from it; None
/// while the view isn't known.
pub(crate) fn listed(state: &AppState, peer: &str) -> Option<bool> {
@@ -382,7 +441,7 @@ pub(crate) async fn refresh(state: &AppState) -> Option<NeighbourChange> {
if !state.is_ready().await {
return None;
}
- let (current, stable, transports, alive, k) = {
+ let (current, stable, transports, alive, k, recorded) = {
let replica = state.doc.read().await;
members::apply(state, &replica.doc);
let k = swarm_neighbours(&replica.doc, state.neighbours_k);
@@ -404,7 +463,12 @@ pub(crate) async fn refresh(state: &AppState) -> Option<NeighbourChange> {
.iter()
.map(|m| (m.clone(), schemes(&members::addresses(&replica.doc, m))))
.collect();
- (current, stable, transports, alive, k)
+ let recorded: BTreeMap<String, members::RecordedLinks> = current
+ .iter()
+ .map(|m| (m.clone(), members::links(&replica.doc, m)))
+ .filter(|(_, links)| *links != members::RecordedLinks::default())
+ .collect();
+ (current, stable, transports, alive, k, recorded)
};
if k != state.neighbours_k {
info_once(&format!(
@@ -486,26 +550,33 @@ pub(crate) async fn refresh(state: &AppState) -> Option<NeighbourChange> {
&& view.stable == stable
&& view.transports == transports
&& view.local_transports == local_transports
+ && view.recorded == recorded
}) {
return None;
}
view.as_ref().map(|view| {
- // The member list's graph stays while the member list does.
- let whole = if view.members == current
+ // The member list's graph stays while the member list does; with the
+ // records, while they do too.
+ let same_list = view.members == current
&& view.k == k
&& view.stable == stable
- && view.transports == transports
- {
+ && view.transports == transports;
+ let whole = if same_list {
view.whole.clone()
} else {
Default::default()
};
- (view.neighbours.clone(), whole)
+ let working = if same_list && view.recorded == recorded {
+ view.working.clone()
+ } else {
+ Default::default()
+ };
+ (view.neighbours.clone(), whole, working)
})
};
- let (previous, whole) = match previous {
- Some((previous, whole)) => (Some(previous), whole),
- None => (None, Default::default()),
+ let (previous, whole, working) = match previous {
+ Some((previous, whole, working)) => (Some(previous), whole, working),
+ None => (None, Default::default(), Default::default()),
};
// Before the first view everyone known counted as a neighbour.
@@ -567,6 +638,8 @@ pub(crate) async fn refresh(state: &AppState) -> Option<NeighbourChange> {
outbound,
neighbours: computed,
whole,
+ recorded,
+ working,
});
// New neighbours are kept alive and judged.
state.peers_due.now();