Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Add a background update to clear tombstoned rooms from the directory #6648

Merged
merged 2 commits into from
Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/6648.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ensure that upgraded rooms are removed from the directory.
5 changes: 5 additions & 0 deletions scripts/synapse_port_db
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ class Store(
logger.exception("Failed to insert: %s", table)
raise

def set_room_is_public(self, room_id, is_public):
raise Exception(
"Attempt to set room_is_public during port_db: database not empty?"
)


class MockHomeserver:
def __init__(self, config):
Expand Down
4 changes: 3 additions & 1 deletion synapse/storage/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# limitations under the License.
import logging
import random
from abc import ABCMeta

from six import PY2
from six.moves import builtins
Expand All @@ -30,7 +31,8 @@
logger = logging.getLogger(__name__)


class SQLBaseStore(object):
# some of our subclasses have abstract methods, so we use the ABCMeta metaclass.
class SQLBaseStore(metaclass=ABCMeta):
"""Base class for data stores that holds helper functions.

Note that multiple instances of this class will exist as there will be one
Expand Down
15 changes: 15 additions & 0 deletions synapse/storage/background_updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,21 @@ def _end_background_update(self, update_name):
"background_updates", keyvalues={"update_name": update_name}
)

def _background_update_progress(self, update_name: str, progress: dict):
"""Update the progress of a background update

Args:
update_name: The name of the background update task
progress: The progress of the update.
"""

return self.db.runInteraction(
"background_update_progress",
self._background_update_progress_txn,
update_name,
progress,
)

def _background_update_progress_txn(self, txn, update_name, progress):
"""Update the progress of a background update

Expand Down
64 changes: 64 additions & 0 deletions synapse/storage/data_stores/main/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import collections
import logging
import re
from abc import abstractmethod
from typing import Optional, Tuple

from six import integer_types
Expand Down Expand Up @@ -367,6 +368,8 @@ def get_retention_policy_for_room_txn(txn):


class RoomBackgroundUpdateStore(SQLBaseStore):
REMOVE_TOMESTONED_ROOMS_BG_UPDATE = "remove_tombstoned_rooms_from_directory"

def __init__(self, database: Database, db_conn, hs):
super(RoomBackgroundUpdateStore, self).__init__(database, db_conn, hs)

Expand All @@ -376,6 +379,11 @@ def __init__(self, database: Database, db_conn, hs):
"insert_room_retention", self._background_insert_retention,
)

self.db.updates.register_background_update_handler(
self.REMOVE_TOMESTONED_ROOMS_BG_UPDATE,
self._remove_tombstoned_rooms_from_directory,
)

@defer.inlineCallbacks
def _background_insert_retention(self, progress, batch_size):
"""Retrieves a list of all rooms within a range and inserts an entry for each of
Expand Down Expand Up @@ -444,6 +452,62 @@ def _background_insert_retention_txn(txn):

defer.returnValue(batch_size)

async def _remove_tombstoned_rooms_from_directory(
self, progress, batch_size
) -> int:
"""Removes any rooms with tombstone events from the room directory

Nowadays this is handled by the room upgrade handler, but we may have some
that got left behind
"""

last_room = progress.get("room_id", "")

def _get_rooms(txn):
txn.execute(
"""
SELECT room_id
FROM rooms r
INNER JOIN current_state_events cse USING (room_id)
WHERE room_id > ? AND r.is_public
AND cse.type = '%s' AND cse.state_key = ''
ORDER BY room_id ASC
LIMIT ?;
"""
% EventTypes.Tombstone,
(last_room, batch_size),
)

return [row[0] for row in txn]

rooms = await self.db.runInteraction(
"get_tombstoned_directory_rooms", _get_rooms
)

if not rooms:
await self.db.updates._end_background_update(
self.REMOVE_TOMESTONED_ROOMS_BG_UPDATE
)
return 0

for room_id in rooms:
logger.info("Removing tombstoned room %s from the directory", room_id)
await self.set_room_is_public(room_id, False)

await self.db.updates._background_update_progress(
self.REMOVE_TOMESTONED_ROOMS_BG_UPDATE, {"room_id": rooms[-1]}
)

return len(rooms)

@abstractmethod
def set_room_is_public(self, room_id, is_public):
# this will need to be implemented if a background update is performed with
# existing (tombstoned, public) rooms in the database.
#
# It's overridden by RoomStore for the synapse master.
raise NotImplementedError()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They way we've done this elsewhere is to have RoomBackgroundUpdateStore inherit from a class with this function defined (which is tedious but at least avoids spurious NotImplementedError stuff). You could also use ABC metaclass stuff.

Or we can leave it as is but then I think it needs a comment here as to what on earth is going on

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done the ABC thing. perhaps it's better now?



class RoomStore(RoomBackgroundUpdateStore, RoomWorkerStore, SearchStore):
def __init__(self, database: Database, db_conn, hs):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

-- Now that #6232 is a thing, we can remove old rooms from the directory.
INSERT INTO background_updates (update_name, progress_json) VALUES
('remove_tombstoned_rooms_from_directory', '{}');