Compare commits

...

2 Commits

Author SHA1 Message Date
Andrew Morgan
e5541ab1e8 newsfile 2025-07-03 13:11:13 +01:00
Andrew Morgan
883cd86a08 Send only latest membership events down (legacy) /sync
This commit aims to fix the edge case where a user has multiple
membership events for a single room in a single sync period, and ends up
receiving both down /sync. This confuses the client, as they don't know
which membership event is the latest - only the server does.

Thus we exclude "older" membership events from /sync, where ordering is
defined by events' "stream_ordering". Now clients will only get one
membership event per room per sync - the latest.
2025-07-03 13:11:13 +01:00
2 changed files with 30 additions and 2 deletions

1
changelog.d/18648.bugfix Normal file
View File

@@ -0,0 +1 @@
Only include latest membership events for each user/room combination in a single incremental legacy sync response.

View File

@@ -2539,9 +2539,36 @@ class SyncHandler:
assert since_token
mem_change_events_by_room_id: Dict[str, List[EventBase]] = {}
# Filter out older membership events for the same user in the same room, keeping only
# the most recent one based on stream_ordering.
#
# This prevents situations where a user leaves a room and is re-invited within
# the same incremental sync period, which would otherwise result in both the leave
# and invite appearing in the sync result and confusing clients about the user's
# actual state in the room.
most_recent_events_by_room_and_user: Dict[Tuple[str, str], EventBase] = {}
for event in membership_change_events:
mem_change_events_by_room_id.setdefault(event.room_id, []).append(event)
room_and_user_key = (event.room_id, event.state_key)
if room_and_user_key in most_recent_events_by_room_and_user:
existing_event = most_recent_events_by_room_and_user[room_and_user_key]
assert event.internal_metadata.stream_ordering
assert existing_event.internal_metadata.stream_ordering
# Replace it if the new event has a higher stream ordering
if (
event.internal_metadata.stream_ordering
> existing_event.internal_metadata.stream_ordering
):
most_recent_events_by_room_and_user[room_and_user_key] = event
else:
most_recent_events_by_room_and_user[room_and_user_key] = event
# Now rebuild mem_change_events_by_room_id using only the most recent membership event
# for each user in each room, ensuring the sync result reflects the latest state
mem_change_events_by_room_id: Dict[str, List[EventBase]] = {}
for (room_id, _), event in most_recent_events_by_room_and_user.items():
mem_change_events_by_room_id.setdefault(room_id, []).append(event)
newly_joined_rooms: List[str] = list(
sync_result_builder.forced_newly_joined_room_ids