mirror of
https://github.com/element-hq/synapse.git
synced 2024-12-21 03:42:55 +03:00
Move computing interested rooms to room list
This commit is contained in:
parent
5592a9e6cb
commit
42f43a8ddf
5 changed files with 298 additions and 237 deletions
|
@ -57,7 +57,7 @@ from synapse.types import (
|
||||||
StreamKeyType,
|
StreamKeyType,
|
||||||
StreamToken,
|
StreamToken,
|
||||||
)
|
)
|
||||||
from synapse.types.handlers import OperationType, SlidingSyncConfig, SlidingSyncResult
|
from synapse.types.handlers import SlidingSyncConfig, SlidingSyncResult
|
||||||
from synapse.types.state import StateFilter
|
from synapse.types.state import StateFilter
|
||||||
from synapse.util.async_helpers import concurrently_execute
|
from synapse.util.async_helpers import concurrently_execute
|
||||||
from synapse.visibility import filter_events_for_client
|
from synapse.visibility import filter_events_for_client
|
||||||
|
@ -237,232 +237,23 @@ class SlidingSyncHandler:
|
||||||
sync_config.room_subscriptions is not None
|
sync_config.room_subscriptions is not None
|
||||||
and len(sync_config.room_subscriptions) > 0
|
and len(sync_config.room_subscriptions) > 0
|
||||||
)
|
)
|
||||||
if has_lists or has_room_subscriptions:
|
|
||||||
room_membership_for_user_map = (
|
interested_rooms = await self.room_lists.compute_interested_rooms(
|
||||||
await self.room_lists.get_room_membership_for_user_at_to_token(
|
sync_config=sync_config,
|
||||||
user=sync_config.user,
|
previous_connection_state=previous_connection_state,
|
||||||
to_token=to_token,
|
|
||||||
from_token=from_token.stream_token if from_token else None,
|
from_token=from_token.stream_token if from_token else None,
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Assemble sliding window lists
|
|
||||||
lists: Dict[str, SlidingSyncResult.SlidingWindowList] = {}
|
|
||||||
# Keep track of the rooms that we can display and need to fetch more info about
|
|
||||||
relevant_room_map: Dict[str, RoomSyncConfig] = {}
|
|
||||||
# The set of room IDs of all rooms that could appear in any list. These
|
|
||||||
# include rooms that are outside the list ranges.
|
|
||||||
all_rooms: Set[str] = set()
|
|
||||||
if has_lists and sync_config.lists is not None:
|
|
||||||
with start_active_span("assemble_sliding_window_lists"):
|
|
||||||
sync_room_map = await self.room_lists.filter_rooms_relevant_for_sync(
|
|
||||||
user=sync_config.user,
|
|
||||||
room_membership_for_user_map=room_membership_for_user_map,
|
|
||||||
)
|
|
||||||
|
|
||||||
for list_key, list_config in sync_config.lists.items():
|
|
||||||
# Apply filters
|
|
||||||
filtered_sync_room_map = sync_room_map
|
|
||||||
if list_config.filters is not None:
|
|
||||||
filtered_sync_room_map = await self.room_lists.filter_rooms(
|
|
||||||
sync_config.user,
|
|
||||||
sync_room_map,
|
|
||||||
list_config.filters,
|
|
||||||
to_token,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Find which rooms are partially stated and may need to be filtered out
|
|
||||||
# depending on the `required_state` requested (see below).
|
|
||||||
partial_state_room_map = (
|
|
||||||
await self.store.is_partial_state_room_batched(
|
|
||||||
filtered_sync_room_map.keys()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Since creating the `RoomSyncConfig` takes some work, let's just do it
|
|
||||||
# once and make a copy whenever we need it.
|
|
||||||
room_sync_config = RoomSyncConfig.from_room_config(list_config)
|
|
||||||
|
|
||||||
# Exclude partially-stated rooms if we must wait for the room to be
|
|
||||||
# fully-stated
|
|
||||||
if room_sync_config.must_await_full_state(self.is_mine_id):
|
|
||||||
filtered_sync_room_map = {
|
|
||||||
room_id: room
|
|
||||||
for room_id, room in filtered_sync_room_map.items()
|
|
||||||
if not partial_state_room_map.get(room_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
all_rooms.update(filtered_sync_room_map)
|
|
||||||
|
|
||||||
# Sort the list
|
|
||||||
sorted_room_info = await self.room_lists.sort_rooms(
|
|
||||||
filtered_sync_room_map, to_token
|
|
||||||
)
|
|
||||||
|
|
||||||
ops: List[SlidingSyncResult.SlidingWindowList.Operation] = []
|
|
||||||
if list_config.ranges:
|
|
||||||
for range in list_config.ranges:
|
|
||||||
room_ids_in_list: List[str] = []
|
|
||||||
|
|
||||||
# We're going to loop through the sorted list of rooms starting
|
|
||||||
# at the range start index and keep adding rooms until we fill
|
|
||||||
# up the range or run out of rooms.
|
|
||||||
#
|
|
||||||
# Both sides of range are inclusive so we `+ 1`
|
|
||||||
max_num_rooms = range[1] - range[0] + 1
|
|
||||||
for room_membership in sorted_room_info[range[0] :]:
|
|
||||||
room_id = room_membership.room_id
|
|
||||||
|
|
||||||
if len(room_ids_in_list) >= max_num_rooms:
|
|
||||||
break
|
|
||||||
|
|
||||||
# Take the superset of the `RoomSyncConfig` for each room.
|
|
||||||
#
|
|
||||||
# Update our `relevant_room_map` with the room we're going
|
|
||||||
# to display and need to fetch more info about.
|
|
||||||
existing_room_sync_config = relevant_room_map.get(
|
|
||||||
room_id
|
|
||||||
)
|
|
||||||
if existing_room_sync_config is not None:
|
|
||||||
existing_room_sync_config.combine_room_sync_config(
|
|
||||||
room_sync_config
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# Make a copy so if we modify it later, it doesn't
|
|
||||||
# affect all references.
|
|
||||||
relevant_room_map[room_id] = (
|
|
||||||
room_sync_config.deep_copy()
|
|
||||||
)
|
|
||||||
|
|
||||||
room_ids_in_list.append(room_id)
|
|
||||||
|
|
||||||
ops.append(
|
|
||||||
SlidingSyncResult.SlidingWindowList.Operation(
|
|
||||||
op=OperationType.SYNC,
|
|
||||||
range=range,
|
|
||||||
room_ids=room_ids_in_list,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
lists[list_key] = SlidingSyncResult.SlidingWindowList(
|
|
||||||
count=len(sorted_room_info),
|
|
||||||
ops=ops,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Handle room subscriptions
|
|
||||||
if has_room_subscriptions and sync_config.room_subscriptions is not None:
|
|
||||||
with start_active_span("assemble_room_subscriptions"):
|
|
||||||
# Find which rooms are partially stated and may need to be filtered out
|
|
||||||
# depending on the `required_state` requested (see below).
|
|
||||||
partial_state_room_map = await self.store.is_partial_state_room_batched(
|
|
||||||
sync_config.room_subscriptions.keys()
|
|
||||||
)
|
|
||||||
|
|
||||||
for (
|
|
||||||
room_id,
|
|
||||||
room_subscription,
|
|
||||||
) in sync_config.room_subscriptions.items():
|
|
||||||
room_membership_for_user_at_to_token = (
|
|
||||||
await self.check_room_subscription_allowed_for_user(
|
|
||||||
room_id=room_id,
|
|
||||||
room_membership_for_user_map=room_membership_for_user_map,
|
|
||||||
to_token=to_token,
|
to_token=to_token,
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
# Skip this room if the user isn't allowed to see it
|
lists = interested_rooms.lists
|
||||||
if not room_membership_for_user_at_to_token:
|
relevant_room_map = interested_rooms.relevant_room_map
|
||||||
continue
|
all_rooms = interested_rooms.all_rooms
|
||||||
|
room_membership_for_user_map = interested_rooms.room_membership_for_user_map
|
||||||
all_rooms.add(room_id)
|
relevant_rooms_to_send_map = interested_rooms.relevant_rooms_to_send_map
|
||||||
|
|
||||||
room_membership_for_user_map[room_id] = (
|
|
||||||
room_membership_for_user_at_to_token
|
|
||||||
)
|
|
||||||
|
|
||||||
# Take the superset of the `RoomSyncConfig` for each room.
|
|
||||||
room_sync_config = RoomSyncConfig.from_room_config(
|
|
||||||
room_subscription
|
|
||||||
)
|
|
||||||
|
|
||||||
# Exclude partially-stated rooms if we must wait for the room to be
|
|
||||||
# fully-stated
|
|
||||||
if room_sync_config.must_await_full_state(self.is_mine_id):
|
|
||||||
if partial_state_room_map.get(room_id):
|
|
||||||
continue
|
|
||||||
|
|
||||||
all_rooms.add(room_id)
|
|
||||||
|
|
||||||
# Update our `relevant_room_map` with the room we're going to display
|
|
||||||
# and need to fetch more info about.
|
|
||||||
existing_room_sync_config = relevant_room_map.get(room_id)
|
|
||||||
if existing_room_sync_config is not None:
|
|
||||||
existing_room_sync_config.combine_room_sync_config(
|
|
||||||
room_sync_config
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
relevant_room_map[room_id] = room_sync_config
|
|
||||||
|
|
||||||
# Fetch room data
|
# Fetch room data
|
||||||
rooms: Dict[str, SlidingSyncResult.RoomResult] = {}
|
rooms: Dict[str, SlidingSyncResult.RoomResult] = {}
|
||||||
|
|
||||||
# Filter out rooms that haven't received updates and we've sent down
|
|
||||||
# previously.
|
|
||||||
# Keep track of the rooms that we're going to display and need to fetch more info about
|
|
||||||
relevant_rooms_to_send_map = relevant_room_map
|
|
||||||
with start_active_span("filter_relevant_rooms_to_send"):
|
|
||||||
if from_token:
|
|
||||||
rooms_should_send = set()
|
|
||||||
|
|
||||||
# First we check if there are rooms that match a list/room
|
|
||||||
# subscription and have updates we need to send (i.e. either because
|
|
||||||
# we haven't sent the room down, or we have but there are missing
|
|
||||||
# updates).
|
|
||||||
for room_id, room_config in relevant_room_map.items():
|
|
||||||
prev_room_sync_config = previous_connection_state.room_configs.get(
|
|
||||||
room_id
|
|
||||||
)
|
|
||||||
if prev_room_sync_config is not None:
|
|
||||||
# Always include rooms whose timeline limit has increased.
|
|
||||||
# (see the "XXX: Odd behavior" described below)
|
|
||||||
if (
|
|
||||||
prev_room_sync_config.timeline_limit
|
|
||||||
< room_config.timeline_limit
|
|
||||||
):
|
|
||||||
rooms_should_send.add(room_id)
|
|
||||||
continue
|
|
||||||
|
|
||||||
status = previous_connection_state.rooms.have_sent_room(room_id)
|
|
||||||
if (
|
|
||||||
# The room was never sent down before so the client needs to know
|
|
||||||
# about it regardless of any updates.
|
|
||||||
status.status == HaveSentRoomFlag.NEVER
|
|
||||||
# `PREVIOUSLY` literally means the "room was sent down before *AND*
|
|
||||||
# there are updates we haven't sent down" so we already know this
|
|
||||||
# room has updates.
|
|
||||||
or status.status == HaveSentRoomFlag.PREVIOUSLY
|
|
||||||
):
|
|
||||||
rooms_should_send.add(room_id)
|
|
||||||
elif status.status == HaveSentRoomFlag.LIVE:
|
|
||||||
# We know that we've sent all updates up until `from_token`,
|
|
||||||
# so we just need to check if there have been updates since
|
|
||||||
# then.
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
assert_never(status.status)
|
|
||||||
|
|
||||||
# We only need to check for new events since any state changes
|
|
||||||
# will also come down as new events.
|
|
||||||
rooms_that_have_updates = self.store.get_rooms_that_might_have_updates(
|
|
||||||
relevant_room_map.keys(), from_token.stream_token.room_key
|
|
||||||
)
|
|
||||||
rooms_should_send.update(rooms_that_have_updates)
|
|
||||||
relevant_rooms_to_send_map = {
|
|
||||||
room_id: room_sync_config
|
|
||||||
for room_id, room_sync_config in relevant_room_map.items()
|
|
||||||
if room_id in rooms_should_send
|
|
||||||
}
|
|
||||||
|
|
||||||
new_connection_state = previous_connection_state.get_mutable()
|
new_connection_state = previous_connection_state.get_mutable()
|
||||||
|
|
||||||
@trace
|
@trace
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, Dict, List, Mapping, Optional, Sequence, Set
|
from typing import TYPE_CHECKING, AbstractSet, Dict, Mapping, Optional, Sequence, Set
|
||||||
|
|
||||||
from typing_extensions import assert_never
|
from typing_extensions import assert_never
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ from synapse.types import (
|
||||||
JsonMapping,
|
JsonMapping,
|
||||||
MultiWriterStreamToken,
|
MultiWriterStreamToken,
|
||||||
SlidingSyncStreamToken,
|
SlidingSyncStreamToken,
|
||||||
|
StrCollection,
|
||||||
StreamToken,
|
StreamToken,
|
||||||
)
|
)
|
||||||
from synapse.types.handlers import OperationType, SlidingSyncConfig, SlidingSyncResult
|
from synapse.types.handlers import OperationType, SlidingSyncConfig, SlidingSyncResult
|
||||||
|
@ -55,9 +56,9 @@ class SlidingSyncExtensionHandler:
|
||||||
sync_config: SlidingSyncConfig,
|
sync_config: SlidingSyncConfig,
|
||||||
previous_connection_state: "PerConnectionState",
|
previous_connection_state: "PerConnectionState",
|
||||||
new_connection_state: "MutablePerConnectionState",
|
new_connection_state: "MutablePerConnectionState",
|
||||||
actual_lists: Dict[str, SlidingSyncResult.SlidingWindowList],
|
actual_lists: Mapping[str, SlidingSyncResult.SlidingWindowList],
|
||||||
actual_room_ids: Set[str],
|
actual_room_ids: Set[str],
|
||||||
actual_room_response_map: Dict[str, SlidingSyncResult.RoomResult],
|
actual_room_response_map: Mapping[str, SlidingSyncResult.RoomResult],
|
||||||
to_token: StreamToken,
|
to_token: StreamToken,
|
||||||
from_token: Optional[SlidingSyncStreamToken],
|
from_token: Optional[SlidingSyncStreamToken],
|
||||||
) -> SlidingSyncResult.Extensions:
|
) -> SlidingSyncResult.Extensions:
|
||||||
|
@ -144,10 +145,10 @@ class SlidingSyncExtensionHandler:
|
||||||
|
|
||||||
def find_relevant_room_ids_for_extension(
|
def find_relevant_room_ids_for_extension(
|
||||||
self,
|
self,
|
||||||
requested_lists: Optional[List[str]],
|
requested_lists: Optional[StrCollection],
|
||||||
requested_room_ids: Optional[List[str]],
|
requested_room_ids: Optional[StrCollection],
|
||||||
actual_lists: Dict[str, SlidingSyncResult.SlidingWindowList],
|
actual_lists: Mapping[str, SlidingSyncResult.SlidingWindowList],
|
||||||
actual_room_ids: Set[str],
|
actual_room_ids: AbstractSet[str],
|
||||||
) -> Set[str]:
|
) -> Set[str]:
|
||||||
"""
|
"""
|
||||||
Handle the reserved `lists`/`rooms` keys for extensions. Extensions should only
|
Handle the reserved `lists`/`rooms` keys for extensions. Extensions should only
|
||||||
|
@ -343,7 +344,7 @@ class SlidingSyncExtensionHandler:
|
||||||
async def get_account_data_extension_response(
|
async def get_account_data_extension_response(
|
||||||
self,
|
self,
|
||||||
sync_config: SlidingSyncConfig,
|
sync_config: SlidingSyncConfig,
|
||||||
actual_lists: Dict[str, SlidingSyncResult.SlidingWindowList],
|
actual_lists: Mapping[str, SlidingSyncResult.SlidingWindowList],
|
||||||
actual_room_ids: Set[str],
|
actual_room_ids: Set[str],
|
||||||
account_data_request: SlidingSyncConfig.Extensions.AccountDataExtension,
|
account_data_request: SlidingSyncConfig.Extensions.AccountDataExtension,
|
||||||
to_token: StreamToken,
|
to_token: StreamToken,
|
||||||
|
@ -436,9 +437,9 @@ class SlidingSyncExtensionHandler:
|
||||||
sync_config: SlidingSyncConfig,
|
sync_config: SlidingSyncConfig,
|
||||||
previous_connection_state: "PerConnectionState",
|
previous_connection_state: "PerConnectionState",
|
||||||
new_connection_state: "MutablePerConnectionState",
|
new_connection_state: "MutablePerConnectionState",
|
||||||
actual_lists: Dict[str, SlidingSyncResult.SlidingWindowList],
|
actual_lists: Mapping[str, SlidingSyncResult.SlidingWindowList],
|
||||||
actual_room_ids: Set[str],
|
actual_room_ids: Set[str],
|
||||||
actual_room_response_map: Dict[str, SlidingSyncResult.RoomResult],
|
actual_room_response_map: Mapping[str, SlidingSyncResult.RoomResult],
|
||||||
receipts_request: SlidingSyncConfig.Extensions.ReceiptsExtension,
|
receipts_request: SlidingSyncConfig.Extensions.ReceiptsExtension,
|
||||||
to_token: StreamToken,
|
to_token: StreamToken,
|
||||||
from_token: Optional[SlidingSyncStreamToken],
|
from_token: Optional[SlidingSyncStreamToken],
|
||||||
|
@ -598,9 +599,9 @@ class SlidingSyncExtensionHandler:
|
||||||
async def get_typing_extension_response(
|
async def get_typing_extension_response(
|
||||||
self,
|
self,
|
||||||
sync_config: SlidingSyncConfig,
|
sync_config: SlidingSyncConfig,
|
||||||
actual_lists: Dict[str, SlidingSyncResult.SlidingWindowList],
|
actual_lists: Mapping[str, SlidingSyncResult.SlidingWindowList],
|
||||||
actual_room_ids: Set[str],
|
actual_room_ids: Set[str],
|
||||||
actual_room_response_map: Dict[str, SlidingSyncResult.RoomResult],
|
actual_room_response_map: Mapping[str, SlidingSyncResult.RoomResult],
|
||||||
typing_request: SlidingSyncConfig.Extensions.TypingExtension,
|
typing_request: SlidingSyncConfig.Extensions.TypingExtension,
|
||||||
to_token: StreamToken,
|
to_token: StreamToken,
|
||||||
from_token: Optional[SlidingSyncStreamToken],
|
from_token: Optional[SlidingSyncStreamToken],
|
||||||
|
|
|
@ -40,6 +40,11 @@ from synapse.api.constants import (
|
||||||
)
|
)
|
||||||
from synapse.events import StrippedStateEvent
|
from synapse.events import StrippedStateEvent
|
||||||
from synapse.events.utils import parse_stripped_state_event
|
from synapse.events.utils import parse_stripped_state_event
|
||||||
|
from synapse.handlers.sliding_sync.types import (
|
||||||
|
HaveSentRoomFlag,
|
||||||
|
PerConnectionState,
|
||||||
|
RoomSyncConfig,
|
||||||
|
)
|
||||||
from synapse.logging.opentracing import start_active_span, trace
|
from synapse.logging.opentracing import start_active_span, trace
|
||||||
from synapse.storage.databases.main.state import (
|
from synapse.storage.databases.main.state import (
|
||||||
ROOM_UNKNOWN_SENTINEL,
|
ROOM_UNKNOWN_SENTINEL,
|
||||||
|
@ -56,7 +61,7 @@ from synapse.types import (
|
||||||
StreamToken,
|
StreamToken,
|
||||||
UserID,
|
UserID,
|
||||||
)
|
)
|
||||||
from synapse.types.handlers import SlidingSyncConfig
|
from synapse.types.handlers import OperationType, SlidingSyncConfig, SlidingSyncResult
|
||||||
from synapse.types.state import StateFilter
|
from synapse.types.state import StateFilter
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -66,6 +71,30 @@ if TYPE_CHECKING:
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(auto_attribs=True, slots=True, frozen=True)
|
||||||
|
class SlidingSyncInterestedRooms:
|
||||||
|
"""The set of rooms and metadata a client is interested in based on their
|
||||||
|
sliding sync request.
|
||||||
|
|
||||||
|
Returned by `compute_interested_rooms`.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
lists: A mapping from list name to the list result for the response
|
||||||
|
relevant_room_map: A map from rooms that match the sync request to
|
||||||
|
their room sync config.
|
||||||
|
relevant_rooms_to_send_map: Subset of `relevant_room_map` that
|
||||||
|
includes the rooms that *may* have relevant updates. Rooms not
|
||||||
|
in this map will definitely not have room updates (though
|
||||||
|
extensions may have updates in these rooms).
|
||||||
|
"""
|
||||||
|
|
||||||
|
lists: Mapping[str, SlidingSyncResult.SlidingWindowList]
|
||||||
|
relevant_room_map: Mapping[str, RoomSyncConfig]
|
||||||
|
relevant_rooms_to_send_map: Mapping[str, RoomSyncConfig]
|
||||||
|
all_rooms: Set[str]
|
||||||
|
room_membership_for_user_map: Mapping[str, "_RoomMembershipForUser"]
|
||||||
|
|
||||||
|
|
||||||
class Sentinel(enum.Enum):
|
class Sentinel(enum.Enum):
|
||||||
# defining a sentinel in this way allows mypy to correctly handle the
|
# defining a sentinel in this way allows mypy to correctly handle the
|
||||||
# type of a dictionary lookup and subsequent type narrowing.
|
# type of a dictionary lookup and subsequent type narrowing.
|
||||||
|
@ -154,6 +183,246 @@ class SlidingSyncRoomLists:
|
||||||
self.store = hs.get_datastores().main
|
self.store = hs.get_datastores().main
|
||||||
self.storage_controllers = hs.get_storage_controllers()
|
self.storage_controllers = hs.get_storage_controllers()
|
||||||
self.rooms_to_exclude_globally = hs.config.server.rooms_to_exclude_from_sync
|
self.rooms_to_exclude_globally = hs.config.server.rooms_to_exclude_from_sync
|
||||||
|
self.is_mine_id = hs.is_mine_id
|
||||||
|
|
||||||
|
async def compute_interested_rooms(
|
||||||
|
self,
|
||||||
|
sync_config: SlidingSyncConfig,
|
||||||
|
previous_connection_state: "PerConnectionState",
|
||||||
|
to_token: StreamToken,
|
||||||
|
from_token: Optional[StreamToken],
|
||||||
|
) -> SlidingSyncInterestedRooms:
|
||||||
|
"""Fetch the set of rooms that match the request"""
|
||||||
|
|
||||||
|
room_membership_for_user_map = (
|
||||||
|
await self.get_room_membership_for_user_at_to_token(
|
||||||
|
sync_config.user, to_token, from_token
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assemble sliding window lists
|
||||||
|
lists: Dict[str, SlidingSyncResult.SlidingWindowList] = {}
|
||||||
|
# Keep track of the rooms that we can display and need to fetch more info about
|
||||||
|
relevant_room_map: Dict[str, RoomSyncConfig] = {}
|
||||||
|
# The set of room IDs of all rooms that could appear in any list. These
|
||||||
|
# include rooms that are outside the list ranges.
|
||||||
|
all_rooms: Set[str] = set()
|
||||||
|
|
||||||
|
if sync_config.lists:
|
||||||
|
with start_active_span("assemble_sliding_window_lists"):
|
||||||
|
sync_room_map = await self.filter_rooms_relevant_for_sync(
|
||||||
|
user=sync_config.user,
|
||||||
|
room_membership_for_user_map=room_membership_for_user_map,
|
||||||
|
)
|
||||||
|
|
||||||
|
for list_key, list_config in sync_config.lists.items():
|
||||||
|
# Apply filters
|
||||||
|
filtered_sync_room_map = sync_room_map
|
||||||
|
if list_config.filters is not None:
|
||||||
|
filtered_sync_room_map = await self.filter_rooms(
|
||||||
|
sync_config.user,
|
||||||
|
sync_room_map,
|
||||||
|
list_config.filters,
|
||||||
|
to_token,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Find which rooms are partially stated and may need to be filtered out
|
||||||
|
# depending on the `required_state` requested (see below).
|
||||||
|
partial_state_room_map = (
|
||||||
|
await self.store.is_partial_state_room_batched(
|
||||||
|
filtered_sync_room_map.keys()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Since creating the `RoomSyncConfig` takes some work, let's just do it
|
||||||
|
# once and make a copy whenever we need it.
|
||||||
|
room_sync_config = RoomSyncConfig.from_room_config(list_config)
|
||||||
|
|
||||||
|
# Exclude partially-stated rooms if we must wait for the room to be
|
||||||
|
# fully-stated
|
||||||
|
if room_sync_config.must_await_full_state(self.is_mine_id):
|
||||||
|
filtered_sync_room_map = {
|
||||||
|
room_id: room
|
||||||
|
for room_id, room in filtered_sync_room_map.items()
|
||||||
|
if not partial_state_room_map.get(room_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
all_rooms.update(filtered_sync_room_map)
|
||||||
|
|
||||||
|
# Sort the list
|
||||||
|
sorted_room_info = await self.sort_rooms(
|
||||||
|
filtered_sync_room_map, to_token
|
||||||
|
)
|
||||||
|
|
||||||
|
ops: List[SlidingSyncResult.SlidingWindowList.Operation] = []
|
||||||
|
if list_config.ranges:
|
||||||
|
for range in list_config.ranges:
|
||||||
|
room_ids_in_list: List[str] = []
|
||||||
|
|
||||||
|
# We're going to loop through the sorted list of rooms starting
|
||||||
|
# at the range start index and keep adding rooms until we fill
|
||||||
|
# up the range or run out of rooms.
|
||||||
|
#
|
||||||
|
# Both sides of range are inclusive so we `+ 1`
|
||||||
|
max_num_rooms = range[1] - range[0] + 1
|
||||||
|
for room_membership in sorted_room_info[range[0] :]:
|
||||||
|
room_id = room_membership.room_id
|
||||||
|
|
||||||
|
if len(room_ids_in_list) >= max_num_rooms:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Take the superset of the `RoomSyncConfig` for each room.
|
||||||
|
#
|
||||||
|
# Update our `relevant_room_map` with the room we're going
|
||||||
|
# to display and need to fetch more info about.
|
||||||
|
existing_room_sync_config = relevant_room_map.get(
|
||||||
|
room_id
|
||||||
|
)
|
||||||
|
if existing_room_sync_config is not None:
|
||||||
|
existing_room_sync_config.combine_room_sync_config(
|
||||||
|
room_sync_config
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Make a copy so if we modify it later, it doesn't
|
||||||
|
# affect all references.
|
||||||
|
relevant_room_map[room_id] = (
|
||||||
|
room_sync_config.deep_copy()
|
||||||
|
)
|
||||||
|
|
||||||
|
room_ids_in_list.append(room_id)
|
||||||
|
|
||||||
|
ops.append(
|
||||||
|
SlidingSyncResult.SlidingWindowList.Operation(
|
||||||
|
op=OperationType.SYNC,
|
||||||
|
range=range,
|
||||||
|
room_ids=room_ids_in_list,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
lists[list_key] = SlidingSyncResult.SlidingWindowList(
|
||||||
|
count=len(sorted_room_info),
|
||||||
|
ops=ops,
|
||||||
|
)
|
||||||
|
|
||||||
|
if sync_config.room_subscriptions:
|
||||||
|
with start_active_span("assemble_room_subscriptions"):
|
||||||
|
# Find which rooms are partially stated and may need to be filtered out
|
||||||
|
# depending on the `required_state` requested (see below).
|
||||||
|
partial_state_room_map = await self.store.is_partial_state_room_batched(
|
||||||
|
sync_config.room_subscriptions.keys()
|
||||||
|
)
|
||||||
|
|
||||||
|
for (
|
||||||
|
room_id,
|
||||||
|
room_subscription,
|
||||||
|
) in sync_config.room_subscriptions.items():
|
||||||
|
room_membership_for_user_at_to_token = (
|
||||||
|
await self.check_room_subscription_allowed_for_user(
|
||||||
|
room_id=room_id,
|
||||||
|
room_membership_for_user_map=room_membership_for_user_map,
|
||||||
|
to_token=to_token,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Skip this room if the user isn't allowed to see it
|
||||||
|
if not room_membership_for_user_at_to_token:
|
||||||
|
continue
|
||||||
|
|
||||||
|
all_rooms.add(room_id)
|
||||||
|
|
||||||
|
room_membership_for_user_map[room_id] = (
|
||||||
|
room_membership_for_user_at_to_token
|
||||||
|
)
|
||||||
|
|
||||||
|
# Take the superset of the `RoomSyncConfig` for each room.
|
||||||
|
room_sync_config = RoomSyncConfig.from_room_config(
|
||||||
|
room_subscription
|
||||||
|
)
|
||||||
|
|
||||||
|
# Exclude partially-stated rooms if we must wait for the room to be
|
||||||
|
# fully-stated
|
||||||
|
if room_sync_config.must_await_full_state(self.is_mine_id):
|
||||||
|
if partial_state_room_map.get(room_id):
|
||||||
|
continue
|
||||||
|
|
||||||
|
all_rooms.add(room_id)
|
||||||
|
|
||||||
|
# Update our `relevant_room_map` with the room we're going to display
|
||||||
|
# and need to fetch more info about.
|
||||||
|
existing_room_sync_config = relevant_room_map.get(room_id)
|
||||||
|
if existing_room_sync_config is not None:
|
||||||
|
existing_room_sync_config.combine_room_sync_config(
|
||||||
|
room_sync_config
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
relevant_room_map[room_id] = room_sync_config
|
||||||
|
|
||||||
|
# Filtered subset of `relevant_room_map` for rooms that may have updates
|
||||||
|
# (in the event stream)
|
||||||
|
relevant_rooms_to_send_map: Dict[str, RoomSyncConfig] = relevant_room_map
|
||||||
|
if relevant_room_map:
|
||||||
|
with start_active_span("filter_relevant_rooms_to_send"):
|
||||||
|
if from_token:
|
||||||
|
rooms_should_send = set()
|
||||||
|
|
||||||
|
# First we check if there are rooms that match a list/room
|
||||||
|
# subscription and have updates we need to send (i.e. either because
|
||||||
|
# we haven't sent the room down, or we have but there are missing
|
||||||
|
# updates).
|
||||||
|
for room_id, room_config in relevant_room_map.items():
|
||||||
|
prev_room_sync_config = (
|
||||||
|
previous_connection_state.room_configs.get(room_id)
|
||||||
|
)
|
||||||
|
if prev_room_sync_config is not None:
|
||||||
|
# Always include rooms whose timeline limit has increased.
|
||||||
|
# (see the "XXX: Odd behavior" described below)
|
||||||
|
if (
|
||||||
|
prev_room_sync_config.timeline_limit
|
||||||
|
< room_config.timeline_limit
|
||||||
|
):
|
||||||
|
rooms_should_send.add(room_id)
|
||||||
|
continue
|
||||||
|
|
||||||
|
status = previous_connection_state.rooms.have_sent_room(room_id)
|
||||||
|
if (
|
||||||
|
# The room was never sent down before so the client needs to know
|
||||||
|
# about it regardless of any updates.
|
||||||
|
status.status == HaveSentRoomFlag.NEVER
|
||||||
|
# `PREVIOUSLY` literally means the "room was sent down before *AND*
|
||||||
|
# there are updates we haven't sent down" so we already know this
|
||||||
|
# room has updates.
|
||||||
|
or status.status == HaveSentRoomFlag.PREVIOUSLY
|
||||||
|
):
|
||||||
|
rooms_should_send.add(room_id)
|
||||||
|
elif status.status == HaveSentRoomFlag.LIVE:
|
||||||
|
# We know that we've sent all updates up until `from_token`,
|
||||||
|
# so we just need to check if there have been updates since
|
||||||
|
# then.
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
assert_never(status.status)
|
||||||
|
|
||||||
|
# We only need to check for new events since any state changes
|
||||||
|
# will also come down as new events.
|
||||||
|
rooms_that_have_updates = (
|
||||||
|
self.store.get_rooms_that_might_have_updates(
|
||||||
|
relevant_room_map.keys(), from_token.room_key
|
||||||
|
)
|
||||||
|
)
|
||||||
|
rooms_should_send.update(rooms_that_have_updates)
|
||||||
|
relevant_rooms_to_send_map = {
|
||||||
|
room_id: room_sync_config
|
||||||
|
for room_id, room_sync_config in relevant_room_map.items()
|
||||||
|
if room_id in rooms_should_send
|
||||||
|
}
|
||||||
|
|
||||||
|
return SlidingSyncInterestedRooms(
|
||||||
|
lists=lists,
|
||||||
|
relevant_room_map=relevant_room_map,
|
||||||
|
relevant_rooms_to_send_map=relevant_rooms_to_send_map,
|
||||||
|
all_rooms=all_rooms,
|
||||||
|
room_membership_for_user_map=room_membership_for_user_map,
|
||||||
|
)
|
||||||
|
|
||||||
@trace
|
@trace
|
||||||
async def get_room_membership_for_user_at_to_token(
|
async def get_room_membership_for_user_at_to_token(
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
|
from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Tuple, Union
|
||||||
|
|
||||||
from synapse.api.constants import AccountDataTypes, EduTypes, Membership, PresenceState
|
from synapse.api.constants import AccountDataTypes, EduTypes, Membership, PresenceState
|
||||||
from synapse.api.errors import Codes, StoreError, SynapseError
|
from synapse.api.errors import Codes, StoreError, SynapseError
|
||||||
|
@ -975,7 +975,7 @@ class SlidingSyncRestServlet(RestServlet):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def encode_lists(
|
def encode_lists(
|
||||||
self, lists: Dict[str, SlidingSyncResult.SlidingWindowList]
|
self, lists: Mapping[str, SlidingSyncResult.SlidingWindowList]
|
||||||
) -> JsonDict:
|
) -> JsonDict:
|
||||||
def encode_operation(
|
def encode_operation(
|
||||||
operation: SlidingSyncResult.SlidingWindowList.Operation,
|
operation: SlidingSyncResult.SlidingWindowList.Operation,
|
||||||
|
|
|
@ -409,7 +409,7 @@ class SlidingSyncResult:
|
||||||
)
|
)
|
||||||
|
|
||||||
next_pos: SlidingSyncStreamToken
|
next_pos: SlidingSyncStreamToken
|
||||||
lists: Dict[str, SlidingWindowList]
|
lists: Mapping[str, SlidingWindowList]
|
||||||
rooms: Dict[str, RoomResult]
|
rooms: Dict[str, RoomResult]
|
||||||
extensions: Extensions
|
extensions: Extensions
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue