diff --git a/synapse/handlers/sync.py b/synapse/handlers/sync.py index 881dde15a6..fb1331902a 100644 --- a/synapse/handlers/sync.py +++ b/synapse/handlers/sync.py @@ -287,6 +287,16 @@ class SyncResult: @attr.s(slots=True, frozen=True, auto_attribs=True) class E2eeSyncResult: + """ + Attributes: + to_device: List of direct messages for the device. + device_lists: List of user_ids whose devices have changed + device_one_time_keys_count: Dict of algorithm to count for one time keys + for this device + device_unused_fallback_key_types: List of key types that have an unused fallback + key + """ + next_batch: StreamToken to_device: List[JsonDict] device_lists: DeviceListUpdates @@ -387,7 +397,17 @@ class SyncHandler: return an empty sync result. Args: + requester: The user requesting the sync response. + sync_config: Config/info necessary to process the sync request. + sync_version: Determines what kind of sync response to generate. request_key: The key to use for caching the response. + since_token: The point in the stream to sync from. + timeout: How long to wait for new data to arrive before giving up. + full_state: Whether to return the full state for each room. + + Returns: + When `SyncVersion.SYNC_V2`, returns a full `SyncResult`. + When `SyncVersion.E2EE_SYNC`, returns a `E2eeSyncResult`. """ # If the user is not part of the mau group, then check that limits have # not been exceeded (if not part of the group by this point, almost certain @@ -567,11 +587,21 @@ class SyncHandler: since_token: Optional[StreamToken] = None, full_state: bool = False, ) -> Union[SyncResult, E2eeSyncResult]: - """Generates the response body of a sync result, represented as a SyncResult. + """Generates the response body of a sync result, represented as a `SyncResult`/`E2eeSyncResult`. This is a wrapper around `generate_sync_result` which starts an open tracing span to track the sync. See `generate_sync_result` for the next part of your indoctrination. + + Args: + sync_config: Config/info necessary to process the sync request. + sync_version: Determines what kind of sync response to generate. + since_token: The point in the stream to sync from.p. + full_state: Whether to return the full state for each room. + + Returns: + When `SyncVersion.SYNC_V2`, returns a full `SyncResult`. + When `SyncVersion.E2EE_SYNC`, returns a `E2eeSyncResult`. """ with start_active_span("sync.current_sync_for_user"): log_kv({"since_token": since_token}) @@ -1800,7 +1830,18 @@ class SyncHandler: sync_config: SyncConfig, since_token: Optional[StreamToken] = None, ) -> E2eeSyncResult: - """Generates the response body of a MSC3575 Sliding Sync `/sync/e2ee` result.""" + """ + Generates the response body of a MSC3575 Sliding Sync `/sync/e2ee` result. + + This is represented by a `E2eeSyncResult` struct, which is built from small + pieces using a `SyncResultBuilder`. The `sync_result_builder` is passed as a + mutable ("inout") parameter to various helper functions. These retrieve and + process the data which forms the sync body, often writing to the + `sync_result_builder` to store their output. + + At the end, we transfer data from the `sync_result_builder` to a new `E2eeSyncResult` + instance to signify that the sync calculation is complete. + """ user_id = sync_config.user.to_string() # TODO: Should we exclude app services here? There could be an argument to allow @@ -1863,7 +1904,20 @@ class SyncHandler: full_state: bool = False, ) -> "SyncResultBuilder": """ - Assemble a `SyncResultBuilder` with all of the necessary context + Assemble a `SyncResultBuilder` with all of the initial context to + start building up the sync response: + + - Membership changes between the last sync and the current sync. + - Joined room IDs (minus any rooms to exclude). + - Rooms that became fully-stated/un-partial stated since the last sync. + + Args: + sync_config: Config/info necessary to process the sync request. + since_token: The point in the stream to sync from. + full_state: Whether to return the full state for each room. + + Returns: + `SyncResultBuilder` ready to start generating parts of the sync response. """ user_id = sync_config.user.to_string() diff --git a/synapse/rest/client/sync.py b/synapse/rest/client/sync.py index 30c5c13f1a..78d5709ecd 100644 --- a/synapse/rest/client/sync.py +++ b/synapse/rest/client/sync.py @@ -563,6 +563,12 @@ class SlidingSyncE2eeRestServlet(RestServlet): get E2EE events without having to sit through a big initial sync (`/sync` v2). And we can avoid encryption events being backed up by the main sync response. + Having To-Device messages split out to this sync endpoint also helps when clients + need to have 2 or more sync streams open at a time, e.g a push notification process + and a main process. This can cause the two processes to race to fetch the To-Device + events, resulting in the need for complex synchronisation rules to ensure the token + is correctly and atomically exchanged between processes. + GET parameters:: timeout(int): How long to wait for new events in milliseconds. since(batch_token): Batch token when asking for incremental deltas. @@ -624,8 +630,6 @@ class SlidingSyncE2eeRestServlet(RestServlet): if since is not None: since_token = await StreamToken.from_string(self.store, since) - logger.info(f"sync with since_token: {since_token}") - # Request cache key request_key = ( SyncVersion.E2EE_SYNC, diff --git a/tests/rest/client/test_sendtodevice_base.py b/tests/rest/client/test_sendtodevice_base.py index 62c6364a59..5677f4f280 100644 --- a/tests/rest/client/test_sendtodevice_base.py +++ b/tests/rest/client/test_sendtodevice_base.py @@ -27,10 +27,10 @@ from synapse.rest.client import login, sendtodevice, sync from synapse.server import HomeServer from synapse.util import Clock -from tests.unittest import override_config +from tests.unittest import HomeserverTestCase, override_config -class SendToDeviceTestCaseBase: +class SendToDeviceTestCaseBase(HomeserverTestCase): """ Test `/sendToDevice` will deliver messages across to people receiving them over `/sync`.