From 59b1652f4a4e289099ea4750e6c1ca79339e5f96 Mon Sep 17 00:00:00 2001 From: Erwan Leboucher Date: Thu, 10 Sep 2026 17:11:33 +0200 Subject: [PATCH] fix(sync): Keep sync positions of rooms outside a list's window --- .../sliding-sync-window-positions.bugfix.md | 1 + src/service/sync/mod.rs | 45 ++++++++++++++----- 2 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 changelog.d/sliding-sync-window-positions.bugfix.md diff --git a/changelog.d/sliding-sync-window-positions.bugfix.md b/changelog.d/sliding-sync-window-positions.bugfix.md new file mode 100644 index 000000000..c2207f8db --- /dev/null +++ b/changelog.d/sliding-sync-window-positions.bugfix.md @@ -0,0 +1 @@ +Fixed simplified Sliding Sync re-sending rooms in full after a list's window shrinks and grows again. Contributed by @eleboucher. diff --git a/src/service/sync/mod.rs b/src/service/sync/mod.rs index cfe8510ff..5b50b38de 100644 --- a/src/service/sync/mod.rs +++ b/src/service/sync/mod.rs @@ -52,6 +52,17 @@ fn acknowledge(&mut self, pos: u64) { self.acknowledged_pos = pos; } } + + fn stage_pending(&mut self, pos: u64, known_rooms: BTreeMap>) { + let mut pending_rooms = self.known_rooms.clone(); + for (list_id, rooms) in known_rooms { + let list = pending_rooms.entry(list_id).or_default(); + for room_id in rooms { + list.insert(room_id, pos); + } + } + self.pending.insert(pos, pending_rooms); + } } type DbConnections = SyncMutex>; @@ -285,17 +296,7 @@ pub fn store_snake_sync_pending( let cached = &mut cached.lock(); drop(cache); - let mut pending_rooms = cached.known_rooms.clone(); - for (list_id, rooms) in known_rooms { - let list = pending_rooms.entry(list_id).or_default(); - for last_seen in list.values_mut() { - *last_seen = 0; - } - for room_id in rooms { - list.insert(room_id, pos); - } - } - cached.pending.insert(pos, pending_rooms); + cached.stage_pending(pos, known_rooms); } pub fn update_snake_sync_subscriptions( @@ -432,6 +433,28 @@ fn pending_rooms_are_committed_only_when_the_response_pos_is_acknowledged() { assert_eq!(cache.known_rooms["list"].len(), 1); } + #[test] + fn rooms_outside_the_served_ranges_keep_their_position() { + let windowed = OwnedRoomId::try_from("!windowed:example.com").unwrap(); + let dropped = OwnedRoomId::try_from("!dropped:example.com").unwrap(); + let mut cache = SnakeSyncCache::default(); + let stage = |cache: &mut SnakeSyncCache, pos, rooms: BTreeSet| { + cache.stage_pending(pos, BTreeMap::from([("all_rooms".to_owned(), rooms)])); + cache.acknowledge(pos); + }; + + stage(&mut cache, 100, BTreeSet::from([windowed.clone(), dropped.clone()])); + stage(&mut cache, 200, BTreeSet::from([windowed.clone()])); + + assert_eq!(cache.known_rooms["all_rooms"][&windowed], 200); + assert_eq!(cache.known_rooms["all_rooms"][&dropped], 100); + + stage(&mut cache, 300, BTreeSet::from([windowed.clone(), dropped.clone()])); + + assert_eq!(cache.known_rooms["all_rooms"][&windowed], 300); + assert_eq!(cache.known_rooms["all_rooms"][&dropped], 300); + } + #[test] fn overlapping_responses_accept_either_response_position() { let mut cache = SnakeSyncCache {