From 12fae75f850c3ca63a68378b2684b5d89f667e10 Mon Sep 17 00:00:00 2001 From: tv Date: Thu, 5 Nov 2026 15:59:35 +0100 Subject: offer answers --- docs/peers.md | 3 ++- src/document/sync.rs | 10 ++++++++++ src/swarm/sync.rs | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/peers.md b/docs/peers.md index 991bb40..5f5cfc9 100644 --- a/docs/peers.md +++ b/docs/peers.md @@ -178,7 +178,8 @@ as it knows them; of those, the ones it holds, and for any it lacks, the heads t share). The peer fetches them as a blob, in chunks (each deflated where that makes it at least a tenth smaller), from the sender and any other peer holding both sets of heads (every such peer produces the same bytes), loads them, -and syncs on; the rest goes in ordinary messages. Until the peer answers, the sender +and syncs on; the rest goes in ordinary messages. The offer is the sender's answer to +what the peer sent: the peer waits for no other while it downloads. Until the peer answers, the sender sends it nothing; if the answer is overdue, it offers again (the same offer, without generating the message again, while neither side's heads moved). This is how a join without a recent remake, or a return after much was written, gets the bulk of its changes. A node diff --git a/src/document/sync.rs b/src/document/sync.rs index 7695b82..c703300 100644 --- a/src/document/sync.rs +++ b/src/document/sync.rs @@ -870,6 +870,16 @@ impl SyncSession { self.reply_backoff.reset(); } + /// The peer answered our message with a delta offer (`SyncBody::Delta`): the wait + /// ends, and to Automerge no message of ours is in flight any more, as an answer of + /// its own would have told it. Else, with our heads unchanged (an offer we don't + /// fetch, or a download that fails), it would say nothing again, and the peer would + /// never hear what we have to offer afresh. + pub(crate) fn answered_by_offer(&mut self) { + self.reply_settled(); + self.state.in_flight = false; + } + /// If the answer is overdue: forgets that the unanswered changes were sent and that /// a message is in flight, so the next sync pass sends them again. Returns how many /// changes that concerns. diff --git a/src/swarm/sync.rs b/src/swarm/sync.rs index e551cb8..0edc277 100644 --- a/src/swarm/sync.rs +++ b/src/swarm/sync.rs @@ -487,6 +487,17 @@ pub(crate) async fn handle_sync( ) } transport::SyncBody::Delta(spec) => { + // An offer answers what we sent as much as a message would: the wait for an + // answer ends here, not when the download does (its reply timeout would + // otherwise count against a peer that answered, suspect its path and give it + // up as the source to catch up from). What it offers is what it has. + { + let mut peer_state = peer_state_mutex.lock().await; + peer_state.reports_ready = Some(from_ready); + peer_state.last_heads = Some((epoch, transport::HeadsSummary::of(spec.to.clone()))); + peer_state.last_sync_rx = Some(Instant::now()); + peer_state.sync.answered_by_offer(); + } fetch_delta(state, peer, epoch, spec); return Ok(()); } -- cgit v1.3.1