aboutsummaryrefslogtreecommitdiffstats
path: root/src/swarm/tree.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-05 01:23:40 +0100
committertv <tv@krebsco.de>2026-11-05 01:23:40 +0100
commit889e23ee1eb8a209efe7c1eae86aa39091560149 (patch)
tree8da04d6d62f9a1647f90b6ea459eba62b91f5a12 /src/swarm/tree.rs
parent0d16147e95211b0829fddaefb0d014fe0c48dbc8 (diff)
covered neighbours have it
Diffstat (limited to 'src/swarm/tree.rs')
-rw-r--r--src/swarm/tree.rs35
1 files changed, 31 insertions, 4 deletions
diff --git a/src/swarm/tree.rs b/src/swarm/tree.rs
index 015d62c..20df2ec 100644
--- a/src/swarm/tree.rs
+++ b/src/swarm/tree.rs
@@ -284,6 +284,9 @@ pub(crate) struct Plan {
/// Our neighbours that no member they reach comes before (the tree reaches them only
/// by announcement), and those not in the member list's graph (whom no tree has).
pub(crate) uncovered: Vec<String>,
+ /// Our neighbours that get the writer's changes from another parent, and the writer
+ /// itself: they have them, or will, without us (`forward`).
+ pub(crate) covered: Vec<String>,
}
/// Our part in `writer`'s tree (`children`, `Plan`); None while the member list isn't
@@ -305,11 +308,13 @@ pub(crate) async fn plan(state: &AppState, writer: u64) -> Option<Plan> {
let mut children = Vec::new();
let mut alone = Vec::new();
let mut uncovered = Vec::new();
+ let mut covered = Vec::new();
for member in graph.get(me).into_iter().flatten() {
let Some(its_rank) = rank(&graph, &distance, &reached, member) else {
continue;
};
if distance[member.as_str()] == 0 {
+ covered.push(member.clone());
continue;
}
match parent(&graph, &distance, &reached, member) {
@@ -325,6 +330,8 @@ pub(crate) async fn plan(state: &AppState, writer: u64) -> Option<Plan> {
}
children.push(member.clone());
}
+ Some(parent) if parent != me => covered.push(member.clone()),
+ // Ours by the graph, but we don't reach it, or it comes before us.
Some(_) => {}
}
}
@@ -341,14 +348,34 @@ pub(crate) async fn plan(state: &AppState, writer: u64) -> Option<Plan> {
children,
alone,
uncovered,
+ covered,
})
}
-/// We applied a change of `writer`'s that `from` passed on: to our children in its
-/// tree (but `from`), a sync message each, saying whose. Returns our part in the tree
-/// (`Plan`), None if we have none (announcements then do it all).
-pub(crate) async fn forward(state: &AppState, writer: u64, from: &str) -> Option<Plan> {
+/// We applied `changes` of `writer`'s that `from` passed on (none if we had them
+/// already): to our children in its tree (but `from`), a sync message each, saying
+/// whose. Returns our part in the tree (`Plan`), None if we have none (announcements
+/// then do it all).
+///
+/// Our neighbours that get them from another parent (`Plan::covered`) count as having
+/// them (`SyncSession::known_to_peer`): else, with writers in turn, each change coming
+/// along another tree, the next message we pass on to one would carry all we got since
+/// our last to it, mostly what its other parents brought it. Should one lack them after
+/// all, it asks, and gets them (`resend_if_needed`).
+pub(crate) async fn forward(
+ state: &AppState,
+ writer: u64,
+ from: &str,
+ changes: &[crate::document::ChangeHash],
+) -> Option<Plan> {
let mut plan = plan(state, writer).await?;
+ if !changes.is_empty() {
+ for neighbour in &plan.covered {
+ if let Some(peer_state) = state.get_peer_state(neighbour).await {
+ peer_state.lock().await.sync.known_to_peer(changes);
+ }
+ }
+ }
plan.children.retain(|child| child != from);
let children = &plan.children;
for child in children {