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

Commit a466164

Browse files
authored
Optimise get_rooms_for_user (drop with_stream_ordering) (#13787)
1 parent be76cd8 commit a466164

File tree

7 files changed

+66
-75
lines changed

7 files changed

+66
-75
lines changed

changelog.d/13787.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Optimise get rooms for user calls. Contributed by Nick @ Beeper (@fizzadar).

synapse/handlers/device.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,9 @@ async def get_user_ids_changed(
273273
possibly_left = possibly_changed | possibly_left
274274

275275
# Double check if we still share rooms with the given user.
276-
users_rooms = await self.store.get_rooms_for_users_with_stream_ordering(
277-
possibly_left
278-
)
276+
users_rooms = await self.store.get_rooms_for_users(possibly_left)
279277
for changed_user_id, entries in users_rooms.items():
280-
if any(e.room_id in room_ids for e in entries):
278+
if any(rid in room_ids for rid in entries):
281279
possibly_left.discard(changed_user_id)
282280
else:
283281
possibly_joined.discard(changed_user_id)

synapse/handlers/sync.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,16 +1490,14 @@ async def _generate_sync_entry_for_device_list(
14901490
since_token.device_list_key
14911491
)
14921492
if changed_users is not None:
1493-
result = await self.store.get_rooms_for_users_with_stream_ordering(
1494-
changed_users
1495-
)
1493+
result = await self.store.get_rooms_for_users(changed_users)
14961494

14971495
for changed_user_id, entries in result.items():
14981496
# Check if the changed user shares any rooms with the user,
14991497
# or if the changed user is the syncing user (as we always
15001498
# want to include device list updates of their own devices).
15011499
if user_id == changed_user_id or any(
1502-
e.room_id in joined_rooms for e in entries
1500+
rid in joined_rooms for rid in entries
15031501
):
15041502
users_that_have_changed.add(changed_user_id)
15051503
else:
@@ -1533,13 +1531,9 @@ async def _generate_sync_entry_for_device_list(
15331531
newly_left_users.update(left_users)
15341532

15351533
# Remove any users that we still share a room with.
1536-
left_users_rooms = (
1537-
await self.store.get_rooms_for_users_with_stream_ordering(
1538-
newly_left_users
1539-
)
1540-
)
1534+
left_users_rooms = await self.store.get_rooms_for_users(newly_left_users)
15411535
for user_id, entries in left_users_rooms.items():
1542-
if any(e.room_id in joined_rooms for e in entries):
1536+
if any(rid in joined_rooms for rid in entries):
15431537
newly_left_users.discard(user_id)
15441538

15451539
return DeviceListUpdates(

synapse/storage/_base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def _invalidate_state_caches(
9494
self._attempt_to_invalidate_cache(
9595
"get_rooms_for_user_with_stream_ordering", (user_id,)
9696
)
97+
self._attempt_to_invalidate_cache("get_rooms_for_user", (user_id,))
9798

9899
# Purge other caches based on room state.
99100
self._attempt_to_invalidate_cache("get_room_summary", (room_id,))

synapse/storage/databases/main/cache.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ def _process_event_stream_row(self, token: int, row: EventsStreamRow) -> None:
205205
self.get_rooms_for_user_with_stream_ordering.invalidate(
206206
(data.state_key,)
207207
)
208+
self.get_rooms_for_user.invalidate((data.state_key,))
208209
else:
209210
raise Exception("Unknown events stream row type %s" % (row.type,))
210211

synapse/storage/databases/main/roommember.py

Lines changed: 56 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import logging
1616
from typing import (
1717
TYPE_CHECKING,
18-
Callable,
1918
Collection,
2019
Dict,
2120
FrozenSet,
@@ -52,7 +51,6 @@
5251
from synapse.util.async_helpers import Linearizer
5352
from synapse.util.caches import intern_string
5453
from synapse.util.caches.descriptors import _CacheContext, cached, cachedList
55-
from synapse.util.cancellation import cancellable
5654
from synapse.util.iterutils import batch_iter
5755
from synapse.util.metrics import Measure
5856

@@ -600,58 +598,6 @@ def _get_rooms_for_user_with_stream_ordering_txn(
600598
for room_id, instance, stream_id in txn
601599
)
602600

603-
@cachedList(
604-
cached_method_name="get_rooms_for_user_with_stream_ordering",
605-
list_name="user_ids",
606-
)
607-
async def get_rooms_for_users_with_stream_ordering(
608-
self, user_ids: Collection[str]
609-
) -> Dict[str, FrozenSet[GetRoomsForUserWithStreamOrdering]]:
610-
"""A batched version of `get_rooms_for_user_with_stream_ordering`.
611-
612-
Returns:
613-
Map from user_id to set of rooms that is currently in.
614-
"""
615-
return await self.db_pool.runInteraction(
616-
"get_rooms_for_users_with_stream_ordering",
617-
self._get_rooms_for_users_with_stream_ordering_txn,
618-
user_ids,
619-
)
620-
621-
def _get_rooms_for_users_with_stream_ordering_txn(
622-
self, txn: LoggingTransaction, user_ids: Collection[str]
623-
) -> Dict[str, FrozenSet[GetRoomsForUserWithStreamOrdering]]:
624-
625-
clause, args = make_in_list_sql_clause(
626-
self.database_engine,
627-
"c.state_key",
628-
user_ids,
629-
)
630-
631-
sql = f"""
632-
SELECT c.state_key, room_id, e.instance_name, e.stream_ordering
633-
FROM current_state_events AS c
634-
INNER JOIN events AS e USING (room_id, event_id)
635-
WHERE
636-
c.type = 'm.room.member'
637-
AND c.membership = ?
638-
AND {clause}
639-
"""
640-
641-
txn.execute(sql, [Membership.JOIN] + args)
642-
643-
result: Dict[str, Set[GetRoomsForUserWithStreamOrdering]] = {
644-
user_id: set() for user_id in user_ids
645-
}
646-
for user_id, room_id, instance, stream_id in txn:
647-
result[user_id].add(
648-
GetRoomsForUserWithStreamOrdering(
649-
room_id, PersistedEventPosition(instance, stream_id)
650-
)
651-
)
652-
653-
return {user_id: frozenset(v) for user_id, v in result.items()}
654-
655601
async def get_users_server_still_shares_room_with(
656602
self, user_ids: Collection[str]
657603
) -> Set[str]:
@@ -693,19 +639,68 @@ def get_users_server_still_shares_room_with_txn(
693639

694640
return {row[0] for row in txn}
695641

696-
@cancellable
697-
async def get_rooms_for_user(
698-
self, user_id: str, on_invalidate: Optional[Callable[[], None]] = None
699-
) -> FrozenSet[str]:
642+
@cached(max_entries=500000, iterable=True)
643+
async def get_rooms_for_user(self, user_id: str) -> FrozenSet[str]:
700644
"""Returns a set of room_ids the user is currently joined to.
701645
702646
If a remote user only returns rooms this server is currently
703647
participating in.
704648
"""
705-
rooms = await self.get_rooms_for_user_with_stream_ordering(
706-
user_id, on_invalidate=on_invalidate
649+
rooms = self.get_rooms_for_user_with_stream_ordering.cache.get_immediate(
650+
(user_id,),
651+
None,
652+
update_metrics=False,
653+
)
654+
if rooms:
655+
return frozenset(r.room_id for r in rooms)
656+
657+
room_ids = await self.db_pool.simple_select_onecol(
658+
table="current_state_events",
659+
keyvalues={
660+
"type": EventTypes.Member,
661+
"membership": Membership.JOIN,
662+
"state_key": user_id,
663+
},
664+
retcol="room_id",
665+
desc="get_rooms_for_user",
707666
)
708-
return frozenset(r.room_id for r in rooms)
667+
668+
return frozenset(room_ids)
669+
670+
@cachedList(
671+
cached_method_name="get_rooms_for_user",
672+
list_name="user_ids",
673+
)
674+
async def get_rooms_for_users(
675+
self, user_ids: Collection[str]
676+
) -> Dict[str, FrozenSet[str]]:
677+
"""A batched version of `get_rooms_for_user`.
678+
679+
Returns:
680+
Map from user_id to set of rooms that is currently in.
681+
"""
682+
683+
rows = await self.db_pool.simple_select_many_batch(
684+
table="current_state_events",
685+
column="state_key",
686+
iterable=user_ids,
687+
retcols=(
688+
"state_key",
689+
"room_id",
690+
),
691+
keyvalues={
692+
"type": EventTypes.Member,
693+
"membership": Membership.JOIN,
694+
},
695+
desc="get_rooms_for_users",
696+
)
697+
698+
user_rooms: Dict[str, Set[str]] = {user_id: set() for user_id in user_ids}
699+
700+
for row in rows:
701+
user_rooms[row["state_key"]].add(row["room_id"])
702+
703+
return {key: frozenset(rooms) for key, rooms in user_rooms.items()}
709704

710705
@cached(max_entries=10000)
711706
async def does_pair_of_users_share_a_room(

tests/handlers/test_sync.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ def test_unknown_room_version(self):
159159

160160
# Blow away caches (supported room versions can only change due to a restart).
161161
self.store.get_rooms_for_user_with_stream_ordering.invalidate_all()
162+
self.store.get_rooms_for_user.invalidate_all()
162163
self.get_success(self.store._get_event_cache.clear())
163164
self.store._event_ref.clear()
164165

0 commit comments

Comments
 (0)