mirror of
https://git.anonymousland.org/anonymousland/synapse-product.git
synced 2024-12-21 23:45:01 -05:00
Refactor get_user_devices_from_cache to avoid mutating cached values. (#15040)
The previous version of the code could mutate a cached value, but only if the input requested all devices of a user *and* a specific device. To avoid this nonsensical situation we no longer fetch a specific device ID if all of a user's devices are returned.
This commit is contained in:
parent
fd296b7343
commit
a481fb9f98
1
changelog.d/15040.misc
Normal file
1
changelog.d/15040.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Avoid mutating a cached value in `get_user_devices_from_cache`.
|
@ -159,19 +159,22 @@ class E2eKeysHandler:
|
|||||||
# A map of destination -> user ID -> device IDs.
|
# A map of destination -> user ID -> device IDs.
|
||||||
remote_queries_not_in_cache: Dict[str, Dict[str, Iterable[str]]] = {}
|
remote_queries_not_in_cache: Dict[str, Dict[str, Iterable[str]]] = {}
|
||||||
if remote_queries:
|
if remote_queries:
|
||||||
query_list: List[Tuple[str, Optional[str]]] = []
|
user_ids = set()
|
||||||
|
user_and_device_ids: List[Tuple[str, str]] = []
|
||||||
for user_id, device_ids in remote_queries.items():
|
for user_id, device_ids in remote_queries.items():
|
||||||
if device_ids:
|
if device_ids:
|
||||||
query_list.extend(
|
user_and_device_ids.extend(
|
||||||
(user_id, device_id) for device_id in device_ids
|
(user_id, device_id) for device_id in device_ids
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
query_list.append((user_id, None))
|
user_ids.add(user_id)
|
||||||
|
|
||||||
(
|
(
|
||||||
user_ids_not_in_cache,
|
user_ids_not_in_cache,
|
||||||
remote_results,
|
remote_results,
|
||||||
) = await self.store.get_user_devices_from_cache(query_list)
|
) = await self.store.get_user_devices_from_cache(
|
||||||
|
user_ids, user_and_device_ids
|
||||||
|
)
|
||||||
|
|
||||||
# Check that the homeserver still shares a room with all cached users.
|
# Check that the homeserver still shares a room with all cached users.
|
||||||
# Note that this check may be slightly racy when a remote user leaves a
|
# Note that this check may be slightly racy when a remote user leaves a
|
||||||
|
@ -746,42 +746,45 @@ class DeviceWorkerStore(RoomMemberWorkerStore, EndToEndKeyWorkerStore):
|
|||||||
@trace
|
@trace
|
||||||
@cancellable
|
@cancellable
|
||||||
async def get_user_devices_from_cache(
|
async def get_user_devices_from_cache(
|
||||||
self, query_list: List[Tuple[str, Optional[str]]]
|
self, user_ids: Set[str], user_and_device_ids: List[Tuple[str, str]]
|
||||||
) -> Tuple[Set[str], Dict[str, Dict[str, JsonDict]]]:
|
) -> Tuple[Set[str], Dict[str, Dict[str, JsonDict]]]:
|
||||||
"""Get the devices (and keys if any) for remote users from the cache.
|
"""Get the devices (and keys if any) for remote users from the cache.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
query_list: List of (user_id, device_ids), if device_ids is
|
user_ids: users which should have all device IDs returned
|
||||||
falsey then return all device ids for that user.
|
user_and_device_ids: List of (user_id, device_ids)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A tuple of (user_ids_not_in_cache, results_map), where
|
A tuple of (user_ids_not_in_cache, results_map), where
|
||||||
user_ids_not_in_cache is a set of user_ids and results_map is a
|
user_ids_not_in_cache is a set of user_ids and results_map is a
|
||||||
mapping of user_id -> device_id -> device_info.
|
mapping of user_id -> device_id -> device_info.
|
||||||
"""
|
"""
|
||||||
user_ids = {user_id for user_id, _ in query_list}
|
unique_user_ids = user_ids | {user_id for user_id, _ in user_and_device_ids}
|
||||||
user_map = await self.get_device_list_last_stream_id_for_remotes(list(user_ids))
|
user_map = await self.get_device_list_last_stream_id_for_remotes(
|
||||||
|
list(unique_user_ids)
|
||||||
|
)
|
||||||
|
|
||||||
# We go and check if any of the users need to have their device lists
|
# We go and check if any of the users need to have their device lists
|
||||||
# resynced. If they do then we remove them from the cached list.
|
# resynced. If they do then we remove them from the cached list.
|
||||||
users_needing_resync = await self.get_user_ids_requiring_device_list_resync(
|
users_needing_resync = await self.get_user_ids_requiring_device_list_resync(
|
||||||
user_ids
|
unique_user_ids
|
||||||
)
|
)
|
||||||
user_ids_in_cache = {
|
user_ids_in_cache = {
|
||||||
user_id for user_id, stream_id in user_map.items() if stream_id
|
user_id for user_id, stream_id in user_map.items() if stream_id
|
||||||
} - users_needing_resync
|
} - users_needing_resync
|
||||||
user_ids_not_in_cache = user_ids - user_ids_in_cache
|
user_ids_not_in_cache = unique_user_ids - user_ids_in_cache
|
||||||
|
|
||||||
|
# First fetch all the users which all devices are to be returned.
|
||||||
results: Dict[str, Dict[str, JsonDict]] = {}
|
results: Dict[str, Dict[str, JsonDict]] = {}
|
||||||
for user_id, device_id in query_list:
|
for user_id in user_ids:
|
||||||
if user_id not in user_ids_in_cache:
|
if user_id in user_ids_in_cache:
|
||||||
continue
|
results[user_id] = await self.get_cached_devices_for_user(user_id)
|
||||||
|
# Then fetch all device-specific requests, but skip users we've already
|
||||||
if device_id:
|
# fetched all devices for.
|
||||||
|
for user_id, device_id in user_and_device_ids:
|
||||||
|
if user_id in user_ids_in_cache and user_id not in user_ids:
|
||||||
device = await self._get_cached_user_device(user_id, device_id)
|
device = await self._get_cached_user_device(user_id, device_id)
|
||||||
results.setdefault(user_id, {})[device_id] = device
|
results.setdefault(user_id, {})[device_id] = device
|
||||||
else:
|
|
||||||
results[user_id] = await self.get_cached_devices_for_user(user_id)
|
|
||||||
|
|
||||||
set_tag("in_cache", str(results))
|
set_tag("in_cache", str(results))
|
||||||
set_tag("not_in_cache", str(user_ids_not_in_cache))
|
set_tag("not_in_cache", str(user_ids_not_in_cache))
|
||||||
|
Loading…
Reference in New Issue
Block a user