From f92032f6da36ae6a2d8e43ed3ef5da67a2c831e2 Mon Sep 17 00:00:00 2001 From: Erwan Leboucher Date: Thu, 27 Aug 2026 15:53:58 +0200 Subject: [PATCH] fix(sync): Retain overlapping sync positions --- ...iding-sync-overlapping-responses.bugfix.md | 1 + src/service/sync/mod.rs | 50 +++++++++---------- 2 files changed, 25 insertions(+), 26 deletions(-) create mode 100644 changelog.d/sliding-sync-overlapping-responses.bugfix.md diff --git a/changelog.d/sliding-sync-overlapping-responses.bugfix.md b/changelog.d/sliding-sync-overlapping-responses.bugfix.md new file mode 100644 index 000000000..cd14e029b --- /dev/null +++ b/changelog.d/sliding-sync-overlapping-responses.bugfix.md @@ -0,0 +1 @@ +Fixed simplified Sliding Sync restarting when overlapping sync responses arrive out of order. Contributed by @eleboucher. diff --git a/src/service/sync/mod.rs b/src/service/sync/mod.rs index 6a97377df..9f67ad9a1 100644 --- a/src/service/sync/mod.rs +++ b/src/service/sync/mod.rs @@ -37,32 +37,18 @@ struct SnakeSyncCache { subscriptions: BTreeMap, known_rooms: BTreeMap>, acknowledged_pos: u64, - pending: Option, + pending: BTreeMap>>, extensions: v5::request::Extensions, } -struct PendingSnakeSync { - pos: u64, - known_rooms: BTreeMap>, -} - impl SnakeSyncCache { fn accepts_pos(&self, pos: u64) -> bool { - self.acknowledged_pos == pos - || self - .pending - .as_ref() - .is_some_and(|pending| pending.pos == pos) + self.acknowledged_pos == pos || self.pending.contains_key(&pos) } fn acknowledge(&mut self, pos: u64) { - if self - .pending - .as_ref() - .is_some_and(|pending| pending.pos == pos) - { - let pending = self.pending.take().expect("pending sync checked above"); - self.known_rooms = pending.known_rooms; + if let Some(known_rooms) = self.pending.remove(&pos) { + self.known_rooms = known_rooms; self.acknowledged_pos = pos; } } @@ -308,7 +294,7 @@ pub fn store_snake_sync_pending( list.insert(room_id, pos); } } - cached.pending = Some(PendingSnakeSync { pos, known_rooms: pending_rooms }); + cached.pending.insert(pos, pending_rooms); } pub fn update_snake_sync_subscriptions( @@ -425,13 +411,10 @@ async fn subscriber_still_woken_after_a_sibling_disconnects() { fn pending_rooms_are_committed_only_when_the_response_pos_is_acknowledged() { let room_id = OwnedRoomId::try_from("!room:example.com").unwrap(); let mut cache = SnakeSyncCache { - pending: Some(PendingSnakeSync { - pos: 42, - known_rooms: BTreeMap::from([( - "list".to_owned(), - BTreeMap::from([(room_id, 42)]), - )]), - }), + pending: BTreeMap::from([( + 42, + BTreeMap::from([("list".to_owned(), BTreeMap::from([(room_id, 42)]))]), + )]), ..Default::default() }; @@ -447,4 +430,19 @@ fn pending_rooms_are_committed_only_when_the_response_pos_is_acknowledged() { assert_eq!(cache.acknowledged_pos, 42); assert_eq!(cache.known_rooms["list"].len(), 1); } + + #[test] + fn overlapping_responses_accept_either_response_position() { + let mut cache = SnakeSyncCache { + acknowledged_pos: 100, + pending: BTreeMap::from([(200, BTreeMap::new()), (201, BTreeMap::new())]), + ..Default::default() + }; + + assert!(cache.accepts_pos(200)); + assert!(cache.accepts_pos(201)); + + cache.acknowledge(200); + assert!(cache.accepts_pos(201)); + } }