mirror of
https://github.com/element-hq/synapse.git
synced 2024-12-14 11:57:44 +00:00
Fix incorrect get_rooms_for_user
for remote user (#11999)
When the server leaves a room the `get_rooms_for_user` cache is not correctly invalidated for the remote users in the room. This means that subsequent calls to `get_rooms_for_user` for the remote users would incorrectly include the room (it shouldn't be included because the server no longer knows anything about the room).
This commit is contained in:
parent
5598556b77
commit
dc9fe61050
3 changed files with 124 additions and 11 deletions
1
changelog.d/11999.bugfix
Normal file
1
changelog.d/11999.bugfix
Normal file
|
@ -0,0 +1 @@
|
|||
Fix long standing bug where `get_rooms_for_user` was not correctly invalidated for remote users when the server left a room.
|
|
@ -975,6 +975,17 @@ class PersistEventsStore:
|
|||
to_delete = delta_state.to_delete
|
||||
to_insert = delta_state.to_insert
|
||||
|
||||
# Figure out the changes of membership to invalidate the
|
||||
# `get_rooms_for_user` cache.
|
||||
# We find out which membership events we may have deleted
|
||||
# and which we have added, then we invalidate the caches for all
|
||||
# those users.
|
||||
members_changed = {
|
||||
state_key
|
||||
for ev_type, state_key in itertools.chain(to_delete, to_insert)
|
||||
if ev_type == EventTypes.Member
|
||||
}
|
||||
|
||||
if delta_state.no_longer_in_room:
|
||||
# Server is no longer in the room so we delete the room from
|
||||
# current_state_events, being careful we've already updated the
|
||||
|
@ -993,6 +1004,11 @@ class PersistEventsStore:
|
|||
"""
|
||||
txn.execute(sql, (stream_id, self._instance_name, room_id))
|
||||
|
||||
# We also want to invalidate the membership caches for users
|
||||
# that were in the room.
|
||||
users_in_room = self.store.get_users_in_room_txn(txn, room_id)
|
||||
members_changed.update(users_in_room)
|
||||
|
||||
self.db_pool.simple_delete_txn(
|
||||
txn,
|
||||
table="current_state_events",
|
||||
|
@ -1102,17 +1118,6 @@ class PersistEventsStore:
|
|||
|
||||
# Invalidate the various caches
|
||||
|
||||
# Figure out the changes of membership to invalidate the
|
||||
# `get_rooms_for_user` cache.
|
||||
# We find out which membership events we may have deleted
|
||||
# and which we have added, then we invalidate the caches for all
|
||||
# those users.
|
||||
members_changed = {
|
||||
state_key
|
||||
for ev_type, state_key in itertools.chain(to_delete, to_insert)
|
||||
if ev_type == EventTypes.Member
|
||||
}
|
||||
|
||||
for member in members_changed:
|
||||
txn.call_after(
|
||||
self.store.get_rooms_for_user_with_stream_ordering.invalidate,
|
||||
|
|
|
@ -329,3 +329,110 @@ class ExtremPruneTestCase(HomeserverTestCase):
|
|||
|
||||
# Check the new extremity is just the new remote event.
|
||||
self.assert_extremities([local_message_event_id, remote_event_2.event_id])
|
||||
|
||||
|
||||
class InvalideUsersInRoomCacheTestCase(HomeserverTestCase):
|
||||
servlets = [
|
||||
admin.register_servlets,
|
||||
room.register_servlets,
|
||||
login.register_servlets,
|
||||
]
|
||||
|
||||
def prepare(self, reactor, clock, homeserver):
|
||||
self.state = self.hs.get_state_handler()
|
||||
self.persistence = self.hs.get_storage().persistence
|
||||
self.store = self.hs.get_datastore()
|
||||
|
||||
def test_remote_user_rooms_cache_invalidated(self):
|
||||
"""Test that if the server leaves a room the `get_rooms_for_user` cache
|
||||
is invalidated for remote users.
|
||||
"""
|
||||
|
||||
# Set up a room with a local and remote user in it.
|
||||
user_id = self.register_user("user", "pass")
|
||||
token = self.login("user", "pass")
|
||||
|
||||
room_id = self.helper.create_room_as(
|
||||
"user", room_version=RoomVersions.V6.identifier, tok=token
|
||||
)
|
||||
|
||||
body = self.helper.send(room_id, body="Test", tok=token)
|
||||
local_message_event_id = body["event_id"]
|
||||
|
||||
# Fudge a join event for a remote user.
|
||||
remote_user = "@user:other"
|
||||
remote_event_1 = event_from_pdu_json(
|
||||
{
|
||||
"type": EventTypes.Member,
|
||||
"state_key": remote_user,
|
||||
"content": {"membership": Membership.JOIN},
|
||||
"room_id": room_id,
|
||||
"sender": remote_user,
|
||||
"depth": 5,
|
||||
"prev_events": [local_message_event_id],
|
||||
"auth_events": [],
|
||||
"origin_server_ts": self.clock.time_msec(),
|
||||
},
|
||||
RoomVersions.V6,
|
||||
)
|
||||
|
||||
context = self.get_success(self.state.compute_event_context(remote_event_1))
|
||||
self.get_success(self.persistence.persist_event(remote_event_1, context))
|
||||
|
||||
# Call `get_rooms_for_user` to add the remote user to the cache
|
||||
rooms = self.get_success(self.store.get_rooms_for_user(remote_user))
|
||||
self.assertEqual(set(rooms), {room_id})
|
||||
|
||||
# Now we have the local server leave the room, and check that calling
|
||||
# `get_user_in_room` for the remote user no longer includes the room.
|
||||
self.helper.leave(room_id, user_id, tok=token)
|
||||
|
||||
rooms = self.get_success(self.store.get_rooms_for_user(remote_user))
|
||||
self.assertEqual(set(rooms), set())
|
||||
|
||||
def test_room_remote_user_cache_invalidated(self):
|
||||
"""Test that if the server leaves a room the `get_users_in_room` cache
|
||||
is invalidated for remote users.
|
||||
"""
|
||||
|
||||
# Set up a room with a local and remote user in it.
|
||||
user_id = self.register_user("user", "pass")
|
||||
token = self.login("user", "pass")
|
||||
|
||||
room_id = self.helper.create_room_as(
|
||||
"user", room_version=RoomVersions.V6.identifier, tok=token
|
||||
)
|
||||
|
||||
body = self.helper.send(room_id, body="Test", tok=token)
|
||||
local_message_event_id = body["event_id"]
|
||||
|
||||
# Fudge a join event for a remote user.
|
||||
remote_user = "@user:other"
|
||||
remote_event_1 = event_from_pdu_json(
|
||||
{
|
||||
"type": EventTypes.Member,
|
||||
"state_key": remote_user,
|
||||
"content": {"membership": Membership.JOIN},
|
||||
"room_id": room_id,
|
||||
"sender": remote_user,
|
||||
"depth": 5,
|
||||
"prev_events": [local_message_event_id],
|
||||
"auth_events": [],
|
||||
"origin_server_ts": self.clock.time_msec(),
|
||||
},
|
||||
RoomVersions.V6,
|
||||
)
|
||||
|
||||
context = self.get_success(self.state.compute_event_context(remote_event_1))
|
||||
self.get_success(self.persistence.persist_event(remote_event_1, context))
|
||||
|
||||
# Call `get_users_in_room` to add the remote user to the cache
|
||||
users = self.get_success(self.store.get_users_in_room(room_id))
|
||||
self.assertEqual(set(users), {user_id, remote_user})
|
||||
|
||||
# Now we have the local server leave the room, and check that calling
|
||||
# `get_user_in_room` for the remote user no longer includes the room.
|
||||
self.helper.leave(room_id, user_id, tok=token)
|
||||
|
||||
users = self.get_success(self.store.get_users_in_room(room_id))
|
||||
self.assertEqual(users, [])
|
||||
|
|
Loading…
Reference in a new issue