Ensure (room_id, next_batch_id) is unique to avoid cross-talk/conflicts between batches (MSC2716) (#10877)

Part of [MSC2716](https://github.com/matrix-org/matrix-doc/pull/2716)

Part of https://github.com/matrix-org/synapse/issues/10737
This commit is contained in:
Eric Eastwood 2021-09-28 21:23:16 -05:00 committed by GitHub
parent 0f007fe009
commit 9fd057b8c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 4 deletions

View File

@ -0,0 +1 @@
Ensure `(room_id, next_batch_id)` is unique across [MSC2716](https://github.com/matrix-org/matrix-doc/pull/2716) insertion events in rooms to avoid cross-talk/conflicts between batches.

View File

@ -16,6 +16,7 @@
# limitations under the License. # limitations under the License.
import logging import logging
import random import random
from http import HTTPStatus
from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Tuple from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Tuple
from canonicaljson import encode_canonical_json from canonicaljson import encode_canonical_json
@ -1461,6 +1462,39 @@ class EventCreationHandler:
if prev_state_ids: if prev_state_ids:
raise AuthError(403, "Changing the room create event is forbidden") raise AuthError(403, "Changing the room create event is forbidden")
if event.type == EventTypes.MSC2716_INSERTION:
room_version = await self.store.get_room_version_id(event.room_id)
room_version_obj = KNOWN_ROOM_VERSIONS[room_version]
create_event = await self.store.get_create_event_for_room(event.room_id)
room_creator = create_event.content.get(EventContentFields.ROOM_CREATOR)
# Only check an insertion event if the room version
# supports it or the event is from the room creator.
if room_version_obj.msc2716_historical or (
self.config.experimental.msc2716_enabled
and event.sender == room_creator
):
next_batch_id = event.content.get(
EventContentFields.MSC2716_NEXT_BATCH_ID
)
conflicting_insertion_event_id = (
await self.store.get_insertion_event_by_batch_id(
event.room_id, next_batch_id
)
)
if conflicting_insertion_event_id is not None:
# The current insertion event that we're processing is invalid
# because an insertion event already exists in the room with the
# same next_batch_id. We can't allow multiple because the batch
# pointing will get weird, e.g. we can't determine which insertion
# event the batch event is pointing to.
raise SynapseError(
HTTPStatus.BAD_REQUEST,
"Another insertion event already exists with the same next_batch_id",
errcode=Codes.INVALID_PARAM,
)
# Mark any `m.historical` messages as backfilled so they don't appear # Mark any `m.historical` messages as backfilled so they don't appear
# in `/sync` and have the proper decrementing `stream_ordering` as we import # in `/sync` and have the proper decrementing `stream_ordering` as we import
backfilled = False backfilled = False

View File

@ -306,11 +306,13 @@ class RoomBatchSendEventRestServlet(RestServlet):
# Verify the batch_id_from_query corresponds to an actual insertion event # Verify the batch_id_from_query corresponds to an actual insertion event
# and have the batch connected. # and have the batch connected.
corresponding_insertion_event_id = ( corresponding_insertion_event_id = (
await self.store.get_insertion_event_by_batch_id(batch_id_from_query) await self.store.get_insertion_event_by_batch_id(
room_id, batch_id_from_query
)
) )
if corresponding_insertion_event_id is None: if corresponding_insertion_event_id is None:
raise SynapseError( raise SynapseError(
400, HTTPStatus.BAD_REQUEST,
"No insertion event corresponds to the given ?batch_id", "No insertion event corresponds to the given ?batch_id",
errcode=Codes.INVALID_PARAM, errcode=Codes.INVALID_PARAM,
) )

View File

@ -18,7 +18,9 @@ from synapse.storage._base import SQLBaseStore
class RoomBatchStore(SQLBaseStore): class RoomBatchStore(SQLBaseStore):
async def get_insertion_event_by_batch_id(self, batch_id: str) -> Optional[str]: async def get_insertion_event_by_batch_id(
self, room_id: str, batch_id: str
) -> Optional[str]:
"""Retrieve a insertion event ID. """Retrieve a insertion event ID.
Args: Args:
@ -30,7 +32,7 @@ class RoomBatchStore(SQLBaseStore):
""" """
return await self.db_pool.simple_select_one_onecol( return await self.db_pool.simple_select_one_onecol(
table="insertion_events", table="insertion_events",
keyvalues={"next_batch_id": batch_id}, keyvalues={"room_id": room_id, "next_batch_id": batch_id},
retcol="event_id", retcol="event_id",
allow_none=True, allow_none=True,
) )