aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-03 06:23:23 +0100
committertv <tv@krebsco.de>2026-11-03 06:23:23 +0100
commit4559c4427fcdebe1b816ce8c4e651cf11571b7d5 (patch)
treebeefb9ad0b91145f4a4dbaec67fb09501f4bb525
parentb16977b18fdd1e5193f5c015eadcb61a63f4fae1 (diff)
tree changes had already
-rw-r--r--docs/peers.md4
-rw-r--r--src/document/sync.rs7
-rw-r--r--src/swarm/sync.rs18
3 files changed, 24 insertions, 5 deletions
diff --git a/docs/peers.md b/docs/peers.md
index 59b45d0..939f318 100644
--- a/docs/peers.md
+++ b/docs/peers.md
@@ -316,7 +316,9 @@ cycles; taking only one it reaches routes around links that don't work: where a
behind NAT reaches a single member, that one comes first among those as far from it,
and the others take it as their parent. A sync message that passes a
writer's change on says whose; whoever applies one sends it at once to its children in
-that writer's tree, however it got it. Its announcement round then goes only to the
+that writer's tree, however it got it. So does one that had the change already: it may
+have come another way first without saying whose (with an answer to one of its own
+messages, say), and then nothing was passed on. Its announcement round then goes only to the
neighbours the tree leaves out (no member they reach comes before them, or the member
list's graph doesn't have them); all others are told in a slow round 2 s later, which
catches what a tree missed (a parent that went away, nodes whose views of the tree
diff --git a/src/document/sync.rs b/src/document/sync.rs
index 44ad833..4a3fe5e 100644
--- a/src/document/sync.rs
+++ b/src/document/sync.rs
@@ -22,6 +22,13 @@ use crate::transport::SyncPayload;
/// A sync message from a peer, decoded (`decode`), to apply (`SyncSession::receive`).
pub(crate) struct Received(sync::Message);
+impl Received {
+ /// Whether it carries changes (it may carry only heads and what the peer needs).
+ pub(crate) fn carries_changes(&self) -> bool {
+ !self.0.changes.is_empty()
+ }
+}
+
/// `payload` decoded. Before taking the document's lock: decoding a large message takes
/// a while.
pub(crate) fn decode(payload: &SyncPayload) -> anyhow::Result<Received> {
diff --git a/src/swarm/sync.rs b/src/swarm/sync.rs
index 187dacc..dd5444b 100644
--- a/src/swarm/sync.rs
+++ b/src/swarm/sync.rs
@@ -376,11 +376,12 @@ pub(crate) async fn handle_sync(
let clock = Arc::clone(&state.clock);
let ahead_ms = state.clock_ahead_ms.load(Ordering::Relaxed);
- // Some((caught_up, heads changed)) when applied, None when the message belongs
- // to another epoch.
- tokio::task::spawn_blocking(move || -> Result<Option<(bool, bool)>> {
+ // Some((caught_up, heads changed, carried changes)) when applied, None when the
+ // message belongs to another epoch.
+ tokio::task::spawn_blocking(move || -> Result<Option<(bool, bool, bool)>> {
// Before the lock: decoding a large message takes a while.
let message = crate::document::sync::decode(&message)?;
+ let carried = message.carries_changes();
let mut replica = doc.blocking_write();
// Authoritative check, under the same lock as the apply: the document may
@@ -430,13 +431,14 @@ pub(crate) async fn handle_sync(
// before it registered itself).
check_caught_up && from_ready && peer_state.sync.have_all_theirs(&replica.doc),
changed,
+ carried,
)))
})
.await
.context("Sync task panicked")??
};
- let Some((caught_up, changed)) = applied else {
+ let Some((caught_up, changed, carried)) = applied else {
debug!(peer = %peer, epoch = %epoch_label, "Dropped sync message from another epoch");
return Ok(());
};
@@ -459,6 +461,14 @@ pub(crate) async fn handle_sync(
// Caught up: our other neighbours may lack what the source had.
if changed || just_caught_up {
crate::swarm::relay::announce(state, peer, writer.filter(|_| changed)).await;
+ } else if carried
+ && let Some(writer) = writer
+ && state.initial_sync_done.load(Ordering::Acquire)
+ {
+ // A writer's changes along its tree that we had already: they came another way
+ // first, not saying whose (with an answer to one of our messages, say), so we
+ // passed nothing on then. Our children in its tree may lack them still.
+ crate::swarm::tree::forward(state, writer, peer).await;
}
state