Repository: matrix-org/synapse

Issue:
`get_forgotten_rooms_for_user` cache is not invalidated when rejoining a room
The `get_forgotten_rooms_for_user` cache is only invalidated when a user `/forget`s a room, and not when a user re-joins a room.

I think this means we may exclude previously-forgotten rooms from an initial `/sync`, even if the user is now joined to them.


Discussion:

Resolving patch:
diff --git a/synapse/storage/databases/main/cache.py b/synapse/storage/databases/main/cache.py
--- a/synapse/storage/databases/main/cache.py
+++ b/synapse/storage/databases/main/cache.py
@@ -289,6 +289,17 @@ def _invalidate_caches_for_event(
             )
             self._attempt_to_invalidate_cache("get_rooms_for_user", (state_key,))
 
+            self._attempt_to_invalidate_cache(
+                "did_forget",
+                (
+                    state_key,
+                    room_id,
+                ),
+            )
+            self._attempt_to_invalidate_cache(
+                "get_forgotten_rooms_for_user", (state_key,)
+            )
+
         if relates_to:
             self._attempt_to_invalidate_cache("get_relations_for_event", (relates_to,))
             self._attempt_to_invalidate_cache("get_references_for_event", (relates_to,))
@@ -336,6 +347,8 @@ def _invalidate_caches_for_room_events(self, room_id: str) -> None:
             "get_rooms_for_user_with_stream_ordering", None
         )
         self._attempt_to_invalidate_cache("get_rooms_for_user", None)
+        self._attempt_to_invalidate_cache("did_forget", None)
+        self._attempt_to_invalidate_cache("get_forgotten_rooms_for_user", None)
         self._attempt_to_invalidate_cache("get_references_for_event", None)
         self._attempt_to_invalidate_cache("get_thread_summary", None)
         self._attempt_to_invalidate_cache("get_thread_participated", None)


Test patch:
diff --git a/tests/handlers/test_room_member.py b/tests/handlers/test_room_member.py
--- a/tests/handlers/test_room_member.py
+++ b/tests/handlers/test_room_member.py
@@ -333,6 +333,27 @@ def test_leave_and_forget(self) -> None:
             self.get_success(self.store.is_locally_forgotten_room(self.room_id))
         )
 
+    def test_leave_and_unforget(self) -> None:
+        """Tests if rejoining a room unforgets the room, so that it shows up in sync again."""
+        self.helper.join(self.room_id, user=self.bob, tok=self.bob_token)
+
+        # alice is not the last room member that leaves and forgets the room
+        self.helper.leave(self.room_id, user=self.alice, tok=self.alice_token)
+        self.get_success(self.handler.forget(self.alice_ID, self.room_id))
+        self.assertTrue(
+            self.get_success(self.store.did_forget(self.alice, self.room_id))
+        )
+
+        self.helper.join(self.room_id, user=self.alice, tok=self.alice_token)
+        self.assertFalse(
+            self.get_success(self.store.did_forget(self.alice, self.room_id))
+        )
+
+        # the server has not forgotten the room
+        self.assertFalse(
+            self.get_success(self.store.is_locally_forgotten_room(self.room_id))
+        )
+
     @override_config({"forget_rooms_on_leave": True})
     def test_leave_and_auto_forget(self) -> None:
         """Tests the `forget_rooms_on_leave` config option."""