diff options
Diffstat (limited to 'src/swarm')
| -rw-r--r-- | src/swarm/relay.rs | 40 | ||||
| -rw-r--r-- | src/swarm/sync.rs | 3 |
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; |
