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

Faster room joins: Send device list updates out to servers in partially joined rooms #13874

Merged
merged 5 commits into from
Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/13874.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Faster room joins: Send device list updates to most servers in rooms with partial state.
6 changes: 5 additions & 1 deletion synapse/handlers/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,11 +688,15 @@ async def _handle_new_device_update_async(self) -> None:
# Ignore any users that aren't ours
if self.hs.is_mine_id(user_id):
hosts = set(
await self._storage_controllers.state.get_current_hosts_in_room(
await self._storage_controllers.state.get_current_hosts_in_room_or_partial_state_approximation(
room_id
)
)
hosts.discard(self.server_name)
# For rooms with partial state, `hosts` is merely an
# approximation. When we transition to a full state room, we
# will have to send out device list updates to any servers we
# missed.
Comment on lines +696 to +699
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This refers to the first todo in Erik's comment here: #12993 (comment)


# Check if we've already sent this update to some hosts
if current_stream_id == stream_id:
Expand Down
44 changes: 43 additions & 1 deletion synapse/storage/controllers/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
List,
Mapping,
Optional,
Sequence,
Tuple,
)

Expand Down Expand Up @@ -524,12 +525,53 @@ async def get_current_state_event(
return state_map.get(key)

async def get_current_hosts_in_room(self, room_id: str) -> List[str]:
"""Get current hosts in room based on current state."""
"""Get current hosts in room based on current state.

Blocks until we have full state for the given room. This only happens for rooms
with partial state.

Returns:
A list of hosts in the room, sorted by longest in the room first. (aka.
sorted by join with the lowest depth first).
"""

await self._partial_state_room_tracker.await_full_state(room_id)

return await self.stores.main.get_current_hosts_in_room(room_id)

async def get_current_hosts_in_room_or_partial_state_approximation(
self, room_id: str
) -> Sequence[str]:
MatMaul marked this conversation as resolved.
Show resolved Hide resolved
"""Get approximation of current hosts in room based on current state.

For rooms with full state, this is equivalent to `get_current_hosts_in_room`,
with the same order of results.

For rooms with partial state, no blocking occurs. Instead, the list of hosts
in the room at the time of joining is combined with the list of hosts which
joined the room afterwards. The returned list may include hosts that are not
actually in the room and exclude hosts that are in the room, since we may
calculate state incorrectly during the partial state phase. The order of results
is arbitrary for rooms with partial state.
"""
# We have to read this list first to mitigate races with un-partial stating.
# This will be empty for rooms with full state.
hosts_at_join = await self.stores.main.get_partial_state_servers_at_join(
room_id
)

hosts_from_state = await self.stores.main.get_current_hosts_in_room(room_id)
hosts_from_state_set = set(hosts_from_state)

# First take the list of hosts based on the current state.
# For rooms with partial state, this will be missing most hosts.
hosts = list(hosts_from_state)
# Then add in the list of hosts in the room at the time we joined.
# This will be an empty list for rooms with full state.
hosts.extend(host for host in hosts_at_join if host not in hosts_from_state_set)
MatMaul marked this conversation as resolved.
Show resolved Hide resolved

return hosts

async def get_users_in_room_with_profiles(
self, room_id: str
) -> Dict[str, ProfileInfo]:
Expand Down
17 changes: 17 additions & 0 deletions synapse/storage/databases/main/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
List,
Mapping,
Optional,
Sequence,
Tuple,
Union,
cast,
Expand Down Expand Up @@ -1133,6 +1134,22 @@ def get_rooms_for_retention_period_in_range_txn(
get_rooms_for_retention_period_in_range_txn,
)

async def get_partial_state_servers_at_join(self, room_id: str) -> Sequence[str]:
"""Gets the list of servers in a partial state room at the time we joined it.

Returns:
The `servers_in_room` list from the `/send_join` response for partial state
rooms. May not be accurate or complete, as it comes from a remote
homeserver.
An empty list for full state rooms.
"""
return await self.db_pool.simple_select_onecol(
"partial_state_rooms_servers",
keyvalues={"room_id": room_id},
retcol="server_name",
desc="get_partial_state_servers_at_join",
)

async def get_partial_state_rooms_and_servers(
self,
) -> Mapping[str, Collection[str]]:
Expand Down