aboutsummaryrefslogtreecommitdiffstats
path: root/src/swarm
diff options
context:
space:
mode:
Diffstat (limited to 'src/swarm')
-rw-r--r--src/swarm/relay.rs40
-rw-r--r--src/swarm/sync.rs3
2 files changed, 31 insertions, 12 deletions
diff --git a/src/swarm/relay.rs b/src/swarm/relay.rs
index e049846..31df0d3 100644
--- a/src/swarm/relay.rs
+++ b/src/swarm/relay.rs
@@ -251,8 +251,11 @@ async fn announce_now(state: &AppState, pending: &Pending, slow: bool) {
/// The neighbours to tell of the changes `senders` sent us. The peer whose changes we
/// announce has them already, so with a single sender it is skipped; with several, each
/// lacks the others' changes. So is a neighbour whose latest heads (its STATUS, an
-/// announcement of its own, or its STATUS_REPLY) are ours now: it has all we have; and
-/// one `sent` the changes along a writer's tree, which has them already.
+/// announcement of its own, its STATUS_REPLY, or its latest sync message) are ours, or
+/// have all of ours in their history: it has all we have; and one `sent` the changes
+/// along a writer's tree, which has them already. Only what the neighbour said counts,
+/// not what we sent it: a message along a tree may be lost, which is what the slow
+/// round is for.
async fn unreached(
state: &AppState,
senders: &HashSet<String>,
@@ -262,11 +265,13 @@ async fn unreached(
1 => senders.iter().next().cloned(),
_ => None,
};
- let ours = {
+ let (epoch, heads) = {
let stamp = state.stamp.borrow();
- (stamp.epoch, HeadsSummary::of(stamp.heads.to_vec()))
+ (stamp.epoch, stamp.heads.to_vec())
};
- let mut unreached = Vec::new();
+ let ours = HeadsSummary::of(heads.clone());
+ // What each neighbour said it has.
+ let mut said = Vec::new();
for (peer, peer_state) in state.peers_snapshot().await {
if skip.as_deref() == Some(peer.as_str())
|| sent.contains(&peer)
@@ -274,17 +279,28 @@ async fn unreached(
{
continue;
}
- let has_ours = peer_state
+ let last = peer_state
.lock()
.await
.last_heads
- .as_ref()
- .is_some_and(|(epoch, heads)| *epoch == ours.0 && *heads == ours.1);
- if !has_ours {
- unreached.push(peer);
- }
+ .clone()
+ .filter(|(theirs, _)| *theirs == epoch)
+ .map(|(_, heads)| heads);
+ said.push((peer, last));
+ }
+ let replica = state.doc.read().await;
+ if replica.epoch != epoch {
+ return said.into_iter().map(|(peer, _)| peer).collect();
}
- unreached
+ said.into_iter()
+ .filter(|(_, last)| {
+ !last.as_ref().is_some_and(|theirs| {
+ *theirs == ours
+ || crate::document::changes::covers(&replica.doc, theirs.listed(), &heads)
+ })
+ })
+ .map(|(peer, _)| peer)
+ .collect()
}
/// Pulls in progress: one neighbour at a time, so each change arrives about once. The
diff --git a/src/swarm/sync.rs b/src/swarm/sync.rs
index ed4c4da..8660742 100644
--- a/src/swarm/sync.rs
+++ b/src/swarm/sync.rs
@@ -484,6 +484,9 @@ pub(crate) async fn handle_sync(
let mut peer_state = peer_state_mutex.blocking_lock();
peer_state.reports_ready = Some(from_ready);
+ // What it says it has (`swarm::relay::unreached`).
+ peer_state.last_heads =
+ Some((epoch, transport::HeadsSummary::of(message.heads().to_vec())));
let heads_before = replica.heads().to_vec();
let sync = &mut peer_state.sync;