Repository: matrix-org/synapse

Issue:
/sync is not bundling aggregations for limited rooms
[MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675) says that `/sync` is one of the APIs which should bundle aggregations:

> `GET /sync`, only for room sections in the response where `limited` field is `true`; this amounts to all rooms in the response if the `since` request parameter was not passed, also known as an initial sync.

Currently it always disables bundled aggregations for this endpoint, see #5220.

We should also double check the other endpoints:

* [x] `GET /rooms/{roomId}/messages` -- [see code](https://github.com/matrix-org/synapse/blob/a265fbd397ae72b2d3ea4c9310591ff1d0f3e05c/synapse/handlers/pagination.py#L545-L558)
* [x] `GET /rooms/{roomId}/context` -- [see code](https://github.com/matrix-org/synapse/blob/a265fbd397ae72b2d3ea4c9310591ff1d0f3e05c/synapse/rest/client/room.py#L709-L723) (note that this doesn't bundle them for state events, unclear if this is "fine" or not)
* [x] `GET /rooms/{roomId}/event/{eventId}` -- [see code](https://github.com/matrix-org/synapse/blob/a265fbd397ae72b2d3ea4c9310591ff1d0f3e05c/synapse/rest/client/room.py#L664-L666)
* [ ] `GET /sync`
* [x] `GET /relations` -- see #11087


Discussion:
Blocks https://github.com/vector-im/element-web/issues/19588
From my tests on vector.modular.im it appears that `GET /rooms/{roomId}/event/{eventId}` includes the bundled relationship correctly
It looks like we include them in all the other places, minus the state events when calling `context`.

Resolving patch:
diff --git a/synapse/handlers/sync.py b/synapse/handlers/sync.py
--- a/synapse/handlers/sync.py
+++ b/synapse/handlers/sync.py
@@ -98,6 +98,9 @@ class TimelineBatch:
     prev_batch: StreamToken
     events: List[EventBase]
     limited: bool
+    # A mapping of event ID to the bundled aggregations for the above events.
+    # This is only calculated if limited is true.
+    bundled_aggregations: Optional[Dict[str, Dict[str, Any]]] = None
 
     def __bool__(self) -> bool:
         """Make the result appear empty if there are no updates. This is used
@@ -630,10 +633,17 @@ async def _load_filtered_recents(
 
             prev_batch_token = now_token.copy_and_replace("room_key", room_key)
 
+        # Don't bother to bundle aggregations if the timeline is unlimited,
+        # as clients will have all the necessary information.
+        bundled_aggregations = None
+        if limited or newly_joined_room:
+            bundled_aggregations = await self.store.get_bundled_aggregations(recents)
+
         return TimelineBatch(
             events=recents,
             prev_batch=prev_batch_token,
             limited=limited or newly_joined_room,
+            bundled_aggregations=bundled_aggregations,
         )
 
     async def get_state_after_event(
diff --git a/synapse/rest/client/sync.py b/synapse/rest/client/sync.py
--- a/synapse/rest/client/sync.py
+++ b/synapse/rest/client/sync.py
@@ -554,20 +554,9 @@ def serialize(
                 )
 
         serialized_state = serialize(state_events)
-        # Don't bother to bundle aggregations if the timeline is unlimited,
-        # as clients will have all the necessary information.
-        # bundle_aggregations=room.timeline.limited,
-        #
-        # richvdh 2021-12-15: disable this temporarily as it has too high an
-        # overhead for initialsyncs. We need to figure out a way that the
-        # bundling can be done *before* the events are stored in the
-        # SyncResponseCache so that this part can be synchronous.
-        #
-        # Ensure to re-enable the test at tests/rest/client/test_relations.py::RelationsTestCase.test_bundled_aggregations.
-        # if room.timeline.limited:
-        #    aggregations = await self.store.get_bundled_aggregations(timeline_events)
-        aggregations = None
-        serialized_timeline = serialize(timeline_events, aggregations)
+        serialized_timeline = serialize(
+            timeline_events, room.timeline.bundled_aggregations
+        )
 
         account_data = room.account_data
 


Test patch:
diff --git a/tests/rest/client/test_relations.py b/tests/rest/client/test_relations.py
--- a/tests/rest/client/test_relations.py
+++ b/tests/rest/client/test_relations.py
@@ -572,11 +572,11 @@ def _find_and_assert_event(events):
         assert_bundle(channel.json_body["event"]["unsigned"].get("m.relations"))
 
         # Request sync.
-        # channel = self.make_request("GET", "/sync", access_token=self.user_token)
-        # self.assertEquals(200, channel.code, channel.json_body)
-        # room_timeline = channel.json_body["rooms"]["join"][self.room]["timeline"]
-        # self.assertTrue(room_timeline["limited"])
-        # _find_and_assert_event(room_timeline["events"])
+        channel = self.make_request("GET", "/sync", access_token=self.user_token)
+        self.assertEquals(200, channel.code, channel.json_body)
+        room_timeline = channel.json_body["rooms"]["join"][self.room]["timeline"]
+        self.assertTrue(room_timeline["limited"])
+        _find_and_assert_event(room_timeline["events"])
 
         # Note that /relations is tested separately in test_aggregation_get_event_for_thread
         # since it needs different data configured.