diff options
-rw-r--r-- | synapse/handlers/sliding_sync.py | 32 | ||||
-rw-r--r-- | tests/rest/client/test_sync.py | 70 |
2 files changed, 100 insertions, 2 deletions
diff --git a/synapse/handlers/sliding_sync.py b/synapse/handlers/sliding_sync.py index d94160c59b..a88c66c4f7 100644 --- a/synapse/handlers/sliding_sync.py +++ b/synapse/handlers/sliding_sync.py @@ -653,6 +653,13 @@ class SlidingSyncHandler: else: assert_never(status.status) + if status.timeline_limit is not None and ( + status.timeline_limit < relevant_room_map[room_id].timeline_limit + ): + # If the timeline limit has increased we want to send down + # more historic events (even if nothing has since changed). + rooms_should_send.add(room_id) + # We only need to check for new events since any state changes # will also come down as new events. rooms_that_have_updates = self.store.get_rooms_that_might_have_updates( @@ -1476,7 +1483,12 @@ class SlidingSyncHandler: # - When users `newly_joined` # - For an incremental sync where we haven't sent it down this # connection before + # + # We also decide if we should ignore the timeline bound or not. This is + # to handle the case where the client has requested more historical + # messages in the room by increasing the timeline limit. from_bound = None + ignore_timeline_bound = False initial = True if from_token and not room_membership_for_user_at_to_token.newly_joined: room_status = await self.connection_store.have_sent_room( @@ -1484,6 +1496,7 @@ class SlidingSyncHandler: connection_token=from_token.connection_position, room_id=room_id, ) + if room_status.status == HaveSentRoomFlag.LIVE: from_bound = from_token.stream_token.room_key initial = False @@ -1497,9 +1510,24 @@ class SlidingSyncHandler: else: assert_never(room_status.status) + if room_status.timeline_limit is not None and ( + room_status.timeline_limit < room_sync_config.timeline_limit + ): + # If the timeline limit has been increased since previous + # requests then we treat it as request for more events. We do + # this by sending down a `limited` sync, ignoring the from + # bound. + ignore_timeline_bound = True + log_kv({"sliding_sync.room_status": room_status}) - log_kv({"sliding_sync.from_bound": from_bound, "sliding_sync.initial": initial}) + log_kv( + { + "sliding_sync.from_bound": from_bound, + "sliding_sync.ignore_timeline_bound": ignore_timeline_bound, + "sliding_sync.initial": initial, + } + ) # Assemble the list of timeline events # @@ -1542,7 +1570,7 @@ class SlidingSyncHandler: # (from newer to older events) starting at to_bound. # This ensures we fill the `limit` with the newest events first, from_key=to_bound, - to_key=from_bound, + to_key=from_bound if not ignore_timeline_bound else None, direction=Direction.BACKWARDS, # We add one so we can determine if there are enough events to saturate # the limit or not (see `limited`) diff --git a/tests/rest/client/test_sync.py b/tests/rest/client/test_sync.py index 8d5fd30fdd..bb723927c4 100644 --- a/tests/rest/client/test_sync.py +++ b/tests/rest/client/test_sync.py @@ -4940,6 +4940,76 @@ class SlidingSyncTestCase(SlidingSyncBase): response_body, _ = self.do_sync(sync_body, tok=user1_tok) self.assertEqual(response_body["rooms"][room_id1]["initial"], True) + def test_increasing_timeline_range_sends_more_messages(self) -> None: + """ + Test that increasing the timeline limit via room subscriptions sends the + room down with more messages in a limited sync. + """ + + user1_id = self.register_user("user1", "pass") + user1_tok = self.login(user1_id, "pass") + + room_id1 = self.helper.create_room_as(user1_id, tok=user1_tok) + + sync_body = { + "lists": { + "foo-list": { + "ranges": [[0, 1]], + "required_state": [[EventTypes.Create, ""]], + "timeline_limit": 1, + } + } + } + + message_events = [] + for _ in range(10): + resp = self.helper.send(room_id1, "msg", tok=user1_tok) + message_events.append(resp["event_id"]) + + # Make the first Sliding Sync request + response_body, from_token = self.do_sync(sync_body, tok=user1_tok) + room_response = response_body["rooms"][room_id1] + + self.assertEqual(room_response["initial"], True) + self.assertEqual(room_response["limited"], True) + + # We only expect the last message at first + self.assertEqual( + [event["event_id"] for event in room_response["timeline"]], + message_events[-1:], + room_response["timeline"], + ) + + # We also expect to get the create event state. + self.assertEqual( + [event["type"] for event in room_response["required_state"]], + [EventTypes.Create], + ) + + # Now do another request with a room subscription with an increased timeline limit + sync_body["room_subscriptions"] = { + room_id1: { + "required_state": [], + "timeline_limit": 10, + } + } + + response_body, _ = self.do_sync(sync_body, since=from_token, tok=user1_tok) + room_response = response_body["rooms"][room_id1] + + self.assertNotIn("initial", room_response) + self.assertEqual(room_response["limited"], True) + + # Now we expect all the messages + self.assertEqual( + [event["event_id"] for event in room_response["timeline"]], + message_events, + room_response["timeline"], + ) + + # We don't expect to get the room create down, as nothing has changed. + self.assertNotIn("required_state", room_response) + class SlidingSyncToDeviceExtensionTestCase(SlidingSyncBase): """Tests for the to-device sliding sync extension""" |