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

Commit a86b2f6

Browse files
authored
Fix a bug where redactions were not being sent over federation if we did not have the original event. (#13813)
1 parent 6a92944 commit a86b2f6

File tree

6 files changed

+62
-38
lines changed

6 files changed

+62
-38
lines changed

changelog.d/13813.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a long-standing bug where redactions were not being sent over federation if we did not have the original event.

synapse/federation/sender/__init__.py

+20-9
Original file line numberDiff line numberDiff line change
@@ -353,21 +353,25 @@ async def _process_event_queue_loop(self) -> None:
353353
last_token = await self.store.get_federation_out_pos("events")
354354
(
355355
next_token,
356-
events,
357356
event_to_received_ts,
358-
) = await self.store.get_all_new_events_stream(
357+
) = await self.store.get_all_new_event_ids_stream(
359358
last_token, self._last_poked_id, limit=100
360359
)
361360

361+
event_ids = event_to_received_ts.keys()
362+
event_entries = await self.store.get_unredacted_events_from_cache_or_db(
363+
event_ids
364+
)
365+
362366
logger.debug(
363367
"Handling %i -> %i: %i events to send (current id %i)",
364368
last_token,
365369
next_token,
366-
len(events),
370+
len(event_entries),
367371
self._last_poked_id,
368372
)
369373

370-
if not events and next_token >= self._last_poked_id:
374+
if not event_entries and next_token >= self._last_poked_id:
371375
logger.debug("All events processed")
372376
break
373377

@@ -508,8 +512,14 @@ async def handle_room_events(events: List[EventBase]) -> None:
508512
await handle_event(event)
509513

510514
events_by_room: Dict[str, List[EventBase]] = {}
511-
for event in events:
512-
events_by_room.setdefault(event.room_id, []).append(event)
515+
516+
for event_id in event_ids:
517+
# `event_entries` is unsorted, so we have to iterate over `event_ids`
518+
# to ensure the events are in the right order
519+
event_cache = event_entries.get(event_id)
520+
if event_cache:
521+
event = event_cache.event
522+
events_by_room.setdefault(event.room_id, []).append(event)
513523

514524
await make_deferred_yieldable(
515525
defer.gatherResults(
@@ -524,9 +534,10 @@ async def handle_room_events(events: List[EventBase]) -> None:
524534
logger.debug("Successfully handled up to %i", next_token)
525535
await self.store.update_federation_out_pos("events", next_token)
526536

527-
if events:
537+
if event_entries:
528538
now = self.clock.time_msec()
529-
ts = event_to_received_ts[events[-1].event_id]
539+
last_id = next(reversed(event_ids))
540+
ts = event_to_received_ts[last_id]
530541
assert ts is not None
531542

532543
synapse.metrics.event_processing_lag.labels(
@@ -536,7 +547,7 @@ async def handle_room_events(events: List[EventBase]) -> None:
536547
"federation_sender"
537548
).set(ts)
538549

539-
events_processed_counter.inc(len(events))
550+
events_processed_counter.inc(len(event_entries))
540551

541552
event_processing_loop_room_count.labels("federation_sender").inc(
542553
len(events_by_room)

synapse/handlers/appservice.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,13 @@ async def _notify_interested_services(self, max_token: RoomStreamToken) -> None:
109109
last_token = await self.store.get_appservice_last_pos()
110110
(
111111
upper_bound,
112-
events,
113112
event_to_received_ts,
114-
) = await self.store.get_all_new_events_stream(
115-
last_token, self.current_max, limit=100, get_prev_content=True
113+
) = await self.store.get_all_new_event_ids_stream(
114+
last_token, self.current_max, limit=100
115+
)
116+
117+
events = await self.store.get_events_as_list(
118+
event_to_received_ts.keys(), get_prev_content=True
116119
)
117120

118121
events_by_room: Dict[str, List[EventBase]] = {}

synapse/storage/databases/main/events_worker.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ async def get_events_as_list(
474474
return []
475475

476476
# there may be duplicates so we cast the list to a set
477-
event_entry_map = await self._get_events_from_cache_or_db(
477+
event_entry_map = await self.get_unredacted_events_from_cache_or_db(
478478
set(event_ids), allow_rejected=allow_rejected
479479
)
480480

@@ -509,7 +509,9 @@ async def get_events_as_list(
509509
continue
510510

511511
redacted_event_id = entry.event.redacts
512-
event_map = await self._get_events_from_cache_or_db([redacted_event_id])
512+
event_map = await self.get_unredacted_events_from_cache_or_db(
513+
[redacted_event_id]
514+
)
513515
original_event_entry = event_map.get(redacted_event_id)
514516
if not original_event_entry:
515517
# we don't have the redacted event (or it was rejected).
@@ -588,11 +590,16 @@ async def get_events_as_list(
588590
return events
589591

590592
@cancellable
591-
async def _get_events_from_cache_or_db(
592-
self, event_ids: Iterable[str], allow_rejected: bool = False
593+
async def get_unredacted_events_from_cache_or_db(
594+
self,
595+
event_ids: Iterable[str],
596+
allow_rejected: bool = False,
593597
) -> Dict[str, EventCacheEntry]:
594598
"""Fetch a bunch of events from the cache or the database.
595599
600+
Note that the events pulled by this function will not have any redactions
601+
applied, and no guarantee is made about the ordering of the events returned.
602+
596603
If events are pulled from the database, they will be cached for future lookups.
597604
598605
Unknown events are omitted from the response.

synapse/storage/databases/main/stream.py

+13-15
Original file line numberDiff line numberDiff line change
@@ -1024,28 +1024,31 @@ def _get_events_around_txn(
10241024
"after": {"event_ids": events_after, "token": end_token},
10251025
}
10261026

1027-
async def get_all_new_events_stream(
1028-
self, from_id: int, current_id: int, limit: int, get_prev_content: bool = False
1029-
) -> Tuple[int, List[EventBase], Dict[str, Optional[int]]]:
1027+
async def get_all_new_event_ids_stream(
1028+
self,
1029+
from_id: int,
1030+
current_id: int,
1031+
limit: int,
1032+
) -> Tuple[int, Dict[str, Optional[int]]]:
10301033
"""Get all new events
10311034
1032-
Returns all events with from_id < stream_ordering <= current_id.
1035+
Returns all event ids with from_id < stream_ordering <= current_id.
10331036
10341037
Args:
10351038
from_id: the stream_ordering of the last event we processed
10361039
current_id: the stream_ordering of the most recently processed event
10371040
limit: the maximum number of events to return
1038-
get_prev_content: whether to fetch previous event content
10391041
10401042
Returns:
1041-
A tuple of (next_id, events, event_to_received_ts), where `next_id`
1043+
A tuple of (next_id, event_to_received_ts), where `next_id`
10421044
is the next value to pass as `from_id` (it will either be the
10431045
stream_ordering of the last returned event, or, if fewer than `limit`
10441046
events were found, the `current_id`). The `event_to_received_ts` is
1045-
a dictionary mapping event ID to the event `received_ts`.
1047+
a dictionary mapping event ID to the event `received_ts`, sorted by ascending
1048+
stream_ordering.
10461049
"""
10471050

1048-
def get_all_new_events_stream_txn(
1051+
def get_all_new_event_ids_stream_txn(
10491052
txn: LoggingTransaction,
10501053
) -> Tuple[int, Dict[str, Optional[int]]]:
10511054
sql = (
@@ -1070,15 +1073,10 @@ def get_all_new_events_stream_txn(
10701073
return upper_bound, event_to_received_ts
10711074

10721075
upper_bound, event_to_received_ts = await self.db_pool.runInteraction(
1073-
"get_all_new_events_stream", get_all_new_events_stream_txn
1074-
)
1075-
1076-
events = await self.get_events_as_list(
1077-
event_to_received_ts.keys(),
1078-
get_prev_content=get_prev_content,
1076+
"get_all_new_event_ids_stream", get_all_new_event_ids_stream_txn
10791077
)
10801078

1081-
return upper_bound, events, event_to_received_ts
1079+
return upper_bound, event_to_received_ts
10821080

10831081
async def get_federation_out_pos(self, typ: str) -> int:
10841082
if self._need_to_reset_federation_stream_positions:

tests/handlers/test_appservice.py

+11-7
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,13 @@ def test_notify_interested_services(self):
7676
event = Mock(
7777
sender="@someone:anywhere", type="m.room.message", room_id="!foo:bar"
7878
)
79-
self.mock_store.get_all_new_events_stream.side_effect = [
80-
make_awaitable((0, [], {})),
81-
make_awaitable((1, [event], {event.event_id: 0})),
79+
self.mock_store.get_all_new_event_ids_stream.side_effect = [
80+
make_awaitable((0, {})),
81+
make_awaitable((1, {event.event_id: 0})),
82+
]
83+
self.mock_store.get_events_as_list.side_effect = [
84+
make_awaitable([]),
85+
make_awaitable([event]),
8286
]
8387
self.handler.notify_interested_services(RoomStreamToken(None, 1))
8488

@@ -95,10 +99,10 @@ def test_query_user_exists_unknown_user(self):
9599

96100
event = Mock(sender=user_id, type="m.room.message", room_id="!foo:bar")
97101
self.mock_as_api.query_user.return_value = make_awaitable(True)
98-
self.mock_store.get_all_new_events_stream.side_effect = [
99-
make_awaitable((0, [event], {event.event_id: 0})),
102+
self.mock_store.get_all_new_event_ids_stream.side_effect = [
103+
make_awaitable((0, {event.event_id: 0})),
100104
]
101-
105+
self.mock_store.get_events_as_list.side_effect = [make_awaitable([event])]
102106
self.handler.notify_interested_services(RoomStreamToken(None, 0))
103107

104108
self.mock_as_api.query_user.assert_called_once_with(services[0], user_id)
@@ -112,7 +116,7 @@ def test_query_user_exists_known_user(self):
112116

113117
event = Mock(sender=user_id, type="m.room.message", room_id="!foo:bar")
114118
self.mock_as_api.query_user.return_value = make_awaitable(True)
115-
self.mock_store.get_all_new_events_stream.side_effect = [
119+
self.mock_store.get_all_new_event_ids_stream.side_effect = [
116120
make_awaitable((0, [event], {event.event_id: 0})),
117121
]
118122

0 commit comments

Comments
 (0)