Skip to content

Commit c270355

Browse files
SS: Reset connection if token is unrecognized (#17529)
This triggers the client to start a new sliding sync connection. If we don't do this and the client asks for the full range of rooms, we end up sending down all rooms and their state from scratch (which can be very slow) This causes things like element-hq/element-x-ios#3115 after we restart the server --------- Co-authored-by: Eric Eastwood <[email protected]>
1 parent e3db7b2 commit c270355

File tree

4 files changed

+57
-17
lines changed

4 files changed

+57
-17
lines changed

changelog.d/17529.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Reset the sliding sync connection if we don't recognize the per-connection state position.

synapse/api/errors.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ class Codes(str, Enum):
128128
# MSC2677
129129
DUPLICATE_ANNOTATION = "M_DUPLICATE_ANNOTATION"
130130

131+
# MSC3575 we are telling the client they need to reset their sliding sync
132+
# connection.
133+
UNKNOWN_POS = "M_UNKNOWN_POS"
134+
131135

132136
class CodeMessageException(RuntimeError):
133137
"""An exception with integer code, a message string attributes and optional headers.
@@ -847,3 +851,17 @@ def __init__(self) -> None:
847851
msg=PartialStateConflictError.message(),
848852
errcode=Codes.UNKNOWN,
849853
)
854+
855+
856+
class SlidingSyncUnknownPosition(SynapseError):
857+
"""An error that Synapse can return to signal to the client to expire their
858+
sliding sync connection (i.e. send a new request without a `?since=`
859+
param).
860+
"""
861+
862+
def __init__(self) -> None:
863+
super().__init__(
864+
HTTPStatus.BAD_REQUEST,
865+
msg="Unknown position",
866+
errcode=Codes.UNKNOWN_POS,
867+
)

synapse/handlers/sliding_sync.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
EventTypes,
4848
Membership,
4949
)
50+
from synapse.api.errors import SlidingSyncUnknownPosition
5051
from synapse.events import EventBase, StrippedStateEvent
5152
from synapse.events.utils import parse_stripped_state_event, strip_event
5253
from synapse.handlers.relations import BundledAggregations
@@ -491,6 +492,22 @@ async def current_sync_for_user(
491492
# See https://github.com/matrix-org/matrix-doc/issues/1144
492493
raise NotImplementedError()
493494

495+
if from_token:
496+
# Check that we recognize the connection position, if not tell the
497+
# clients that they need to start again.
498+
#
499+
# If we don't do this and the client asks for the full range of
500+
# rooms, we end up sending down all rooms and their state from
501+
# scratch (which can be very slow). By expiring the connection we
502+
# allow the client a chance to do an initial request with a smaller
503+
# range of rooms to get them some results sooner but will end up
504+
# taking the same amount of time (more with round-trips and
505+
# re-processing) in the end to get everything again.
506+
if not await self.connection_store.is_valid_token(
507+
sync_config, from_token.connection_position
508+
):
509+
raise SlidingSyncUnknownPosition()
510+
494511
await self.connection_store.mark_token_seen(
495512
sync_config=sync_config,
496513
from_token=from_token,
@@ -2821,6 +2838,16 @@ class SlidingSyncConnectionStore:
28212838
attr.Factory(dict)
28222839
)
28232840

2841+
async def is_valid_token(
2842+
self, sync_config: SlidingSyncConfig, connection_token: int
2843+
) -> bool:
2844+
"""Return whether the connection token is valid/recognized"""
2845+
if connection_token == 0:
2846+
return True
2847+
2848+
conn_key = self._get_connection_key(sync_config)
2849+
return connection_token in self._connections.get(conn_key, {})
2850+
28242851
async def have_sent_room(
28252852
self, sync_config: SlidingSyncConfig, connection_token: int, room_id: str
28262853
) -> HaveSentRoom:

tests/rest/client/sliding_sync/test_rooms_required_state.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,10 @@ def test_rooms_required_state_incremental_sync(self) -> None:
161161
self.assertIsNone(response_body["rooms"][room_id1].get("required_state"))
162162
self.assertIsNone(response_body["rooms"][room_id1].get("invite_state"))
163163

164-
def test_rooms_required_state_incremental_sync_restart(self) -> None:
164+
def test_rooms_incremental_sync_restart(self) -> None:
165165
"""
166-
Test `rooms.required_state` returns requested state events in the room during an
167-
incremental sync, after a restart (and so the in memory caches are reset).
166+
Test that after a restart (and so the in memory caches are reset) that
167+
we correctly return an `M_UNKNOWN_POS`
168168
"""
169169

170170
user1_id = self.register_user("user1", "pass")
@@ -195,22 +195,16 @@ def test_rooms_required_state_incremental_sync_restart(self) -> None:
195195
self.hs.get_sliding_sync_handler().connection_store._connections.clear()
196196

197197
# Make the Sliding Sync request
198-
response_body, _ = self.do_sync(sync_body, since=from_token, tok=user1_tok)
199-
200-
# If the cache has been cleared then we do expect the state to come down
201-
state_map = self.get_success(
202-
self.storage_controllers.state.get_current_state(room_id1)
198+
channel = self.make_request(
199+
method="POST",
200+
path=self.sync_endpoint + f"?pos={from_token}",
201+
content=sync_body,
202+
access_token=user1_tok,
203203
)
204-
205-
self._assertRequiredStateIncludes(
206-
response_body["rooms"][room_id1]["required_state"],
207-
{
208-
state_map[(EventTypes.Create, "")],
209-
state_map[(EventTypes.RoomHistoryVisibility, "")],
210-
},
211-
exact=True,
204+
self.assertEqual(channel.code, 400, channel.json_body)
205+
self.assertEqual(
206+
channel.json_body["errcode"], "M_UNKNOWN_POS", channel.json_body
212207
)
213-
self.assertIsNone(response_body["rooms"][room_id1].get("invite_state"))
214208

215209
def test_rooms_required_state_wildcard(self) -> None:
216210
"""

0 commit comments

Comments
 (0)