diff --git a/synapse/handlers/sliding_sync/extensions.py b/synapse/handlers/sliding_sync/extensions.py
index 7c2f8a2569..287f4b04ad 100644
--- a/synapse/handlers/sliding_sync/extensions.py
+++ b/synapse/handlers/sliding_sync/extensions.py
@@ -14,7 +14,19 @@
import itertools
import logging
-from typing import TYPE_CHECKING, AbstractSet, Dict, Mapping, Optional, Sequence, Set
+from typing import (
+ TYPE_CHECKING,
+ AbstractSet,
+ ChainMap,
+ Dict,
+ List,
+ Mapping,
+ MutableMapping,
+ Optional,
+ Sequence,
+ Set,
+ cast,
+)
from typing_extensions import assert_never
@@ -381,29 +393,47 @@ class SlidingSyncExtensionHandler:
)
)
+ # TODO: This should take into account the `from_token` and `to_token`
have_push_rules_changed = await self.store.have_push_rules_changed_for_user(
user_id, from_token.stream_token.push_rules_key
)
if have_push_rules_changed:
- global_account_data_map = dict(global_account_data_map)
# TODO: This should take into account the `from_token` and `to_token`
global_account_data_map[
AccountDataTypes.PUSH_RULES
] = await self.push_rules_handler.push_rules_for_user(sync_config.user)
else:
# TODO: This should take into account the `to_token`
- all_global_account_data = await self.store.get_global_account_data_for_user(
- user_id
+ immutable_global_account_data_map = (
+ await self.store.get_global_account_data_for_user(user_id)
)
- global_account_data_map = dict(all_global_account_data)
- # TODO: This should take into account the `to_token`
- global_account_data_map[
- AccountDataTypes.PUSH_RULES
- ] = await self.push_rules_handler.push_rules_for_user(sync_config.user)
+ # Use a `ChainMap` to avoid copying the immutable data from the cache
+ global_account_data_map = ChainMap(
+ {
+ # TODO: This should take into account the `to_token`
+ AccountDataTypes.PUSH_RULES: await self.push_rules_handler.push_rules_for_user(
+ sync_config.user
+ )
+ },
+ # Cast is safe because `ChainMap` only mutates the top-most map,
+ # see https://github.com/python/typeshed/issues/8430
+ cast(
+ MutableMapping[str, JsonMapping], immutable_global_account_data_map
+ ),
+ )
# Fetch room account data
- account_data_by_room_map: Mapping[str, Mapping[str, JsonMapping]] = {}
+ #
+ # List of -> Mapping from room_id to mapping of `type` to `content` of room
+ # account data events.
+ #
+ # This is is a list so we can avoid making copies of immutable data and instead
+ # just provide multiple maps that need to be combined. Normally, we could
+ # reach for `ChainMap` in this scenario, but this is a nested map and accessing
+ # the ChainMap by room_id won't combine the two maps for that room (we would
+ # need a new `NestedChainMap` type class).
+ account_data_by_room_maps: List[Mapping[str, Mapping[str, JsonMapping]]] = []
relevant_room_ids = self.find_relevant_room_ids_for_extension(
requested_lists=account_data_request.lists,
requested_room_ids=account_data_request.rooms,
@@ -418,22 +448,66 @@ class SlidingSyncExtensionHandler:
user_id, from_token.stream_token.account_data_key
)
)
+
+ # Add room tags
+ #
+ # TODO: This should take into account the `from_token` and `to_token`
+ tags_by_room = await self.store.get_updated_tags(
+ user_id, from_token.stream_token.account_data_key
+ )
+ for room_id, tags in tags_by_room.items():
+ account_data_by_room_map.setdefault(room_id, {})[
+ AccountDataTypes.TAG
+ ] = {"tags": tags}
+
+ account_data_by_room_maps.append(account_data_by_room_map)
else:
# TODO: This should take into account the `to_token`
- account_data_by_room_map = (
+ immutable_account_data_by_room_map = (
await self.store.get_room_account_data_for_user(user_id)
)
+ account_data_by_room_maps.append(immutable_account_data_by_room_map)
- # Filter down to the relevant rooms
- account_data_by_room_map = {
- room_id: account_data_map
- for room_id, account_data_map in account_data_by_room_map.items()
- if room_id in relevant_room_ids
- }
+ # Add room tags
+ #
+ # TODO: This should take into account the `to_token`
+ tags_by_room = await self.store.get_tags_for_user(user_id)
+ account_data_by_room_maps.append(
+ {
+ room_id: {AccountDataTypes.TAG: {"tags": tags}}
+ for room_id, tags in tags_by_room.items()
+ }
+ )
+
+ # Filter down to the relevant rooms ... and combine the maps
+ relevant_account_data_by_room_map: MutableMapping[
+ str, Mapping[str, JsonMapping]
+ ] = {}
+ for room_id in relevant_room_ids:
+ # We want to avoid adding empty maps for relevant rooms that have no room
+ # account data so do a quick check to see if it's in any of the maps.
+ is_room_in_maps = False
+ for room_map in account_data_by_room_maps:
+ if room_id in room_map:
+ is_room_in_maps = True
+ break
+
+ # If we found the room in any of the maps, combine the maps for that room
+ if is_room_in_maps:
+ relevant_account_data_by_room_map[room_id] = ChainMap(
+ {},
+ *(
+ # Cast is safe because `ChainMap` only mutates the top-most map,
+ # see https://github.com/python/typeshed/issues/8430
+ cast(MutableMapping[str, JsonMapping], room_map[room_id])
+ for room_map in account_data_by_room_maps
+ if room_map.get(room_id)
+ ),
+ )
return SlidingSyncResult.Extensions.AccountDataExtension(
global_account_data_map=global_account_data_map,
- account_data_by_room_map=account_data_by_room_map,
+ account_data_by_room_map=relevant_account_data_by_room_map,
)
@trace
|