Get lazy loaded members on incremental sync

This commit is contained in:
Eric Eastwood 2024-10-08 01:52:26 -05:00
parent 3b234b53d5
commit 5066cbc6f6

View file

@ -869,6 +869,15 @@ class SlidingSyncHandler:
#
# Calculate the `StateFilter` based on the `required_state` for the room
required_state_filter = StateFilter.none()
added_membership_state_filter = StateFilter.none()
# The requested `required_state_map` with the any lazy membership expanded and
# `$ME` replaced with the user's ID. This allows us to see what membership we've
# sent down to the client in the next request.
#
# Make a copy so we can modify it. Still need to be careful to make a copy of
# the state key sets if we want to add/remove from them. We could make a deep
# copy but this saves us some work.
expanded_required_state_map = dict(room_sync_config.required_state_map)
if room_membership_for_user_at_to_token.membership not in (
Membership.INVITE,
Membership.KNOCK,
@ -933,22 +942,74 @@ class SlidingSyncHandler:
and state_key == StateValues.LAZY
):
lazy_load_room_members = True
# Everyone in the timeline is relevant
#
# FIXME: We probably also care about invite, ban, kick, targets, etc
# but the spec only mentions "senders".
timeline_membership: Set[str] = set()
if timeline_events is not None:
for timeline_event in timeline_events:
timeline_membership.add(timeline_event.sender)
# Add an explicit entry for each user in the timeline
expanded_required_state_map[EventTypes.Member] = (
# Make a copy of the state key set so we can modify it
# without affecting the original `required_state_map`
set(
expanded_required_state_map.get(
EventTypes.Member, set()
)
).union(timeline_membership)
)
# TODO
for user_id in timeline_membership:
required_state_types.append(
(EventTypes.Member, user_id)
)
# FIXME: We probably also care about invite, ban, kick, targets, etc
# but the spec only mentions "senders".
# TODO
if prev_room_sync_config is not None:
previous_memberships_given_to_client = (
prev_room_sync_config.required_state_map.get(
EventTypes.Member, set()
)
)
# Find what new memberships we need to send down
added_membership_user_ids: List[str] = []
for user_id in (
timeline_membership
- previous_memberships_given_to_client
):
added_membership_user_ids.append(user_id)
if added_membership_user_ids:
added_membership_state_filter = (
StateFilter.from_types(
[
(EventTypes.Member, user_id)
for user_id in added_membership_user_ids
]
)
)
elif state_key == StateValues.ME:
num_others += 1
required_state_types.append((state_type, user.to_string()))
# Replace `$ME` with the user's ID so we can deduplicate
# when someone requests the same state with `$ME` or with
# their user ID.
# without affecting the original `required_state_map`
expanded_required_state_map[EventTypes.Member] = (
# Make a copy of the state key set so we can modify it
# without affecting the original `required_state_map`
set(
expanded_required_state_map.get(
EventTypes.Member, set()
)
).union({user.to_string()})
)
else:
num_others += 1
required_state_types.append((state_type, state_key))
@ -1006,15 +1067,27 @@ class SlidingSyncHandler:
else:
assert from_bound is not None
# If we're lazy-loading membership, we need to fetch the current state for
# the new members we haven't seen before in the timeline. If we don't do
# this we'd only send down membership when it changes.
if added_membership_state_filter != StateFilter.none():
state_ids = await self.get_current_state_ids_at(
room_id=room_id,
room_membership_for_user_at_to_token=room_membership_for_user_at_to_token,
state_filter=added_membership_state_filter,
to_token=to_token,
)
room_state_delta_id_map.update(state_ids)
if prev_room_sync_config is not None:
# Check if there are any changes to the required state config
# that we need to handle.
changed_required_state_map, added_state_filter = (
_required_state_changes(
user.to_string(),
prev_room_sync_config,
room_sync_config,
room_state_delta_id_map,
prev_required_state_map=prev_room_sync_config.required_state_map,
request_required_state_map=expanded_required_state_map,
state_deltas=room_state_delta_id_map,
)
)
@ -1107,13 +1180,13 @@ class SlidingSyncHandler:
# sensible order again.
bump_stamp = 0
unstable_expanded_timeline = False
# Record the `room_sync_config` if we're `ignore_timeline_bound` (which means
# that the `timeline_limit` has increased)
room_sync_required_state_map_to_persist = room_sync_config.required_state_map
room_sync_required_state_map_to_persist = expanded_required_state_map
if changed_required_state_map:
room_sync_required_state_map_to_persist = changed_required_state_map
# Record the `room_sync_config` if we're `ignore_timeline_bound` (which means
# that the `timeline_limit` has increased)
unstable_expanded_timeline = False
if ignore_timeline_bound:
# FIXME: We signal the fact that we're sending down more events to
# the client by setting `unstable_expanded_timeline` to true (see
@ -1161,7 +1234,10 @@ class SlidingSyncHandler:
)
else:
new_connection_state.room_configs[room_id] = room_sync_config
new_connection_state.room_configs[room_id] = RoomSyncConfig(
timeline_limit=room_sync_config.timeline_limit,
required_state_map=room_sync_required_state_map_to_persist,
)
set_tag(SynapseTags.RESULT_PREFIX + "initial", initial)
@ -1281,8 +1357,9 @@ class SlidingSyncHandler:
def _required_state_changes(
user_id: str,
previous_room_config: "RoomSyncConfig",
room_sync_config: RoomSyncConfig,
*,
prev_required_state_map: Mapping[str, AbstractSet[str]],
request_required_state_map: Mapping[str, AbstractSet[str]],
state_deltas: StateMap[str],
) -> Tuple[Optional[Mapping[str, AbstractSet[str]]], StateFilter]:
"""Calculates the changes between the required state room config from the
@ -1302,10 +1379,6 @@ def _required_state_changes(
A 2-tuple of updated required state config and the state filter to use
to fetch extra current state that we need to return.
"""
prev_required_state_map = previous_room_config.required_state_map
request_required_state_map = room_sync_config.required_state_map
if prev_required_state_map == request_required_state_map:
# There has been no change. Return immediately.
return None, StateFilter.none()