diff --git a/changelog.d/20182.bugfix b/changelog.d/20182.bugfix new file mode 100644 index 0000000000..637c3efb96 --- /dev/null +++ b/changelog.d/20182.bugfix @@ -0,0 +1 @@ +Fix slow recursive `/relations` requests in large rooms by joining events inside the recursive query. diff --git a/synapse/storage/databases/main/relations.py b/synapse/storage/databases/main/relations.py index 9d9c37e2a4..58f9321628 100644 --- a/synapse/storage/databases/main/relations.py +++ b/synapse/storage/databases/main/relations.py @@ -237,19 +237,28 @@ class RelationsWorkerStore(SQLBaseStore): # If no recursion is needed then the event_relations table is queried # for direct children of the requested event. if recurse: + # The events table is joined inside the recursion rather than + # after it: Postgres cannot accurately estimate the size of a recursive CTE, + # and when the guess is large it joins the CTE against a scan of + # *every* event in the room, which takes seconds in busy rooms. + # Joining per step keeps every events lookup an index probe. sql = """ WITH RECURSIVE related_events AS ( - SELECT event_id, relation_type, relates_to_id, 0 AS depth - FROM event_relations - WHERE relates_to_id = ? - UNION SELECT e.event_id, e.relation_type, e.relates_to_id, depth + 1 - FROM event_relations e - INNER JOIN related_events r ON r.event_id = e.relates_to_id - WHERE depth <= 3 + SELECT er.event_id, er.relation_type, ev.room_id, ev.sender, + ev.topological_ordering, ev.stream_ordering, ev.type, 0 AS depth + FROM event_relations er + INNER JOIN events ev ON ev.event_id = er.event_id + WHERE er.relates_to_id = ? + UNION ALL + SELECT er.event_id, er.relation_type, ev.room_id, ev.sender, + ev.topological_ordering, ev.stream_ordering, ev.type, r.depth + 1 + FROM related_events r + INNER JOIN event_relations er ON er.relates_to_id = r.event_id + INNER JOIN events ev ON ev.event_id = er.event_id + WHERE r.depth <= 3 ) SELECT event_id, relation_type, sender, topological_ordering, stream_ordering FROM related_events - INNER JOIN events USING (event_id) WHERE %s ORDER BY topological_ordering %s, stream_ordering %s LIMIT ?;