@@ -334,6 +334,19 @@ async def _wait_for_sync_for_user(
334334 full_state : bool ,
335335 cache_context : ResponseCacheContext [SyncRequestKey ],
336336 ) -> SyncResult :
337+ """The start of the machinery that produces a /sync response.
338+
339+ See https://spec.matrix.org/v1.1/client-server-api/#syncing for full details.
340+
341+ This method does high-level bookkeeping:
342+ - tracking the kind of sync in the logging context
343+ - deleting any to_device messages whose delivery has been acknowledged.
344+ - deciding if we should dispatch an instant or delayed response
345+ - marking the sync as being lazily loaded, if appropriate
346+
347+ Computing the body of the response begins in the next method,
348+ `current_sync_for_user`.
349+ """
337350 if since_token is None :
338351 sync_type = "initial_sync"
339352 elif full_state :
@@ -363,7 +376,7 @@ async def _wait_for_sync_for_user(
363376 sync_config , since_token , full_state = full_state
364377 )
365378 else :
366-
379+ # Otherwise, we wait for something to happen and report it to the user.
367380 async def current_sync_callback (
368381 before_token : StreamToken , after_token : StreamToken
369382 ) -> SyncResult :
@@ -402,7 +415,12 @@ async def current_sync_for_user(
402415 since_token : Optional [StreamToken ] = None ,
403416 full_state : bool = False ,
404417 ) -> SyncResult :
405- """Get the sync for client needed to match what the server has now."""
418+ """Generates the response body of a sync result, represented as a SyncResult.
419+
420+ This is a wrapper around `generate_sync_result` which starts an open tracing
421+ span to track the sync. See `generate_sync_result` for the next part of your
422+ indoctrination.
423+ """
406424 with start_active_span ("current_sync_for_user" ):
407425 log_kv ({"since_token" : since_token })
408426 sync_result = await self .generate_sync_result (
@@ -560,7 +578,7 @@ async def _load_filtered_recents(
560578 # that have happened since `since_key` up to `end_key`, so we
561579 # can just use `get_room_events_stream_for_room`.
562580 # Otherwise, we want to return the last N events in the room
563- # in toplogical ordering.
581+ # in topological ordering.
564582 if since_key :
565583 events , end_key = await self .store .get_room_events_stream_for_room (
566584 room_id ,
@@ -1042,7 +1060,18 @@ async def generate_sync_result(
10421060 since_token : Optional [StreamToken ] = None ,
10431061 full_state : bool = False ,
10441062 ) -> SyncResult :
1045- """Generates a sync result."""
1063+ """Generates the response body of a sync result.
1064+
1065+ This is represented by a `SyncResult` struct, which is built from small pieces
1066+ using a `SyncResultBuilder`. See also
1067+ https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3sync
1068+ the `sync_result_builder` is passed as a mutable ("inout") parameter to various
1069+ helper functions. These retrieve and process the data which forms the sync body,
1070+ often writing to the `sync_result_builder` to store their output.
1071+
1072+ At the end, we transfer data from the `sync_result_builder` to a new `SyncResult`
1073+ instance to signify that the sync calculation is complete.
1074+ """
10461075 # NB: The now_token gets changed by some of the generate_sync_* methods,
10471076 # this is due to some of the underlying streams not supporting the ability
10481077 # to query up to a given point.
@@ -1344,22 +1373,30 @@ async def _generate_sync_entry_for_to_device(
13441373 async def _generate_sync_entry_for_account_data (
13451374 self , sync_result_builder : "SyncResultBuilder"
13461375 ) -> Dict [str , Dict [str , JsonDict ]]:
1347- """Generates the account data portion of the sync response. Populates
1348- `sync_result_builder` with the result.
1376+ """Generates the account data portion of the sync response.
1377+
1378+ Account data (called "Client Config" in the spec) can be set either globally
1379+ or for a specific room. Account data consists of a list of events which
1380+ accumulate state, much like a room.
1381+
1382+ This function retrieves global and per-room account data. The former is written
1383+ to the given `sync_result_builder`. The latter is returned directly, to be
1384+ later written to the `sync_result_builder` on a room-by-room basis.
13491385
13501386 Args:
13511387 sync_result_builder
13521388
13531389 Returns:
1354- A dictionary containing the per room account data.
1390+ A dictionary whose keys (room ids) map to the per room account data for that
1391+ room.
13551392 """
13561393 sync_config = sync_result_builder .sync_config
13571394 user_id = sync_result_builder .sync_config .user .to_string ()
13581395 since_token = sync_result_builder .since_token
13591396
13601397 if since_token and not sync_result_builder .full_state :
13611398 (
1362- account_data ,
1399+ global_account_data ,
13631400 account_data_by_room ,
13641401 ) = await self .store .get_updated_account_data_for_user (
13651402 user_id , since_token .account_data_key
@@ -1370,23 +1407,23 @@ async def _generate_sync_entry_for_account_data(
13701407 )
13711408
13721409 if push_rules_changed :
1373- account_data ["m.push_rules" ] = await self .push_rules_for_user (
1410+ global_account_data ["m.push_rules" ] = await self .push_rules_for_user (
13741411 sync_config .user
13751412 )
13761413 else :
13771414 (
1378- account_data ,
1415+ global_account_data ,
13791416 account_data_by_room ,
13801417 ) = await self .store .get_account_data_for_user (sync_config .user .to_string ())
13811418
1382- account_data ["m.push_rules" ] = await self .push_rules_for_user (
1419+ global_account_data ["m.push_rules" ] = await self .push_rules_for_user (
13831420 sync_config .user
13841421 )
13851422
13861423 account_data_for_user = await sync_config .filter_collection .filter_account_data (
13871424 [
13881425 {"type" : account_data_type , "content" : content }
1389- for account_data_type , content in account_data .items ()
1426+ for account_data_type , content in global_account_data .items ()
13901427 ]
13911428 )
13921429
@@ -1460,15 +1497,22 @@ async def _generate_sync_entry_for_rooms(
14601497 """Generates the rooms portion of the sync response. Populates the
14611498 `sync_result_builder` with the result.
14621499
1500+ In the response that reaches the client, rooms are divided into four categories:
1501+ `invite`, `join`, `knock`, `leave`. These aren't the same as the four sets of
1502+ room ids returned by this function.
1503+
14631504 Args:
14641505 sync_result_builder
14651506 account_data_by_room: Dictionary of per room account data
14661507
14671508 Returns:
1468- Returns a 4-tuple of
1469- `(newly_joined_rooms, newly_joined_or_invited_users,
1470- newly_left_rooms, newly_left_users)`
1509+ Returns a 4-tuple whose entries are:
1510+ - newly_joined_rooms
1511+ - newly_joined_or_invited_or_knocked_users
1512+ - newly_left_rooms
1513+ - newly_left_users
14711514 """
1515+ # Start by fetching all ephemeral events in rooms we've joined (if required).
14721516 user_id = sync_result_builder .sync_config .user .to_string ()
14731517 block_all_room_ephemeral = (
14741518 sync_result_builder .since_token is None
@@ -1590,19 +1634,22 @@ async def _have_rooms_changed(
15901634 ) -> bool :
15911635 """Returns whether there may be any new events that should be sent down
15921636 the sync. Returns True if there are.
1637+
1638+ Does not modify the `sync_result_builder`.
15931639 """
15941640 user_id = sync_result_builder .sync_config .user .to_string ()
15951641 since_token = sync_result_builder .since_token
15961642 now_token = sync_result_builder .now_token
15971643
15981644 assert since_token
15991645
1600- # Get a list of membership change events that have happened.
1601- rooms_changed = await self .store .get_membership_changes_for_user (
1646+ # Get a list of membership change events that have happened to the user
1647+ # requesting the sync.
1648+ membership_changes = await self .store .get_membership_changes_for_user (
16021649 user_id , since_token .room_key , now_token .room_key
16031650 )
16041651
1605- if rooms_changed :
1652+ if membership_changes :
16061653 return True
16071654
16081655 stream_id = since_token .room_key .stream
@@ -1614,29 +1661,62 @@ async def _have_rooms_changed(
16141661 async def _get_rooms_changed (
16151662 self , sync_result_builder : "SyncResultBuilder" , ignored_users : FrozenSet [str ]
16161663 ) -> _RoomChanges :
1617- """Gets the the changes that have happened since the last sync."""
1664+ """Determine the changes in rooms to report to the user.
1665+
1666+ Ideally, we want to report all events whose stream ordering `s` lies in the
1667+ range `since_token < s <= now_token`, where the two tokens are read from the
1668+ sync_result_builder.
1669+
1670+ If there are too many events in that range to report, things get complicated.
1671+ In this situation we return a truncated list of the most recent events, and
1672+ indicate in the response that there is a "gap" of omitted events. Additionally:
1673+
1674+ - we include a "state_delta", to describe the changes in state over the gap,
1675+ - we include all membership events applying to the user making the request,
1676+ even those in the gap.
1677+
1678+ See the spec for the rationale:
1679+ https://spec.matrix.org/v1.1/client-server-api/#syncing
1680+
1681+ The sync_result_builder is not modified by this function.
1682+ """
16181683 user_id = sync_result_builder .sync_config .user .to_string ()
16191684 since_token = sync_result_builder .since_token
16201685 now_token = sync_result_builder .now_token
16211686 sync_config = sync_result_builder .sync_config
16221687
16231688 assert since_token
16241689
1625- # Get a list of membership change events that have happened.
1626- rooms_changed = await self .store .get_membership_changes_for_user (
1690+ # The spec
1691+ # https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3sync
1692+ # notes that membership events need special consideration:
1693+ #
1694+ # > When a sync is limited, the server MUST return membership events for events
1695+ # > in the gap (between since and the start of the returned timeline), regardless
1696+ # > as to whether or not they are redundant.
1697+ #
1698+ # We fetch such events here, but we only seem to use them for categorising rooms
1699+ # as newly joined, newly left, invited or knocked.
1700+ # TODO: we've already called this function and ran this query in
1701+ # _have_rooms_changed. We could keep the results in memory to avoid a
1702+ # second query, at the cost of more complicated source code.
1703+ membership_change_events = await self .store .get_membership_changes_for_user (
16271704 user_id , since_token .room_key , now_token .room_key
16281705 )
16291706
16301707 mem_change_events_by_room_id : Dict [str , List [EventBase ]] = {}
1631- for event in rooms_changed :
1708+ for event in membership_change_events :
16321709 mem_change_events_by_room_id .setdefault (event .room_id , []).append (event )
16331710
1634- newly_joined_rooms = []
1635- newly_left_rooms = []
1636- room_entries = []
1637- invited = []
1638- knocked = []
1711+ newly_joined_rooms : List [ str ] = []
1712+ newly_left_rooms : List [ str ] = []
1713+ room_entries : List [ RoomSyncResultBuilder ] = []
1714+ invited : List [ InvitedSyncResult ] = []
1715+ knocked : List [ KnockedSyncResult ] = []
16391716 for room_id , events in mem_change_events_by_room_id .items ():
1717+ # The body of this loop will add this room to at least one of the five lists
1718+ # above. Things get messy if you've e.g. joined, left, joined then left the
1719+ # room all in the same sync period.
16401720 logger .debug (
16411721 "Membership changes in %s: [%s]" ,
16421722 room_id ,
@@ -1781,7 +1861,9 @@ async def _get_rooms_changed(
17811861
17821862 timeline_limit = sync_config .filter_collection .timeline_limit ()
17831863
1784- # Get all events for rooms we're currently joined to.
1864+ # Get all events since the `from_key` in rooms we're currently joined to.
1865+ # If there are too many, we get the most recent events only. This leaves
1866+ # a "gap" in the timeline, as described by the spec for /sync.
17851867 room_to_events = await self .store .get_room_events_stream_for_rooms (
17861868 room_ids = sync_result_builder .joined_room_ids ,
17871869 from_key = since_token .room_key ,
@@ -1842,6 +1924,10 @@ async def _get_all_rooms(
18421924 ) -> _RoomChanges :
18431925 """Returns entries for all rooms for the user.
18441926
1927+ Like `_get_rooms_changed`, but assumes the `since_token` is `None`.
1928+
1929+ This function does not modify the sync_result_builder.
1930+
18451931 Args:
18461932 sync_result_builder
18471933 ignored_users: Set of users ignored by user.
@@ -1853,16 +1939,9 @@ async def _get_all_rooms(
18531939 now_token = sync_result_builder .now_token
18541940 sync_config = sync_result_builder .sync_config
18551941
1856- membership_list = (
1857- Membership .INVITE ,
1858- Membership .KNOCK ,
1859- Membership .JOIN ,
1860- Membership .LEAVE ,
1861- Membership .BAN ,
1862- )
1863-
18641942 room_list = await self .store .get_rooms_for_local_user_where_membership_is (
1865- user_id = user_id , membership_list = membership_list
1943+ user_id = user_id ,
1944+ membership_list = Membership .LIST ,
18661945 )
18671946
18681947 room_entries = []
@@ -2212,8 +2291,7 @@ def _calculate_state(
22122291 # to only include membership events for the senders in the timeline.
22132292 # In practice, we can do this by removing them from the p_ids list,
22142293 # which is the list of relevant state we know we have already sent to the client.
2215- # see https://github.com/matrix-org/synapse/pull/2970
2216- # /files/efcdacad7d1b7f52f879179701c7e0d9b763511f#r204732809
2294+ # see https://github.com/matrix-org/synapse/pull/2970/files/efcdacad7d1b7f52f879179701c7e0d9b763511f#r204732809
22172295
22182296 if lazy_load_members :
22192297 p_ids .difference_update (
0 commit comments