Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit d26808d

Browse files
David Robertsonclokep
andauthored
Comments on the /sync tentacles (#11494)
This mainly consists of docstrings and inline comments. There are one or two type annotations and variable renames thrown in while I was here. Co-authored-by: Patrick Cloke <[email protected]>
1 parent f91624a commit d26808d

File tree

3 files changed

+129
-43
lines changed

3 files changed

+129
-43
lines changed

changelog.d/11494.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add comments to various parts of the `/sync` handler.

synapse/handlers/sync.py

Lines changed: 117 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -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(

synapse/storage/databases/main/stream.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ async def get_room_events_stream_for_room(
497497
oldest `limit` events.
498498
499499
Returns:
500-
The list of events (in ascending order) and the token from the start
500+
The list of events (in ascending stream order) and the token from the start
501501
of the chunk of events returned.
502502
"""
503503
if from_key == to_key:
@@ -510,7 +510,7 @@ async def get_room_events_stream_for_room(
510510
if not has_changed:
511511
return [], from_key
512512

513-
def f(txn):
513+
def f(txn: LoggingTransaction) -> List[_EventDictReturn]:
514514
# To handle tokens with a non-empty instance_map we fetch more
515515
# results than necessary and then filter down
516516
min_from_id = from_key.stream
@@ -565,6 +565,13 @@ def f(txn):
565565
async def get_membership_changes_for_user(
566566
self, user_id: str, from_key: RoomStreamToken, to_key: RoomStreamToken
567567
) -> List[EventBase]:
568+
"""Fetch membership events for a given user.
569+
570+
All such events whose stream ordering `s` lies in the range
571+
`from_key < s <= to_key` are returned. Events are ordered by ascending stream
572+
order.
573+
"""
574+
# Start by ruling out cases where a DB query is not necessary.
568575
if from_key == to_key:
569576
return []
570577

@@ -575,7 +582,7 @@ async def get_membership_changes_for_user(
575582
if not has_changed:
576583
return []
577584

578-
def f(txn):
585+
def f(txn: LoggingTransaction) -> List[_EventDictReturn]:
579586
# To handle tokens with a non-empty instance_map we fetch more
580587
# results than necessary and then filter down
581588
min_from_id = from_key.stream
@@ -634,7 +641,7 @@ async def get_recent_events_for_room(
634641
635642
Returns:
636643
A list of events and a token pointing to the start of the returned
637-
events. The events returned are in ascending order.
644+
events. The events returned are in ascending topological order.
638645
"""
639646

640647
rows, token = await self.get_recent_event_ids_for_room(

0 commit comments

Comments
 (0)