summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--synapse/handlers/room_list.py34
-rw-r--r--synapse/storage/room.py52
2 files changed, 79 insertions, 7 deletions
diff --git a/synapse/handlers/room_list.py b/synapse/handlers/room_list.py
index 6a62f3c27e..28bc35f8a3 100644
--- a/synapse/handlers/room_list.py
+++ b/synapse/handlers/room_list.py
@@ -55,16 +55,26 @@ class RoomListHandler(BaseHandler):
         else:
             since_token = None
 
-        room_ids = yield self.store.get_public_room_ids()
-
         rooms_to_order_value = {}
         rooms_to_num_joined = {}
         rooms_to_latest_event_ids = {}
 
+        newly_visible = []
+        newly_unpublished = []
         if since_token:
-            current_stream_token = since_token.stream_ordering
+            stream_token = since_token.stream_ordering
+            current_public_id = yield self.store.get_current_public_room_stream_id()
+            public_room_stream_id = since_token.public_room_stream_id
+            newly_visible, newly_unpublished = yield self.store.get_public_room_changes(
+                public_room_stream_id, current_public_id
+            )
         else:
-            current_stream_token = yield self.store.get_room_max_stream_ordering()
+            stream_token = yield self.store.get_room_max_stream_ordering()
+            public_room_stream_id = yield self.store.get_current_public_room_stream_id()
+
+        room_ids = yield self.store.get_public_room_ids_at_stream_id(
+            public_room_stream_id
+        )
 
         # We want to return rooms in a particular order: the number of joined
         # users. We then arbitrarily use the room_id as a tie breaker.
@@ -74,7 +84,7 @@ class RoomListHandler(BaseHandler):
             latest_event_ids = rooms_to_latest_event_ids.get(room_id, None)
             if not latest_event_ids:
                 latest_event_ids = yield self.store.get_forward_extremeties_for_room(
-                    room_id, current_stream_token
+                    room_id, stream_token
                 )
                 rooms_to_latest_event_ids[room_id] = latest_event_ids
 
@@ -125,6 +135,9 @@ class RoomListHandler(BaseHandler):
             if num_joined_users == 0:
                 return
 
+            if room_id in newly_unpublished:
+                return
+
             result = {
                 "room_id": room_id,
                 "num_joined_members": num_joined_users,
@@ -207,10 +220,14 @@ class RoomListHandler(BaseHandler):
             "chunk": chunk,
         }
 
+        if since_token:
+            results["new_rooms"] = bool(newly_visible)
+
         if not since_token or since_token.direction_is_forward:
             if new_limit:
                 results["next_batch"] = RoomListNextBatch(
-                    stream_ordering=current_stream_token,
+                    stream_ordering=stream_token,
+                    public_room_stream_id=public_room_stream_id,
                     current_limit=new_limit,
                     direction_is_forward=True,
                 ).to_token()
@@ -222,7 +239,8 @@ class RoomListHandler(BaseHandler):
         else:
             if new_limit:
                 results["prev_batch"] = RoomListNextBatch(
-                    stream_ordering=current_stream_token,
+                    stream_ordering=stream_token,
+                    public_room_stream_id=public_room_stream_id,
                     current_limit=new_limit,
                     direction_is_forward=False,
                 ).to_token()
@@ -245,12 +263,14 @@ class RoomListHandler(BaseHandler):
 
 class RoomListNextBatch(namedtuple("RoomListNextBatch", (
     "stream_ordering",  # stream_ordering of the first public room list
+    "public_room_stream_id",  # public room stream id for first public room list
     "current_limit",  # The number of previous rooms returned
     "direction_is_forward",  # Bool if this is a next_batch, false if prev_batch
 ))):
 
     KEY_DICT = {
         "stream_ordering": "s",
+        "public_room_stream_id": "p",
         "current_limit": "n",
         "direction_is_forward": "d",
     }
diff --git a/synapse/storage/room.py b/synapse/storage/room.py
index ef0d79891e..8aa4545939 100644
--- a/synapse/storage/room.py
+++ b/synapse/storage/room.py
@@ -255,3 +255,55 @@ class RoomStore(SQLBaseStore):
             },
             desc="add_event_report"
         )
+
+    def get_current_public_room_stream_id(self):
+        return self._public_room_id_gen.get_current_token()
+
+    def get_public_room_ids_at_stream_id(self, stream_id):
+        return self.runInteraction(
+            "get_public_room_ids_at_stream_id",
+            self.get_public_room_ids_at_stream_id_txn, stream_id
+        )
+
+    def get_public_room_ids_at_stream_id_txn(self, txn, stream_id):
+        return {
+            rm
+            for rm, vis in self.get_published_at_stream_id_txn(txn, stream_id).items()
+            if vis
+        }
+
+    def get_published_at_stream_id_txn(self, txn, stream_id):
+        sql = ("""
+            SELECT room_id, visibility FROM public_room_list_stream
+            INNER JOIN (
+                SELECT room_id, max(stream_id) AS stream_id
+                FROM public_room_list_stream
+                WHERE stream_id <= ?
+                GROUP BY room_id
+            ) grouped USING (room_id, stream_id)
+        """)
+
+        txn.execute(sql, (stream_id,))
+        return dict(txn.fetchall())
+
+    def get_public_room_changes(self, prev_stream_id, new_stream_id):
+        def get_public_room_changes_txn(txn):
+            then_rooms = self.get_public_room_ids_at_stream_id_txn(txn, prev_stream_id)
+
+            now_rooms_dict = self.get_published_at_stream_id_txn(txn, new_stream_id)
+
+            now_rooms_visible = set(
+                rm for rm, vis in now_rooms_dict.items() if vis
+            )
+            now_rooms_not_visible = set(
+                rm for rm, vis in now_rooms_dict.items() if not vis
+            )
+
+            newly_visible = now_rooms_visible - then_rooms
+            newly_unpublished = now_rooms_not_visible & then_rooms
+
+            return newly_visible, newly_unpublished
+
+        return self.runInteraction(
+            "get_public_room_changes", get_public_room_changes_txn
+        )