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

Fix race when persisting an event and deleting a room #12594

Merged
merged 3 commits into from
May 3, 2022
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/12594.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix race when persisting an event and deleting a room that could lead to outbound federation breaking.
15 changes: 15 additions & 0 deletions synapse/storage/databases/main/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
)
from synapse.storage.databases.main.events_worker import EventCacheEntry
from synapse.storage.databases.main.search import SearchEntry
from synapse.storage.engines.postgres import PostgresEngine
from synapse.storage.util.id_generators import AbstractStreamIdGenerator
from synapse.storage.util.sequence import SequenceGenerator
from synapse.types import StateMap, get_domain_from_id
Expand Down Expand Up @@ -364,6 +365,20 @@ def _persist_events_txn(
min_stream_order = events_and_contexts[0][0].internal_metadata.stream_ordering
max_stream_order = events_and_contexts[-1][0].internal_metadata.stream_ordering

# We check that the room still exists for events we're trying to
# persist. This is to protect against races with deleting a room.
#
# Annoyingly SQLite doesn't support row level locking.
if isinstance(self.database_engine, PostgresEngine):
for room_id in {e.room_id for e, _ in events_and_contexts}:
txn.execute(
"SELECT room_version FROM rooms WHERE room_id = ? FOR SHARE",
(room_id,),
)
squahtx marked this conversation as resolved.
Show resolved Hide resolved
row = txn.fetchone()
if row is None:
raise Exception(f"Room does not exist {room_id}")
squahtx marked this conversation as resolved.
Show resolved Hide resolved

# stream orderings should have been assigned by now
assert min_stream_order
assert max_stream_order
Expand Down
8 changes: 6 additions & 2 deletions synapse/storage/databases/main/purge_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,12 @@ async def purge_room(self, room_id: str) -> List[int]:
)

def _purge_room_txn(self, txn: LoggingTransaction, room_id: str) -> List[int]:
# First we fetch all the state groups that should be deleted, before
# We *immediately* delete the room from the rooms table. This ensures
# that we don't race when persisting events (as that transaction checks
# that the room exists).
txn.execute("DELETE FROM rooms WHERE room_id = ?", (room_id,))

# Next, we fetch all the state groups that should be deleted, before
# we delete that information.
txn.execute(
"""
Expand Down Expand Up @@ -403,7 +408,6 @@ def _purge_room_txn(self, txn: LoggingTransaction, room_id: str) -> List[int]:
"room_stats_state",
"room_stats_current",
"room_stats_earliest_token",
"rooms",
"stream_ordering_to_exterm",
"users_in_public_rooms",
"users_who_share_private_rooms",
Expand Down