|
19 | 19 | from twisted.test.proto_helpers import MemoryReactor |
20 | 20 |
|
21 | 21 | from synapse.api.constants import EventTypes |
22 | | -from synapse.api.errors import AuthError, Codes, LimitExceededError, SynapseError |
| 22 | +from synapse.api.errors import ( |
| 23 | + AuthError, |
| 24 | + Codes, |
| 25 | + LimitExceededError, |
| 26 | + NotFoundError, |
| 27 | + SynapseError, |
| 28 | +) |
23 | 29 | from synapse.api.room_versions import RoomVersions |
24 | 30 | from synapse.events import EventBase, make_event_from_dict |
25 | 31 | from synapse.federation.federation_base import event_from_pdu_json |
|
28 | 34 | from synapse.rest import admin |
29 | 35 | from synapse.rest.client import login, room |
30 | 36 | from synapse.server import HomeServer |
| 37 | +from synapse.storage.databases.main.events_worker import EventCacheEntry |
31 | 38 | from synapse.util import Clock |
32 | 39 | from synapse.util.stringutils import random_string |
33 | 40 |
|
@@ -322,6 +329,102 @@ def test_backfill_with_many_backward_extremities(self) -> None: |
322 | 329 | ) |
323 | 330 | self.get_success(d) |
324 | 331 |
|
| 332 | + def test_backfill_ignores_known_events(self) -> None: |
| 333 | + """ |
| 334 | + Tests that events that we already know about are ignored when backfilling. |
| 335 | + """ |
| 336 | + # Set up users |
| 337 | + user_id = self.register_user("kermit", "test") |
| 338 | + tok = self.login("kermit", "test") |
| 339 | + |
| 340 | + other_server = "otherserver" |
| 341 | + other_user = "@otheruser:" + other_server |
| 342 | + |
| 343 | + # Create a room to backfill events into |
| 344 | + room_id = self.helper.create_room_as(room_creator=user_id, tok=tok) |
| 345 | + room_version = self.get_success(self.store.get_room_version(room_id)) |
| 346 | + |
| 347 | + # Build an event to backfill |
| 348 | + event = event_from_pdu_json( |
| 349 | + { |
| 350 | + "type": EventTypes.Message, |
| 351 | + "content": {"body": "hello world", "msgtype": "m.text"}, |
| 352 | + "room_id": room_id, |
| 353 | + "sender": other_user, |
| 354 | + "depth": 32, |
| 355 | + "prev_events": [], |
| 356 | + "auth_events": [], |
| 357 | + "origin_server_ts": self.clock.time_msec(), |
| 358 | + }, |
| 359 | + room_version, |
| 360 | + ) |
| 361 | + |
| 362 | + # Ensure the event is not already in the DB |
| 363 | + self.get_failure( |
| 364 | + self.store.get_event(event.event_id), |
| 365 | + NotFoundError, |
| 366 | + ) |
| 367 | + |
| 368 | + # Backfill the event and check that it has entered the DB. |
| 369 | + |
| 370 | + # We mock out the FederationClient.backfill method, to pretend that a remote |
| 371 | + # server has returned our fake event. |
| 372 | + federation_client_backfill_mock = Mock(return_value=make_awaitable([event])) |
| 373 | + self.hs.get_federation_client().backfill = federation_client_backfill_mock |
| 374 | + |
| 375 | + # We also mock the persist method with a side effect of itself. This allows us |
| 376 | + # to track when it has been called while preserving its function. |
| 377 | + persist_events_and_notify_mock = Mock( |
| 378 | + side_effect=self.hs.get_federation_event_handler().persist_events_and_notify |
| 379 | + ) |
| 380 | + self.hs.get_federation_event_handler().persist_events_and_notify = ( |
| 381 | + persist_events_and_notify_mock |
| 382 | + ) |
| 383 | + |
| 384 | + # Small side-tangent. We populate the event cache with the event, even though |
| 385 | + # it is not yet in the DB. This is an invalid scenario that can currently occur |
| 386 | + # due to not properly invalidating the event cache. |
| 387 | + # See https://github.com/matrix-org/synapse/issues/13476. |
| 388 | + # |
| 389 | + # As a result, backfill should not rely on the event cache to check whether |
| 390 | + # we already have an event in the DB. |
| 391 | + # TODO: Remove this bit when the event cache is properly invalidated. |
| 392 | + cache_entry = EventCacheEntry( |
| 393 | + event=event, |
| 394 | + redacted_event=None, |
| 395 | + ) |
| 396 | + self.store._get_event_cache.set_local((event.event_id,), cache_entry) |
| 397 | + |
| 398 | + # We now call FederationEventHandler.backfill (a separate method) to trigger |
| 399 | + # a backfill request. It should receive the fake event. |
| 400 | + self.get_success( |
| 401 | + self.hs.get_federation_event_handler().backfill( |
| 402 | + other_user, |
| 403 | + room_id, |
| 404 | + limit=10, |
| 405 | + extremities=[], |
| 406 | + ) |
| 407 | + ) |
| 408 | + |
| 409 | + # Check that our fake event was persisted. |
| 410 | + persist_events_and_notify_mock.assert_called_once() |
| 411 | + persist_events_and_notify_mock.reset_mock() |
| 412 | + |
| 413 | + # Now we repeat the backfill, having the homeserver receive the fake event |
| 414 | + # again. |
| 415 | + self.get_success( |
| 416 | + self.hs.get_federation_event_handler().backfill( |
| 417 | + other_user, |
| 418 | + room_id, |
| 419 | + limit=10, |
| 420 | + extremities=[], |
| 421 | + ), |
| 422 | + ) |
| 423 | + |
| 424 | + # This time, we expect no event persistence to have occurred, as we already |
| 425 | + # have this event. |
| 426 | + persist_events_and_notify_mock.assert_not_called() |
| 427 | + |
325 | 428 | @unittest.override_config( |
326 | 429 | {"rc_invites": {"per_user": {"per_second": 0.5, "burst_count": 3}}} |
327 | 430 | ) |
|
0 commit comments