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

Commit daf498e

Browse files
Fix 500 error on /messages when we accumulate more than 5 backward extremities (#11027)
Found while working on the Gitter backfill script and noticed it only happened after we sent 7 batches, https://gitlab.com/gitterHQ/webapp/-/merge_requests/2229#note_665906390 When there are more than 5 backward extremities for a given depth, backfill will throw an error because we sliced the extremity list to 5 but then try to iterate over the full list. This causes us to look for state that we never fetched and we get a `KeyError`. Before when calling `/messages` when there are more than 5 backward extremities: ``` Traceback (most recent call last): File "/usr/local/lib/python3.8/site-packages/synapse/http/server.py", line 258, in _async_render_wrapper callback_return = await self._async_render(request) File "/usr/local/lib/python3.8/site-packages/synapse/http/server.py", line 446, in _async_render callback_return = await raw_callback_return File "/usr/local/lib/python3.8/site-packages/synapse/rest/client/room.py", line 580, in on_GET msgs = await self.pagination_handler.get_messages( File "/usr/local/lib/python3.8/site-packages/synapse/handlers/pagination.py", line 396, in get_messages await self.hs.get_federation_handler().maybe_backfill( File "/usr/local/lib/python3.8/site-packages/synapse/handlers/federation.py", line 133, in maybe_backfill return await self._maybe_backfill_inner(room_id, current_depth, limit) File "/usr/local/lib/python3.8/site-packages/synapse/handlers/federation.py", line 386, in _maybe_backfill_inner likely_extremeties_domains = get_domains_from_state(states[e_id]) KeyError: '$zpFflMEBtZdgcMQWTakaVItTLMjLFdKcRWUPHbbSZJl' ```
1 parent efd0074 commit daf498e

File tree

4 files changed

+79
-12
lines changed

4 files changed

+79
-12
lines changed

changelog.d/11027.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix 500 error on `/messages` when the server accumulates more than 5 backwards extremities at a given depth for a room.

synapse/handlers/federation.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -238,28 +238,30 @@ async def _maybe_backfill_inner(
238238
)
239239
return False
240240

241-
logger.debug(
242-
"room_id: %s, backfill: current_depth: %s, max_depth: %s, extrems: %s",
243-
room_id,
244-
current_depth,
245-
max_depth,
246-
sorted_extremeties_tuple,
247-
)
248-
249241
# We ignore extremities that have a greater depth than our current depth
250242
# as:
251243
# 1. we don't really care about getting events that have happened
252-
# before our current position; and
244+
# after our current position; and
253245
# 2. we have likely previously tried and failed to backfill from that
254246
# extremity, so to avoid getting "stuck" requesting the same
255247
# backfill repeatedly we drop those extremities.
256248
filtered_sorted_extremeties_tuple = [
257249
t for t in sorted_extremeties_tuple if int(t[1]) <= current_depth
258250
]
259251

252+
logger.debug(
253+
"room_id: %s, backfill: current_depth: %s, limit: %s, max_depth: %s, extrems: %s filtered_sorted_extremeties_tuple: %s",
254+
room_id,
255+
current_depth,
256+
limit,
257+
max_depth,
258+
sorted_extremeties_tuple,
259+
filtered_sorted_extremeties_tuple,
260+
)
261+
260262
# However, we need to check that the filtered extremities are non-empty.
261263
# If they are empty then either we can a) bail or b) still attempt to
262-
# backill. We opt to try backfilling anyway just in case we do get
264+
# backfill. We opt to try backfilling anyway just in case we do get
263265
# relevant events.
264266
if filtered_sorted_extremeties_tuple:
265267
sorted_extremeties_tuple = filtered_sorted_extremeties_tuple
@@ -389,7 +391,7 @@ async def try_backfill(domains: List[str]) -> bool:
389391
for key, state_dict in states.items()
390392
}
391393

392-
for e_id, _ in sorted_extremeties_tuple:
394+
for e_id in event_ids:
393395
likely_extremeties_domains = get_domains_from_state(states[e_id])
394396

395397
success = await try_backfill(

synapse/handlers/federation_event.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ async def check_join_restrictions(
392392

393393
@log_function
394394
async def backfill(
395-
self, dest: str, room_id: str, limit: int, extremities: List[str]
395+
self, dest: str, room_id: str, limit: int, extremities: Iterable[str]
396396
) -> None:
397397
"""Trigger a backfill request to `dest` for the given `room_id`
398398

tests/handlers/test_federation.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,18 @@
2323
from synapse.logging.context import LoggingContext, run_in_background
2424
from synapse.rest import admin
2525
from synapse.rest.client import login, room
26+
from synapse.types import create_requester
2627
from synapse.util.stringutils import random_string
2728

2829
from tests import unittest
2930

3031
logger = logging.getLogger(__name__)
3132

3233

34+
def generate_fake_event_id() -> str:
35+
return "$fake_" + random_string(43)
36+
37+
3338
class FederationTestCase(unittest.HomeserverTestCase):
3439
servlets = [
3540
admin.register_servlets,
@@ -198,6 +203,65 @@ def test_rejected_state_event_state(self):
198203

199204
self.assertEqual(sg, sg2)
200205

206+
def test_backfill_with_many_backward_extremities(self):
207+
"""
208+
Check that we can backfill with many backward extremities.
209+
The goal is to make sure that when we only use a portion
210+
of backwards extremities(the magic number is more than 5),
211+
no errors are thrown.
212+
213+
Regression test, see #11027
214+
"""
215+
# create the room
216+
user_id = self.register_user("kermit", "test")
217+
tok = self.login("kermit", "test")
218+
requester = create_requester(user_id)
219+
220+
room_id = self.helper.create_room_as(room_creator=user_id, tok=tok)
221+
222+
ev1 = self.helper.send(room_id, "first message", tok=tok)
223+
224+
# Create "many" backward extremities. The magic number we're trying to
225+
# create more than is 5 which corresponds to the number of backward
226+
# extremities we slice off in `_maybe_backfill_inner`
227+
for _ in range(0, 8):
228+
event_handler = self.hs.get_event_creation_handler()
229+
event, context = self.get_success(
230+
event_handler.create_event(
231+
requester,
232+
{
233+
"type": "m.room.message",
234+
"content": {
235+
"msgtype": "m.text",
236+
"body": "message connected to fake event",
237+
},
238+
"room_id": room_id,
239+
"sender": user_id,
240+
},
241+
prev_event_ids=[
242+
ev1["event_id"],
243+
# We're creating an backward extremity each time thanks
244+
# to this fake event
245+
generate_fake_event_id(),
246+
],
247+
)
248+
)
249+
self.get_success(
250+
event_handler.handle_new_client_event(requester, event, context)
251+
)
252+
253+
current_depth = 1
254+
limit = 100
255+
with LoggingContext("receive_pdu"):
256+
# Make sure backfill still works
257+
d = run_in_background(
258+
self.hs.get_federation_handler().maybe_backfill,
259+
room_id,
260+
current_depth,
261+
limit,
262+
)
263+
self.get_success(d)
264+
201265
def test_backfill_floating_outlier_membership_auth(self):
202266
"""
203267
As the local homeserver, check that we can properly process a federated

0 commit comments

Comments
 (0)