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

Commit 0506bb1

Browse files
authored
Remove get rooms for user with stream ordering (#13991)
By getting the joined rooms before the current token we avoid any reading history to confirm a user *was* in a room. We can then use any membership change events, which we already fetch during sync, to determine the final list of joined room IDs.
1 parent 2b6d41e commit 0506bb1

File tree

2 files changed

+70
-80
lines changed

2 files changed

+70
-80
lines changed

changelog.d/13991.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Optimise queries used to get a users rooms during sync. Contributed by Nick @ Beeper (@fizzadar).

synapse/handlers/sync.py

Lines changed: 69 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,36 +1317,91 @@ async def generate_sync_result(
13171317
At the end, we transfer data from the `sync_result_builder` to a new `SyncResult`
13181318
instance to signify that the sync calculation is complete.
13191319
"""
1320+
1321+
user_id = sync_config.user.to_string()
1322+
app_service = self.store.get_app_service_by_user_id(user_id)
1323+
if app_service:
1324+
# We no longer support AS users using /sync directly.
1325+
# See https://github.com/matrix-org/matrix-doc/issues/1144
1326+
raise NotImplementedError()
1327+
1328+
# Note: we get the users room list *before* we get the current token, this
1329+
# avoids checking back in history if rooms are joined after the token is fetched.
1330+
token_before_rooms = self.event_sources.get_current_token()
1331+
mutable_joined_room_ids = set(await self.store.get_rooms_for_user(user_id))
1332+
13201333
# NB: The now_token gets changed by some of the generate_sync_* methods,
13211334
# this is due to some of the underlying streams not supporting the ability
13221335
# to query up to a given point.
13231336
# Always use the `now_token` in `SyncResultBuilder`
13241337
now_token = self.event_sources.get_current_token()
13251338
log_kv({"now_token": now_token})
13261339

1340+
# Since we fetched the users room list before the token, there's a small window
1341+
# during which membership events may have been persisted, so we fetch these now
1342+
# and modify the joined room list for any changes between the get_rooms_for_user
1343+
# call and the get_current_token call.
1344+
membership_change_events = []
1345+
if since_token:
1346+
membership_change_events = await self.store.get_membership_changes_for_user(
1347+
user_id, since_token.room_key, now_token.room_key, self.rooms_to_exclude
1348+
)
1349+
1350+
mem_last_change_by_room_id: Dict[str, EventBase] = {}
1351+
for event in membership_change_events:
1352+
mem_last_change_by_room_id[event.room_id] = event
1353+
1354+
# For the latest membership event in each room found, add/remove the room ID
1355+
# from the joined room list accordingly. In this case we only care if the
1356+
# latest change is JOIN.
1357+
1358+
for room_id, event in mem_last_change_by_room_id.items():
1359+
assert event.internal_metadata.stream_ordering
1360+
if (
1361+
event.internal_metadata.stream_ordering
1362+
< token_before_rooms.room_key.stream
1363+
):
1364+
continue
1365+
1366+
logger.info(
1367+
"User membership change between getting rooms and current token: %s %s %s",
1368+
user_id,
1369+
event.membership,
1370+
room_id,
1371+
)
1372+
# User joined a room - we have to then check the room state to ensure we
1373+
# respect any bans if there's a race between the join and ban events.
1374+
if event.membership == Membership.JOIN:
1375+
user_ids_in_room = await self.store.get_users_in_room(room_id)
1376+
if user_id in user_ids_in_room:
1377+
mutable_joined_room_ids.add(room_id)
1378+
# The user left the room, or left and was re-invited but not joined yet
1379+
else:
1380+
mutable_joined_room_ids.discard(room_id)
1381+
1382+
# Now we have our list of joined room IDs, exclude as configured and freeze
1383+
joined_room_ids = frozenset(
1384+
(
1385+
room_id
1386+
for room_id in mutable_joined_room_ids
1387+
if room_id not in self.rooms_to_exclude
1388+
)
1389+
)
1390+
13271391
logger.debug(
13281392
"Calculating sync response for %r between %s and %s",
13291393
sync_config.user,
13301394
since_token,
13311395
now_token,
13321396
)
13331397

1334-
user_id = sync_config.user.to_string()
1335-
app_service = self.store.get_app_service_by_user_id(user_id)
1336-
if app_service:
1337-
# We no longer support AS users using /sync directly.
1338-
# See https://github.com/matrix-org/matrix-doc/issues/1144
1339-
raise NotImplementedError()
1340-
else:
1341-
joined_room_ids = await self.get_rooms_for_user_at(
1342-
user_id, now_token.room_key
1343-
)
13441398
sync_result_builder = SyncResultBuilder(
13451399
sync_config,
13461400
full_state,
13471401
since_token=since_token,
13481402
now_token=now_token,
13491403
joined_room_ids=joined_room_ids,
1404+
membership_change_events=membership_change_events,
13501405
)
13511406

13521407
logger.debug("Fetching account data")
@@ -1827,19 +1882,12 @@ async def _have_rooms_changed(
18271882
18281883
Does not modify the `sync_result_builder`.
18291884
"""
1830-
user_id = sync_result_builder.sync_config.user.to_string()
18311885
since_token = sync_result_builder.since_token
1832-
now_token = sync_result_builder.now_token
1886+
membership_change_events = sync_result_builder.membership_change_events
18331887

18341888
assert since_token
18351889

1836-
# Get a list of membership change events that have happened to the user
1837-
# requesting the sync.
1838-
membership_changes = await self.store.get_membership_changes_for_user(
1839-
user_id, since_token.room_key, now_token.room_key
1840-
)
1841-
1842-
if membership_changes:
1890+
if membership_change_events:
18431891
return True
18441892

18451893
stream_id = since_token.room_key.stream
@@ -1878,16 +1926,10 @@ async def _get_rooms_changed(
18781926
since_token = sync_result_builder.since_token
18791927
now_token = sync_result_builder.now_token
18801928
sync_config = sync_result_builder.sync_config
1929+
membership_change_events = sync_result_builder.membership_change_events
18811930

18821931
assert since_token
18831932

1884-
# TODO: we've already called this function and ran this query in
1885-
# _have_rooms_changed. We could keep the results in memory to avoid a
1886-
# second query, at the cost of more complicated source code.
1887-
membership_change_events = await self.store.get_membership_changes_for_user(
1888-
user_id, since_token.room_key, now_token.room_key, self.rooms_to_exclude
1889-
)
1890-
18911933
mem_change_events_by_room_id: Dict[str, List[EventBase]] = {}
18921934
for event in membership_change_events:
18931935
mem_change_events_by_room_id.setdefault(event.room_id, []).append(event)
@@ -2415,60 +2457,6 @@ async def _generate_room_entry(
24152457
else:
24162458
raise Exception("Unrecognized rtype: %r", room_builder.rtype)
24172459

2418-
async def get_rooms_for_user_at(
2419-
self,
2420-
user_id: str,
2421-
room_key: RoomStreamToken,
2422-
) -> FrozenSet[str]:
2423-
"""Get set of joined rooms for a user at the given stream ordering.
2424-
2425-
The stream ordering *must* be recent, otherwise this may throw an
2426-
exception if older than a month. (This function is called with the
2427-
current token, which should be perfectly fine).
2428-
2429-
Args:
2430-
user_id
2431-
stream_ordering
2432-
2433-
ReturnValue:
2434-
Set of room_ids the user is in at given stream_ordering.
2435-
"""
2436-
joined_rooms = await self.store.get_rooms_for_user_with_stream_ordering(user_id)
2437-
2438-
joined_room_ids = set()
2439-
2440-
# We need to check that the stream ordering of the join for each room
2441-
# is before the stream_ordering asked for. This might not be the case
2442-
# if the user joins a room between us getting the current token and
2443-
# calling `get_rooms_for_user_with_stream_ordering`.
2444-
# If the membership's stream ordering is after the given stream
2445-
# ordering, we need to go and work out if the user was in the room
2446-
# before.
2447-
# We also need to check whether the room should be excluded from sync
2448-
# responses as per the homeserver config.
2449-
for joined_room in joined_rooms:
2450-
if joined_room.room_id in self.rooms_to_exclude:
2451-
continue
2452-
2453-
if not joined_room.event_pos.persisted_after(room_key):
2454-
joined_room_ids.add(joined_room.room_id)
2455-
continue
2456-
2457-
logger.info("User joined room after current token: %s", joined_room.room_id)
2458-
2459-
extrems = (
2460-
await self.store.get_forward_extremities_for_room_at_stream_ordering(
2461-
joined_room.room_id, joined_room.event_pos.stream
2462-
)
2463-
)
2464-
user_ids_in_room = await self.state.get_current_user_ids_in_room(
2465-
joined_room.room_id, extrems
2466-
)
2467-
if user_id in user_ids_in_room:
2468-
joined_room_ids.add(joined_room.room_id)
2469-
2470-
return frozenset(joined_room_ids)
2471-
24722460

24732461
def _action_has_highlight(actions: List[JsonDict]) -> bool:
24742462
for action in actions:
@@ -2565,6 +2553,7 @@ class SyncResultBuilder:
25652553
since_token: Optional[StreamToken]
25662554
now_token: StreamToken
25672555
joined_room_ids: FrozenSet[str]
2556+
membership_change_events: List[EventBase]
25682557

25692558
presence: List[UserPresenceState] = attr.Factory(list)
25702559
account_data: List[JsonDict] = attr.Factory(list)

0 commit comments

Comments
 (0)