@@ -1317,36 +1317,91 @@ async def generate_sync_result(
13171317 At the end, we transfer data from the `sync_result_builder` to a new `SyncResult`
13181318 instance to signify that the sync calculation is complete.
13191319 """
1320+
1321+ user_id = sync_config .user .to_string ()
1322+ app_service = self .store .get_app_service_by_user_id (user_id )
1323+ if app_service :
1324+ # We no longer support AS users using /sync directly.
1325+ # See https://github.com/matrix-org/matrix-doc/issues/1144
1326+ raise NotImplementedError ()
1327+
1328+ # Note: we get the users room list *before* we get the current token, this
1329+ # avoids checking back in history if rooms are joined after the token is fetched.
1330+ token_before_rooms = self .event_sources .get_current_token ()
1331+ mutable_joined_room_ids = set (await self .store .get_rooms_for_user (user_id ))
1332+
13201333 # NB: The now_token gets changed by some of the generate_sync_* methods,
13211334 # this is due to some of the underlying streams not supporting the ability
13221335 # to query up to a given point.
13231336 # Always use the `now_token` in `SyncResultBuilder`
13241337 now_token = self .event_sources .get_current_token ()
13251338 log_kv ({"now_token" : now_token })
13261339
1340+ # Since we fetched the users room list before the token, there's a small window
1341+ # during which membership events may have been persisted, so we fetch these now
1342+ # and modify the joined room list for any changes between the get_rooms_for_user
1343+ # call and the get_current_token call.
1344+ membership_change_events = []
1345+ if since_token :
1346+ membership_change_events = await self .store .get_membership_changes_for_user (
1347+ user_id , since_token .room_key , now_token .room_key , self .rooms_to_exclude
1348+ )
1349+
1350+ mem_last_change_by_room_id : Dict [str , EventBase ] = {}
1351+ for event in membership_change_events :
1352+ mem_last_change_by_room_id [event .room_id ] = event
1353+
1354+ # For the latest membership event in each room found, add/remove the room ID
1355+ # from the joined room list accordingly. In this case we only care if the
1356+ # latest change is JOIN.
1357+
1358+ for room_id , event in mem_last_change_by_room_id .items ():
1359+ assert event .internal_metadata .stream_ordering
1360+ if (
1361+ event .internal_metadata .stream_ordering
1362+ < token_before_rooms .room_key .stream
1363+ ):
1364+ continue
1365+
1366+ logger .info (
1367+ "User membership change between getting rooms and current token: %s %s %s" ,
1368+ user_id ,
1369+ event .membership ,
1370+ room_id ,
1371+ )
1372+ # User joined a room - we have to then check the room state to ensure we
1373+ # respect any bans if there's a race between the join and ban events.
1374+ if event .membership == Membership .JOIN :
1375+ user_ids_in_room = await self .store .get_users_in_room (room_id )
1376+ if user_id in user_ids_in_room :
1377+ mutable_joined_room_ids .add (room_id )
1378+ # The user left the room, or left and was re-invited but not joined yet
1379+ else :
1380+ mutable_joined_room_ids .discard (room_id )
1381+
1382+ # Now we have our list of joined room IDs, exclude as configured and freeze
1383+ joined_room_ids = frozenset (
1384+ (
1385+ room_id
1386+ for room_id in mutable_joined_room_ids
1387+ if room_id not in self .rooms_to_exclude
1388+ )
1389+ )
1390+
13271391 logger .debug (
13281392 "Calculating sync response for %r between %s and %s" ,
13291393 sync_config .user ,
13301394 since_token ,
13311395 now_token ,
13321396 )
13331397
1334- user_id = sync_config .user .to_string ()
1335- app_service = self .store .get_app_service_by_user_id (user_id )
1336- if app_service :
1337- # We no longer support AS users using /sync directly.
1338- # See https://github.com/matrix-org/matrix-doc/issues/1144
1339- raise NotImplementedError ()
1340- else :
1341- joined_room_ids = await self .get_rooms_for_user_at (
1342- user_id , now_token .room_key
1343- )
13441398 sync_result_builder = SyncResultBuilder (
13451399 sync_config ,
13461400 full_state ,
13471401 since_token = since_token ,
13481402 now_token = now_token ,
13491403 joined_room_ids = joined_room_ids ,
1404+ membership_change_events = membership_change_events ,
13501405 )
13511406
13521407 logger .debug ("Fetching account data" )
@@ -1827,19 +1882,12 @@ async def _have_rooms_changed(
18271882
18281883 Does not modify the `sync_result_builder`.
18291884 """
1830- user_id = sync_result_builder .sync_config .user .to_string ()
18311885 since_token = sync_result_builder .since_token
1832- now_token = sync_result_builder .now_token
1886+ membership_change_events = sync_result_builder .membership_change_events
18331887
18341888 assert since_token
18351889
1836- # Get a list of membership change events that have happened to the user
1837- # requesting the sync.
1838- membership_changes = await self .store .get_membership_changes_for_user (
1839- user_id , since_token .room_key , now_token .room_key
1840- )
1841-
1842- if membership_changes :
1890+ if membership_change_events :
18431891 return True
18441892
18451893 stream_id = since_token .room_key .stream
@@ -1878,16 +1926,10 @@ async def _get_rooms_changed(
18781926 since_token = sync_result_builder .since_token
18791927 now_token = sync_result_builder .now_token
18801928 sync_config = sync_result_builder .sync_config
1929+ membership_change_events = sync_result_builder .membership_change_events
18811930
18821931 assert since_token
18831932
1884- # TODO: we've already called this function and ran this query in
1885- # _have_rooms_changed. We could keep the results in memory to avoid a
1886- # second query, at the cost of more complicated source code.
1887- membership_change_events = await self .store .get_membership_changes_for_user (
1888- user_id , since_token .room_key , now_token .room_key , self .rooms_to_exclude
1889- )
1890-
18911933 mem_change_events_by_room_id : Dict [str , List [EventBase ]] = {}
18921934 for event in membership_change_events :
18931935 mem_change_events_by_room_id .setdefault (event .room_id , []).append (event )
@@ -2415,60 +2457,6 @@ async def _generate_room_entry(
24152457 else :
24162458 raise Exception ("Unrecognized rtype: %r" , room_builder .rtype )
24172459
2418- async def get_rooms_for_user_at (
2419- self ,
2420- user_id : str ,
2421- room_key : RoomStreamToken ,
2422- ) -> FrozenSet [str ]:
2423- """Get set of joined rooms for a user at the given stream ordering.
2424-
2425- The stream ordering *must* be recent, otherwise this may throw an
2426- exception if older than a month. (This function is called with the
2427- current token, which should be perfectly fine).
2428-
2429- Args:
2430- user_id
2431- stream_ordering
2432-
2433- ReturnValue:
2434- Set of room_ids the user is in at given stream_ordering.
2435- """
2436- joined_rooms = await self .store .get_rooms_for_user_with_stream_ordering (user_id )
2437-
2438- joined_room_ids = set ()
2439-
2440- # We need to check that the stream ordering of the join for each room
2441- # is before the stream_ordering asked for. This might not be the case
2442- # if the user joins a room between us getting the current token and
2443- # calling `get_rooms_for_user_with_stream_ordering`.
2444- # If the membership's stream ordering is after the given stream
2445- # ordering, we need to go and work out if the user was in the room
2446- # before.
2447- # We also need to check whether the room should be excluded from sync
2448- # responses as per the homeserver config.
2449- for joined_room in joined_rooms :
2450- if joined_room .room_id in self .rooms_to_exclude :
2451- continue
2452-
2453- if not joined_room .event_pos .persisted_after (room_key ):
2454- joined_room_ids .add (joined_room .room_id )
2455- continue
2456-
2457- logger .info ("User joined room after current token: %s" , joined_room .room_id )
2458-
2459- extrems = (
2460- await self .store .get_forward_extremities_for_room_at_stream_ordering (
2461- joined_room .room_id , joined_room .event_pos .stream
2462- )
2463- )
2464- user_ids_in_room = await self .state .get_current_user_ids_in_room (
2465- joined_room .room_id , extrems
2466- )
2467- if user_id in user_ids_in_room :
2468- joined_room_ids .add (joined_room .room_id )
2469-
2470- return frozenset (joined_room_ids )
2471-
24722460
24732461def _action_has_highlight (actions : List [JsonDict ]) -> bool :
24742462 for action in actions :
@@ -2565,6 +2553,7 @@ class SyncResultBuilder:
25652553 since_token : Optional [StreamToken ]
25662554 now_token : StreamToken
25672555 joined_room_ids : FrozenSet [str ]
2556+ membership_change_events : List [EventBase ]
25682557
25692558 presence : List [UserPresenceState ] = attr .Factory (list )
25702559 account_data : List [JsonDict ] = attr .Factory (list )
0 commit comments