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

Commit 357561c

Browse files
Backfill remote event fetched by MSC3030 so we can paginate from it later (#13205)
Depends on #13320 Complement tests: matrix-org/complement#406 We could use the same method to backfill for `/context` as well in the future, see #3848
1 parent c7c84b8 commit 357561c

File tree

3 files changed

+94
-15
lines changed

3 files changed

+94
-15
lines changed

changelog.d/13205.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow pagination from remote event after discovering it from MSC3030 `/timestamp_to_event`.

synapse/handlers/federation_event.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,7 @@ async def _process_pulled_event(
793793
if existing:
794794
if not existing.internal_metadata.is_outlier():
795795
logger.info(
796-
"Ignoring received event %s which we have already seen",
796+
"_process_pulled_event: Ignoring received event %s which we have already seen",
797797
event_id,
798798
)
799799
return
@@ -1329,6 +1329,53 @@ async def _handle_marker_event(self, origin: str, marker_event: EventBase) -> No
13291329
marker_event,
13301330
)
13311331

1332+
async def backfill_event_id(
1333+
self, destination: str, room_id: str, event_id: str
1334+
) -> EventBase:
1335+
"""Backfill a single event and persist it as a non-outlier which means
1336+
we also pull in all of the state and auth events necessary for it.
1337+
1338+
Args:
1339+
destination: The homeserver to pull the given event_id from.
1340+
room_id: The room where the event is from.
1341+
event_id: The event ID to backfill.
1342+
1343+
Raises:
1344+
FederationError if we are unable to find the event from the destination
1345+
"""
1346+
logger.info(
1347+
"backfill_event_id: event_id=%s from destination=%s", event_id, destination
1348+
)
1349+
1350+
room_version = await self._store.get_room_version(room_id)
1351+
1352+
event_from_response = await self._federation_client.get_pdu(
1353+
[destination],
1354+
event_id,
1355+
room_version,
1356+
)
1357+
1358+
if not event_from_response:
1359+
raise FederationError(
1360+
"ERROR",
1361+
404,
1362+
"Unable to find event_id=%s from destination=%s to backfill."
1363+
% (event_id, destination),
1364+
affected=event_id,
1365+
)
1366+
1367+
# Persist the event we just fetched, including pulling all of the state
1368+
# and auth events to de-outlier it. This also sets up the necessary
1369+
# `state_groups` for the event.
1370+
await self._process_pulled_events(
1371+
destination,
1372+
[event_from_response],
1373+
# Prevent notifications going to clients
1374+
backfilled=True,
1375+
)
1376+
1377+
return event_from_response
1378+
13321379
async def _get_events_and_persist(
13331380
self, destination: str, room_id: str, event_ids: Collection[str]
13341381
) -> None:

synapse/handlers/room.py

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,7 @@ def __init__(self, hs: "HomeServer"):
13841384
self.store = hs.get_datastores().main
13851385
self.state_handler = hs.get_state_handler()
13861386
self.federation_client = hs.get_federation_client()
1387+
self.federation_event_handler = hs.get_federation_event_handler()
13871388
self._storage_controllers = hs.get_storage_controllers()
13881389

13891390
async def get_event_for_timestamp(
@@ -1479,38 +1480,68 @@ async def get_event_for_timestamp(
14791480
remote_response,
14801481
)
14811482

1482-
# TODO: Do we want to persist this as an extremity?
1483-
# TODO: I think ideally, we would try to backfill from
1484-
# this event and run this whole
1485-
# `get_event_for_timestamp` function again to make sure
1486-
# they didn't give us an event from their gappy history.
14871483
remote_event_id = remote_response.event_id
1488-
origin_server_ts = remote_response.origin_server_ts
1484+
remote_origin_server_ts = remote_response.origin_server_ts
1485+
1486+
# Backfill this event so we can get a pagination token for
1487+
# it with `/context` and paginate `/messages` from this
1488+
# point.
1489+
#
1490+
# TODO: The requested timestamp may lie in a part of the
1491+
# event graph that the remote server *also* didn't have,
1492+
# in which case they will have returned another event
1493+
# which may be nowhere near the requested timestamp. In
1494+
# the future, we may need to reconcile that gap and ask
1495+
# other homeservers, and/or extend `/timestamp_to_event`
1496+
# to return events on *both* sides of the timestamp to
1497+
# help reconcile the gap faster.
1498+
remote_event = (
1499+
await self.federation_event_handler.backfill_event_id(
1500+
domain, room_id, remote_event_id
1501+
)
1502+
)
1503+
1504+
# XXX: When we see that the remote server is not trustworthy,
1505+
# maybe we should not ask them first in the future.
1506+
if remote_origin_server_ts != remote_event.origin_server_ts:
1507+
logger.info(
1508+
"get_event_for_timestamp: Remote server (%s) claimed that remote_event_id=%s occured at remote_origin_server_ts=%s but that isn't true (actually occured at %s). Their claims are dubious and we should consider not trusting them.",
1509+
domain,
1510+
remote_event_id,
1511+
remote_origin_server_ts,
1512+
remote_event.origin_server_ts,
1513+
)
14891514

14901515
# Only return the remote event if it's closer than the local event
14911516
if not local_event or (
1492-
abs(origin_server_ts - timestamp)
1517+
abs(remote_event.origin_server_ts - timestamp)
14931518
< abs(local_event.origin_server_ts - timestamp)
14941519
):
1495-
return remote_event_id, origin_server_ts
1520+
logger.info(
1521+
"get_event_for_timestamp: returning remote_event_id=%s (%s) since it's closer to timestamp=%s than local_event=%s (%s)",
1522+
remote_event_id,
1523+
remote_event.origin_server_ts,
1524+
timestamp,
1525+
local_event.event_id if local_event else None,
1526+
local_event.origin_server_ts if local_event else None,
1527+
)
1528+
return remote_event_id, remote_origin_server_ts
14961529
except (HttpResponseException, InvalidResponseError) as ex:
14971530
# Let's not put a high priority on some other homeserver
14981531
# failing to respond or giving a random response
14991532
logger.debug(
1500-
"Failed to fetch /timestamp_to_event from %s because of exception(%s) %s args=%s",
1533+
"get_event_for_timestamp: Failed to fetch /timestamp_to_event from %s because of exception(%s) %s args=%s",
15011534
domain,
15021535
type(ex).__name__,
15031536
ex,
15041537
ex.args,
15051538
)
1506-
except Exception as ex:
1539+
except Exception:
15071540
# But we do want to see some exceptions in our code
15081541
logger.warning(
1509-
"Failed to fetch /timestamp_to_event from %s because of exception(%s) %s args=%s",
1542+
"get_event_for_timestamp: Failed to fetch /timestamp_to_event from %s because of exception",
15101543
domain,
1511-
type(ex).__name__,
1512-
ex,
1513-
ex.args,
1544+
exc_info=True,
15141545
)
15151546

15161547
# To appease mypy, we have to add both of these conditions to check for

0 commit comments

Comments
 (0)