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

Commit 306ecd6

Browse files
clokepDavid Robertson
authored andcommitted
Skip unused calculations in sync handler. (#14908)
If a sync request does not need to calculate per-room entries & is not generating presence & is not generating device list data (e.g. during initial sync) avoid the expensive calculation of room specific data. This is a micro-optimisation for clients syncing simply to receive to-device information.
1 parent 0f63c73 commit 306ecd6

File tree

3 files changed

+137
-125
lines changed

3 files changed

+137
-125
lines changed

changelog.d/14908.misc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve performance of `/sync` in a few situations.

synapse/api/filtering.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,9 @@ async def filter_room_account_data(
283283
await self._room_filter.filter(events)
284284
)
285285

286+
def blocks_all_rooms(self) -> bool:
287+
return self._room_filter.filters_all_rooms()
288+
286289
def blocks_all_presence(self) -> bool:
287290
return (
288291
self._presence_filter.filters_all_types()

synapse/handlers/sync.py

Lines changed: 133 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,41 +1448,67 @@ async def generate_sync_result(
14481448
sync_result_builder
14491449
)
14501450

1451-
logger.debug("Fetching room data")
1452-
1453-
(
1454-
newly_joined_rooms,
1455-
newly_joined_or_invited_or_knocked_users,
1456-
newly_left_rooms,
1457-
newly_left_users,
1458-
) = await self._generate_sync_entry_for_rooms(
1459-
sync_result_builder, account_data_by_room
1460-
)
1461-
14621451
# Presence data is included if the server has it enabled and not filtered out.
1463-
include_presence_data = (
1452+
include_presence_data = bool(
14641453
self.hs_config.server.use_presence
14651454
and not sync_config.filter_collection.blocks_all_presence()
14661455
)
1467-
if include_presence_data:
1468-
logger.debug("Fetching presence data")
1469-
await self._generate_sync_entry_for_presence(
1470-
sync_result_builder,
1456+
# Device list updates are sent if a since token is provided.
1457+
include_device_list_updates = bool(since_token and since_token.device_list_key)
1458+
1459+
# If we do not care about the rooms or things which depend on the room
1460+
# data (namely presence and device list updates), then we can skip
1461+
# this process completely.
1462+
device_lists = DeviceListUpdates()
1463+
if (
1464+
not sync_result_builder.sync_config.filter_collection.blocks_all_rooms()
1465+
or include_presence_data
1466+
or include_device_list_updates
1467+
):
1468+
logger.debug("Fetching room data")
1469+
1470+
# Note that _generate_sync_entry_for_rooms sets sync_result_builder.joined, which
1471+
# is used in calculate_user_changes below.
1472+
(
14711473
newly_joined_rooms,
1472-
newly_joined_or_invited_or_knocked_users,
1474+
newly_left_rooms,
1475+
) = await self._generate_sync_entry_for_rooms(
1476+
sync_result_builder, account_data_by_room
14731477
)
14741478

1479+
# Work out which users have joined or left rooms we're in. We use this
1480+
# to build the presence and device_list parts of the sync response in
1481+
# `_generate_sync_entry_for_presence` and
1482+
# `_generate_sync_entry_for_device_list` respectively.
1483+
if include_presence_data or include_device_list_updates:
1484+
# This uses the sync_result_builder.joined which is set in
1485+
# `_generate_sync_entry_for_rooms`, if that didn't find any joined
1486+
# rooms for some reason it is a no-op.
1487+
(
1488+
newly_joined_or_invited_or_knocked_users,
1489+
newly_left_users,
1490+
) = sync_result_builder.calculate_user_changes()
1491+
1492+
if include_presence_data:
1493+
logger.debug("Fetching presence data")
1494+
await self._generate_sync_entry_for_presence(
1495+
sync_result_builder,
1496+
newly_joined_rooms,
1497+
newly_joined_or_invited_or_knocked_users,
1498+
)
1499+
1500+
if include_device_list_updates:
1501+
device_lists = await self._generate_sync_entry_for_device_list(
1502+
sync_result_builder,
1503+
newly_joined_rooms=newly_joined_rooms,
1504+
newly_joined_or_invited_or_knocked_users=newly_joined_or_invited_or_knocked_users,
1505+
newly_left_rooms=newly_left_rooms,
1506+
newly_left_users=newly_left_users,
1507+
)
1508+
14751509
logger.debug("Fetching to-device data")
14761510
await self._generate_sync_entry_for_to_device(sync_result_builder)
14771511

1478-
device_lists = await self._generate_sync_entry_for_device_list(
1479-
sync_result_builder,
1480-
newly_joined_rooms=newly_joined_rooms,
1481-
newly_joined_or_invited_or_knocked_users=newly_joined_or_invited_or_knocked_users,
1482-
newly_left_rooms=newly_left_rooms,
1483-
newly_left_users=newly_left_users,
1484-
)
1485-
14861512
logger.debug("Fetching OTK data")
14871513
device_id = sync_config.device_id
14881514
one_time_keys_count: JsonDict = {}
@@ -1551,99 +1577,93 @@ async def _generate_sync_entry_for_device_list(
15511577

15521578
user_id = sync_result_builder.sync_config.user.to_string()
15531579
since_token = sync_result_builder.since_token
1580+
assert since_token is not None
15541581

15551582
# Take a copy since these fields will be mutated later.
15561583
newly_joined_or_invited_or_knocked_users = set(
15571584
newly_joined_or_invited_or_knocked_users
15581585
)
15591586
newly_left_users = set(newly_left_users)
15601587

1561-
if since_token and since_token.device_list_key:
1562-
# We want to figure out what user IDs the client should refetch
1563-
# device keys for, and which users we aren't going to track changes
1564-
# for anymore.
1565-
#
1566-
# For the first step we check:
1567-
# a. if any users we share a room with have updated their devices,
1568-
# and
1569-
# b. we also check if we've joined any new rooms, or if a user has
1570-
# joined a room we're in.
1571-
#
1572-
# For the second step we just find any users we no longer share a
1573-
# room with by looking at all users that have left a room plus users
1574-
# that were in a room we've left.
1588+
# We want to figure out what user IDs the client should refetch
1589+
# device keys for, and which users we aren't going to track changes
1590+
# for anymore.
1591+
#
1592+
# For the first step we check:
1593+
# a. if any users we share a room with have updated their devices,
1594+
# and
1595+
# b. we also check if we've joined any new rooms, or if a user has
1596+
# joined a room we're in.
1597+
#
1598+
# For the second step we just find any users we no longer share a
1599+
# room with by looking at all users that have left a room plus users
1600+
# that were in a room we've left.
15751601

1576-
users_that_have_changed = set()
1602+
users_that_have_changed = set()
15771603

1578-
joined_rooms = sync_result_builder.joined_room_ids
1604+
joined_rooms = sync_result_builder.joined_room_ids
15791605

1580-
# Step 1a, check for changes in devices of users we share a room
1581-
# with
1582-
#
1583-
# We do this in two different ways depending on what we have cached.
1584-
# If we already have a list of all the user that have changed since
1585-
# the last sync then it's likely more efficient to compare the rooms
1586-
# they're in with the rooms the syncing user is in.
1587-
#
1588-
# If we don't have that info cached then we get all the users that
1589-
# share a room with our user and check if those users have changed.
1590-
cache_result = self.store.get_cached_device_list_changes(
1591-
since_token.device_list_key
1592-
)
1593-
if cache_result.hit:
1594-
changed_users = cache_result.entities
1595-
1596-
result = await self.store.get_rooms_for_users(changed_users)
1597-
1598-
for changed_user_id, entries in result.items():
1599-
# Check if the changed user shares any rooms with the user,
1600-
# or if the changed user is the syncing user (as we always
1601-
# want to include device list updates of their own devices).
1602-
if user_id == changed_user_id or any(
1603-
rid in joined_rooms for rid in entries
1604-
):
1605-
users_that_have_changed.add(changed_user_id)
1606-
else:
1607-
users_that_have_changed = (
1608-
await self._device_handler.get_device_changes_in_shared_rooms(
1609-
user_id,
1610-
sync_result_builder.joined_room_ids,
1611-
from_token=since_token,
1612-
)
1613-
)
1614-
1615-
# Step 1b, check for newly joined rooms
1616-
for room_id in newly_joined_rooms:
1617-
joined_users = await self.store.get_users_in_room(room_id)
1618-
newly_joined_or_invited_or_knocked_users.update(joined_users)
1606+
# Step 1a, check for changes in devices of users we share a room
1607+
# with
1608+
#
1609+
# We do this in two different ways depending on what we have cached.
1610+
# If we already have a list of all the user that have changed since
1611+
# the last sync then it's likely more efficient to compare the rooms
1612+
# they're in with the rooms the syncing user is in.
1613+
#
1614+
# If we don't have that info cached then we get all the users that
1615+
# share a room with our user and check if those users have changed.
1616+
cache_result = self.store.get_cached_device_list_changes(
1617+
since_token.device_list_key
1618+
)
1619+
if cache_result.hit:
1620+
changed_users = cache_result.entities
16191621

1620-
# TODO: Check that these users are actually new, i.e. either they
1621-
# weren't in the previous sync *or* they left and rejoined.
1622-
users_that_have_changed.update(newly_joined_or_invited_or_knocked_users)
1622+
result = await self.store.get_rooms_for_users(changed_users)
16231623

1624-
user_signatures_changed = (
1625-
await self.store.get_users_whose_signatures_changed(
1626-
user_id, since_token.device_list_key
1624+
for changed_user_id, entries in result.items():
1625+
# Check if the changed user shares any rooms with the user,
1626+
# or if the changed user is the syncing user (as we always
1627+
# want to include device list updates of their own devices).
1628+
if user_id == changed_user_id or any(
1629+
rid in joined_rooms for rid in entries
1630+
):
1631+
users_that_have_changed.add(changed_user_id)
1632+
else:
1633+
users_that_have_changed = (
1634+
await self._device_handler.get_device_changes_in_shared_rooms(
1635+
user_id,
1636+
sync_result_builder.joined_room_ids,
1637+
from_token=since_token,
16271638
)
16281639
)
1629-
users_that_have_changed.update(user_signatures_changed)
16301640

1631-
# Now find users that we no longer track
1632-
for room_id in newly_left_rooms:
1633-
left_users = await self.store.get_users_in_room(room_id)
1634-
newly_left_users.update(left_users)
1641+
# Step 1b, check for newly joined rooms
1642+
for room_id in newly_joined_rooms:
1643+
joined_users = await self.store.get_users_in_room(room_id)
1644+
newly_joined_or_invited_or_knocked_users.update(joined_users)
16351645

1636-
# Remove any users that we still share a room with.
1637-
left_users_rooms = await self.store.get_rooms_for_users(newly_left_users)
1638-
for user_id, entries in left_users_rooms.items():
1639-
if any(rid in joined_rooms for rid in entries):
1640-
newly_left_users.discard(user_id)
1646+
# TODO: Check that these users are actually new, i.e. either they
1647+
# weren't in the previous sync *or* they left and rejoined.
1648+
users_that_have_changed.update(newly_joined_or_invited_or_knocked_users)
16411649

1642-
return DeviceListUpdates(
1643-
changed=users_that_have_changed, left=newly_left_users
1644-
)
1645-
else:
1646-
return DeviceListUpdates()
1650+
user_signatures_changed = await self.store.get_users_whose_signatures_changed(
1651+
user_id, since_token.device_list_key
1652+
)
1653+
users_that_have_changed.update(user_signatures_changed)
1654+
1655+
# Now find users that we no longer track
1656+
for room_id in newly_left_rooms:
1657+
left_users = await self.store.get_users_in_room(room_id)
1658+
newly_left_users.update(left_users)
1659+
1660+
# Remove any users that we still share a room with.
1661+
left_users_rooms = await self.store.get_rooms_for_users(newly_left_users)
1662+
for user_id, entries in left_users_rooms.items():
1663+
if any(rid in joined_rooms for rid in entries):
1664+
newly_left_users.discard(user_id)
1665+
1666+
return DeviceListUpdates(changed=users_that_have_changed, left=newly_left_users)
16471667

16481668
@trace
16491669
async def _generate_sync_entry_for_to_device(
@@ -1720,6 +1740,7 @@ async def _generate_sync_entry_for_account_data(
17201740
since_token = sync_result_builder.since_token
17211741

17221742
if since_token and not sync_result_builder.full_state:
1743+
# TODO Do not fetch room account data if it will be unused.
17231744
(
17241745
global_account_data,
17251746
account_data_by_room,
@@ -1736,6 +1757,7 @@ async def _generate_sync_entry_for_account_data(
17361757
sync_config.user
17371758
)
17381759
else:
1760+
# TODO Do not fetch room account data if it will be unused.
17391761
(
17401762
global_account_data,
17411763
account_data_by_room,
@@ -1818,7 +1840,7 @@ async def _generate_sync_entry_for_rooms(
18181840
self,
18191841
sync_result_builder: "SyncResultBuilder",
18201842
account_data_by_room: Dict[str, Dict[str, JsonDict]],
1821-
) -> Tuple[AbstractSet[str], AbstractSet[str], AbstractSet[str], AbstractSet[str]]:
1843+
) -> Tuple[AbstractSet[str], AbstractSet[str]]:
18221844
"""Generates the rooms portion of the sync response. Populates the
18231845
`sync_result_builder` with the result.
18241846
@@ -1831,24 +1853,22 @@ async def _generate_sync_entry_for_rooms(
18311853
account_data_by_room: Dictionary of per room account data
18321854
18331855
Returns:
1834-
Returns a 4-tuple describing rooms the user has joined or left, and users who've
1835-
joined or left rooms any rooms the user is in. This gets used later in
1836-
`_generate_sync_entry_for_device_list`.
1856+
Returns a 2-tuple describing rooms the user has joined or left.
18371857
18381858
Its entries are:
18391859
- newly_joined_rooms
1840-
- newly_joined_or_invited_or_knocked_users
18411860
- newly_left_rooms
1842-
- newly_left_users
18431861
"""
18441862

18451863
since_token = sync_result_builder.since_token
18461864
user_id = sync_result_builder.sync_config.user.to_string()
18471865

18481866
# 1. Start by fetching all ephemeral events in rooms we've joined (if required).
1849-
if (
1850-
sync_result_builder.sync_config.filter_collection.blocks_all_room_ephemeral()
1851-
):
1867+
block_all_room_ephemeral = (
1868+
sync_result_builder.sync_config.filter_collection.blocks_all_rooms()
1869+
or sync_result_builder.sync_config.filter_collection.blocks_all_room_ephemeral()
1870+
)
1871+
if block_all_room_ephemeral:
18521872
ephemeral_by_room: Dict[str, List[JsonDict]] = {}
18531873
else:
18541874
now_token, ephemeral_by_room = await self.ephemeral_by_room(
@@ -1870,7 +1890,7 @@ async def _generate_sync_entry_for_rooms(
18701890
)
18711891
if not tags_by_room:
18721892
logger.debug("no-oping sync")
1873-
return set(), set(), set(), set()
1893+
return set(), set()
18741894

18751895
# 3. Work out which rooms need reporting in the sync response.
18761896
ignored_users = await self.store.ignored_users(user_id)
@@ -1899,6 +1919,7 @@ async def _generate_sync_entry_for_rooms(
18991919
# joined or archived).
19001920
async def handle_room_entries(room_entry: "RoomSyncResultBuilder") -> None:
19011921
logger.debug("Generating room entry for %s", room_entry.room_id)
1922+
# Note that this mutates sync_result_builder.{joined,archived}.
19021923
await self._generate_room_entry(
19031924
sync_result_builder,
19041925
room_entry,
@@ -1915,20 +1936,7 @@ async def handle_room_entries(room_entry: "RoomSyncResultBuilder") -> None:
19151936
sync_result_builder.invited.extend(invited)
19161937
sync_result_builder.knocked.extend(knocked)
19171938

1918-
# 5. Work out which users have joined or left rooms we're in. We use this
1919-
# to build the device_list part of the sync response in
1920-
# `_generate_sync_entry_for_device_list`.
1921-
(
1922-
newly_joined_or_invited_or_knocked_users,
1923-
newly_left_users,
1924-
) = sync_result_builder.calculate_user_changes()
1925-
1926-
return (
1927-
set(newly_joined_rooms),
1928-
newly_joined_or_invited_or_knocked_users,
1929-
set(newly_left_rooms),
1930-
newly_left_users,
1931-
)
1939+
return set(newly_joined_rooms), set(newly_left_rooms)
19321940

19331941
async def _have_rooms_changed(
19341942
self, sync_result_builder: "SyncResultBuilder"

0 commit comments

Comments
 (0)