diff options
| author | tv <tv@krebsco.de> | 2026-10-22 21:55:36 +0200 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-22 21:55:36 +0200 |
| commit | 7baed9d4c75437658f2dfa9572226b8163effaee (patch) | |
| tree | 498a20ad60fe4bb50dd93fa58f7c0e38c28394aa /src | |
| parent | 1dcf40f06c27f4deff73df204dc05fbf999234f5 (diff) | |
When an sync messages arrives and automerge has nothing to say about it,
then no reply is generated. With c9279e4 it was assumed that every sync
message gets an answer, so missing replies caused unnecessary resends.
Now a reply is only awaited if the peer's last reported heads differ
from ours. If the peer's heads become equal through a third peer without
replying, the waiting will also abort if a ping or pong showss the same
heads digest on both sides.
Diffstat (limited to 'src')
| -rw-r--r-- | src/state.rs | 6 | ||||
| -rw-r--r-- | src/swarm/peer.rs | 21 | ||||
| -rw-r--r-- | src/swarm/sync.rs | 16 |
3 files changed, 34 insertions, 9 deletions
diff --git a/src/state.rs b/src/state.rs index 49f75dc..63e6490 100644 --- a/src/state.rs +++ b/src/state.rs @@ -178,6 +178,12 @@ impl PeerState { } } + /// Keepalives show both documents are the same: stop waiting for an answer. + pub(crate) fn sync_in_step(&mut self) { + self.sync_reply = None; + self.sync_reply_timeout = SYNC_REPLY_TIMEOUT; + } + /// The peer answered. pub(crate) fn sync_reply_received(&mut self) { self.sync_reply = None; diff --git a/src/swarm/peer.rs b/src/swarm/peer.rs index d801b0f..bad8e24 100644 --- a/src/swarm/peer.rs +++ b/src/swarm/peer.rs @@ -133,12 +133,21 @@ async fn heal_if_diverged( return; }; - let heal = peer_state_mutex.lock().await.needs_heal( - &local.heads_digest, - &remote.heads_digest, - &state.timing, - Instant::now(), - ); + let heal = { + let mut peer_state = peer_state_mutex.lock().await; + if local.heads_digest == remote.heads_digest { + // Same document: whatever answer we were waiting for, there is nothing left + // to send. (Automerge answers only when it has something to say, so a sync + // message to a peer that already has everything gets no reply at all.) + peer_state.sync_in_step(); + } + peer_state.needs_heal( + &local.heads_digest, + &remote.heads_digest, + &state.timing, + Instant::now(), + ) + }; if heal { warn!(peer = %peer, "Documents differ and sync is idle; re-handshaking to restart sync"); diff --git a/src/swarm/sync.rs b/src/swarm/sync.rs index 0c7148e..e294201 100644 --- a/src/swarm/sync.rs +++ b/src/swarm/sync.rs @@ -110,9 +110,19 @@ async fn deliver_sync_to_peer(state: &AppState, peer: &str, retry_delivery: bool peer_state.needs_generate = false; let epoch = replica.epoch; let sent_before = peer_state.sync_state.sent_hashes.clone(); - let sync_doc = replica.doc.sync(); - let message = sync_doc.generate_sync_message(&mut peer_state.sync_state); - if message.is_some() { + let message = { + let sync_doc = replica.doc.sync(); + sync_doc.generate_sync_message(&mut peer_state.sync_state) + }; + // A peer whose heads are ours already has everything; Automerge + // answers only when it has something to say, so it will stay + // silent. Waiting for an answer would only end in a pointless + // resend. + let in_step = peer_state.sync_state.their_heads.as_ref() + == Some(&replica.doc.get_heads()); + if in_step { + peer_state.sync_in_step(); + } else if message.is_some() { let sent = peer_state .sync_state .sent_hashes |
