This repository has been archived by the owner on Apr 26, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Catch-up after Federation Outage (split, 1) (#8230)
Signed-off-by: Olivier Wilkinson (reivilibre) <[email protected]>
- Loading branch information
1 parent
e351298
commit 58f61f1
Showing
7 changed files
with
201 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Track the latest event for every destination and room for catch-up after federation outage. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
synapse/storage/databases/main/schema/delta/58/15_catchup_destination_rooms.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* 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. | ||
*/ | ||
-- This schema delta alters the schema to enable 'catching up' remote homeservers | ||
-- after there has been a connectivity problem for any reason. | ||
|
||
-- This stores, for each (destination, room) pair, the stream_ordering of the | ||
-- latest event for that destination. | ||
CREATE TABLE IF NOT EXISTS destination_rooms ( | ||
-- the destination in question. | ||
destination TEXT NOT NULL REFERENCES destinations (destination), | ||
-- the ID of the room in question | ||
room_id TEXT NOT NULL REFERENCES rooms (room_id), | ||
-- the stream_ordering of the event | ||
stream_ordering INTEGER NOT NULL, | ||
PRIMARY KEY (destination, room_id) | ||
-- We don't declare a foreign key on stream_ordering here because that'd mean | ||
-- we'd need to either maintain an index (expensive) or do a table scan of | ||
-- destination_rooms whenever we delete an event (also potentially expensive). | ||
-- In addition to that, a foreign key on stream_ordering would be redundant | ||
-- as this row doesn't need to refer to a specific event; if the event gets | ||
-- deleted then it doesn't affect the validity of the stream_ordering here. | ||
); | ||
|
||
-- This index is needed to make it so that a deletion of a room (in the rooms | ||
-- table) can be efficient, as otherwise a table scan would need to be performed | ||
-- to check that no destination_rooms rows point to the room to be deleted. | ||
-- Also: it makes it efficient to delete all the entries for a given room ID, | ||
-- such as when purging a room. | ||
CREATE INDEX IF NOT EXISTS destination_rooms_room_id | ||
ON destination_rooms (room_id); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
from mock import Mock | ||
|
||
from synapse.rest import admin | ||
from synapse.rest.client.v1 import login, room | ||
|
||
from tests.test_utils import event_injection, make_awaitable | ||
from tests.unittest import FederatingHomeserverTestCase, override_config | ||
|
||
|
||
class FederationCatchUpTestCases(FederatingHomeserverTestCase): | ||
servlets = [ | ||
admin.register_servlets, | ||
room.register_servlets, | ||
login.register_servlets, | ||
] | ||
|
||
def make_homeserver(self, reactor, clock): | ||
return self.setup_test_homeserver( | ||
federation_transport_client=Mock(spec=["send_transaction"]), | ||
) | ||
|
||
def prepare(self, reactor, clock, hs): | ||
# stub out get_current_hosts_in_room | ||
state_handler = hs.get_state_handler() | ||
|
||
# This mock is crucial for destination_rooms to be populated. | ||
state_handler.get_current_hosts_in_room = Mock( | ||
return_value=make_awaitable(["test", "host2"]) | ||
) | ||
|
||
def get_destination_room(self, room: str, destination: str = "host2") -> dict: | ||
""" | ||
Gets the destination_rooms entry for a (destination, room_id) pair. | ||
Args: | ||
room: room ID | ||
destination: what destination, default is "host2" | ||
Returns: | ||
Dictionary of { event_id: str, stream_ordering: int } | ||
""" | ||
event_id, stream_ordering = self.get_success( | ||
self.hs.get_datastore().db_pool.execute( | ||
"test:get_destination_rooms", | ||
None, | ||
""" | ||
SELECT event_id, stream_ordering | ||
FROM destination_rooms dr | ||
JOIN events USING (stream_ordering) | ||
WHERE dr.destination = ? AND dr.room_id = ? | ||
""", | ||
destination, | ||
room, | ||
) | ||
)[0] | ||
return {"event_id": event_id, "stream_ordering": stream_ordering} | ||
|
||
@override_config({"send_federation": True}) | ||
def test_catch_up_destination_rooms_tracking(self): | ||
""" | ||
Tests that we populate the `destination_rooms` table as needed. | ||
""" | ||
self.register_user("u1", "you the one") | ||
u1_token = self.login("u1", "you the one") | ||
room = self.helper.create_room_as("u1", tok=u1_token) | ||
|
||
self.get_success( | ||
event_injection.inject_member_event(self.hs, room, "@user:host2", "join") | ||
) | ||
|
||
event_id_1 = self.helper.send(room, "wombats!", tok=u1_token)["event_id"] | ||
|
||
row_1 = self.get_destination_room(room) | ||
|
||
event_id_2 = self.helper.send(room, "rabbits!", tok=u1_token)["event_id"] | ||
|
||
row_2 = self.get_destination_room(room) | ||
|
||
# check: events correctly registered in order | ||
self.assertEqual(row_1["event_id"], event_id_1) | ||
self.assertEqual(row_2["event_id"], event_id_2) | ||
self.assertEqual(row_1["stream_ordering"], row_2["stream_ordering"] - 1) |