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

Include bundled aggregations in /sync and related fixes #11478

Merged
merged 14 commits into from
Dec 6, 2021
Merged
1 change: 1 addition & 0 deletions changelog.d/11478.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Include bundled relation aggregations during a limited `/sync` request, per [MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675).
58 changes: 35 additions & 23 deletions synapse/events/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ def format_event_for_client_v2_without_room_id(d: JsonDict) -> JsonDict:
def serialize_event(
e: Union[JsonDict, EventBase],
time_now_ms: int,
*,
as_client_event: bool = True,
event_format: Callable[[JsonDict], JsonDict] = format_event_for_client_v1,
token_id: Optional[str] = None,
Expand Down Expand Up @@ -393,16 +394,18 @@ async def serialize_event(
self,
event: Union[JsonDict, EventBase],
time_now: int,
bundle_relations: bool = True,
*,
bundle_aggregations: bool = True,
**kwargs: Any,
) -> JsonDict:
"""Serializes a single event.

Args:
event: The event being serialized.
time_now: The current time in milliseconds
bundle_relations: Whether to include the bundled relations for this
event.
bundle_aggregations: Whether to include the bundled aggregations for this
event. Only applies to non-state events. (State events never include
bundled aggregations.)
**kwargs: Arguments to pass to `serialize_event`

Returns:
Expand All @@ -414,28 +417,35 @@ async def serialize_event(

serialized_event = serialize_event(event, time_now, **kwargs)

# If MSC1849 is enabled then we need to look if there are any relations
# we need to bundle in with the event.
# Do not bundle relations if the event has been redacted
if not event.internal_metadata.is_redacted() and (
self._msc1849_enabled and bundle_relations
# Check if there are any bundled aggregations to include with the event.
#
# Do not bundle aggregations if any of the following at true:
#
# * Support is disabled via the configuration or the caller.
# * The event is a state event.
# * The event has been redacted.
if (
self._msc1849_enabled
and bundle_aggregations
and not event.is_state()
and not event.internal_metadata.is_redacted()
):
await self._injected_bundled_relations(event, time_now, serialized_event)
await self._injected_bundled_aggregations(event, time_now, serialized_event)

return serialized_event

async def _injected_bundled_relations(
async def _injected_bundled_aggregations(
self, event: EventBase, time_now: int, serialized_event: JsonDict
) -> None:
"""Potentially injects bundled relations into the unsigned portion of the serialized event.
"""Potentially injects bundled aggregations into the unsigned portion of the serialized event.

Args:
event: The event being serialized.
time_now: The current time in milliseconds
serialized_event: The serialized event which may be modified.

"""
# Do not bundle relations for an event which represents an edit or an
# Do not bundle aggregations for an event which represents an edit or an
# annotation. It does not make sense for them to have related events.
relates_to = event.content.get("m.relates_to")
if isinstance(relates_to, (dict, frozendict)):
Expand All @@ -445,18 +455,18 @@ async def _injected_bundled_relations(

event_id = event.event_id

# The bundled relations to include.
relations = {}
# The bundled aggregations to include.
aggregations = {}

annotations = await self.store.get_aggregation_groups_for_event(event_id)
if annotations.chunk:
relations[RelationTypes.ANNOTATION] = annotations.to_dict()
aggregations[RelationTypes.ANNOTATION] = annotations.to_dict()

references = await self.store.get_relations_for_event(
event_id, RelationTypes.REFERENCE, direction="f"
)
if references.chunk:
relations[RelationTypes.REFERENCE] = references.to_dict()
aggregations[RelationTypes.REFERENCE] = references.to_dict()

edit = None
if event.type == EventTypes.Message:
Expand All @@ -482,7 +492,7 @@ async def _injected_bundled_relations(
else:
serialized_event["content"].pop("m.relates_to", None)

relations[RelationTypes.REPLACE] = {
aggregations[RelationTypes.REPLACE] = {
"event_id": edit.event_id,
"origin_server_ts": edit.origin_server_ts,
"sender": edit.sender,
Expand All @@ -495,17 +505,19 @@ async def _injected_bundled_relations(
latest_thread_event,
) = await self.store.get_thread_summary(event_id)
if latest_thread_event:
relations[RelationTypes.THREAD] = {
# Don't bundle relations as this could recurse forever.
aggregations[RelationTypes.THREAD] = {
# Don't bundle aggregations as this could recurse forever.
"latest_event": await self.serialize_event(
latest_thread_event, time_now, bundle_relations=False
latest_thread_event, time_now, bundle_aggregations=False
),
"count": thread_count,
}

# If any bundled relations were found, include them.
if relations:
serialized_event["unsigned"].setdefault("m.relations", {}).update(relations)
# If any bundled aggregations were found, include them.
if aggregations:
serialized_event["unsigned"].setdefault("m.relations", {}).update(
aggregations
)

async def serialize_events(
self, events: Iterable[Union[JsonDict, EventBase]], time_now: int, **kwargs: Any
Expand Down
5 changes: 2 additions & 3 deletions synapse/handlers/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,8 @@ async def get_stream(
events,
time_now,
as_client_event=as_client_event,
# We don't bundle "live" events, as otherwise clients
# will end up double counting annotations.
bundle_relations=False,
# Don't bundle aggregations as this is a deprecated API.
bundle_aggregations=False,
)

chunk = {
Expand Down
30 changes: 24 additions & 6 deletions synapse/handlers/initial_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,11 @@ async def handle_room(event: RoomsForUser) -> None:

invite_event = await self.store.get_event(event.event_id)
d["invite"] = await self._event_serializer.serialize_event(
invite_event, time_now, as_client_event
invite_event,
time_now,
# Don't bundle aggregations as this is a deprecated API.
bundle_aggregations=False,
as_client_event=as_client_event,
clokep marked this conversation as resolved.
Show resolved Hide resolved
)

rooms_ret.append(d)
Expand Down Expand Up @@ -216,7 +220,11 @@ async def handle_room(event: RoomsForUser) -> None:
d["messages"] = {
"chunk": (
await self._event_serializer.serialize_events(
messages, time_now=time_now, as_client_event=as_client_event
messages,
time_now=time_now,
# Don't bundle aggregations as this is a deprecated API.
bundle_aggregations=False,
as_client_event=as_client_event,
)
),
"start": await start_token.to_string(self.store),
Expand All @@ -226,6 +234,8 @@ async def handle_room(event: RoomsForUser) -> None:
d["state"] = await self._event_serializer.serialize_events(
current_state.values(),
time_now=time_now,
# Don't bundle aggregations as this is a deprecated API.
bundle_aggregations=False,
as_client_event=as_client_event,
)

Expand Down Expand Up @@ -366,14 +376,18 @@ async def _room_initial_sync_parted(
"room_id": room_id,
"messages": {
"chunk": (
await self._event_serializer.serialize_events(messages, time_now)
# Don't bundle aggregations as this is a deprecated API.
await self._event_serializer.serialize_events(
messages, time_now, bundle_aggregations=False
)
),
"start": await start_token.to_string(self.store),
"end": await end_token.to_string(self.store),
},
"state": (
# Don't bundle aggregations as this is a deprecated API.
await self._event_serializer.serialize_events(
room_state.values(), time_now
room_state.values(), time_now, bundle_aggregations=False
)
),
"presence": [],
Expand All @@ -392,8 +406,9 @@ async def _room_initial_sync_joined(

# TODO: These concurrently
time_now = self.clock.time_msec()
# Don't bundle aggregations as this is a deprecated API.
state = await self._event_serializer.serialize_events(
current_state.values(), time_now
current_state.values(), time_now, bundle_aggregations=False
)

now_token = self.hs.get_event_sources().get_current_token()
Expand Down Expand Up @@ -467,7 +482,10 @@ async def get_receipts() -> List[JsonDict]:
"room_id": room_id,
"messages": {
"chunk": (
await self._event_serializer.serialize_events(messages, time_now)
# Don't bundle aggregations as this is a deprecated API.
await self._event_serializer.serialize_events(
messages, time_now, bundle_aggregations=False
)
),
"start": await start_token.to_string(self.store),
"end": await end_token.to_string(self.store),
Expand Down
8 changes: 1 addition & 7 deletions synapse/handlers/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,13 +247,7 @@ async def get_state_events(
room_state = room_state_events[membership_event_id]

now = self.clock.time_msec()
events = await self._event_serializer.serialize_events(
room_state.values(),
now,
# We don't bother bundling aggregations in when asked for state
# events, as clients won't use them.
bundle_relations=False,
)
events = await self._event_serializer.serialize_events(room_state.values(), now)
return events

async def get_joined_members(self, requester: Requester, room_id: str) -> dict:
Expand Down
13 changes: 2 additions & 11 deletions synapse/rest/admin/rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,13 +449,7 @@ async def on_GET(
event_ids = await self.store.get_current_state_ids(room_id)
events = await self.store.get_events(event_ids.values())
now = self.clock.time_msec()
room_state = await self._event_serializer.serialize_events(
events.values(),
now,
# We don't bother bundling aggregations in when asked for state
# events, as clients won't use them.
bundle_relations=False,
)
room_state = await self._event_serializer.serialize_events(events.values(), now)
ret = {"state": room_state}

return HTTPStatus.OK, ret
Expand Down Expand Up @@ -789,10 +783,7 @@ async def on_GET(
results["events_after"], time_now
)
results["state"] = await self._event_serializer.serialize_events(
results["state"],
time_now,
# No need to bundle aggregations for state events
bundle_relations=False,
results["state"], time_now
)

return HTTPStatus.OK, results
Expand Down
9 changes: 4 additions & 5 deletions synapse/rest/client/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,13 @@ async def on_GET(
)

now = self.clock.time_msec()
# We set bundle_relations to False when retrieving the original
# event because we want the content before relations were applied to
# it.
# Do not bundle aggregations when retrieving the original event because
# we want the content before relations are applied to it.
original_event = await self._event_serializer.serialize_event(
event, now, bundle_relations=False
event, now, bundle_aggregations=False
)
# The relations returned for the requested event do include their
# bundled relations.
# bundled aggregations.
serialized_events = await self._event_serializer.serialize_events(events, now)

return_value = pagination_chunk.to_dict()
Expand Down
5 changes: 1 addition & 4 deletions synapse/rest/client/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,10 +716,7 @@ async def on_GET(
results["events_after"], time_now
)
results["state"] = await self._event_serializer.serialize_events(
results["state"],
time_now,
# No need to bundle aggregations for state events
bundle_relations=False,
results["state"], time_now
)

return 200, results
Expand Down
6 changes: 3 additions & 3 deletions synapse/rest/client/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,9 +520,9 @@ def serialize(events: Iterable[EventBase]) -> Awaitable[List[JsonDict]]:
return self._event_serializer.serialize_events(
events,
time_now=time_now,
# We don't bundle "live" events, as otherwise clients
# will end up double counting annotations.
bundle_relations=False,
# Don't bother to bundle aggregations if the timeline is unlimited,
# as clients will have all the necessary information.
bundle_aggregations=room.timeline.limited,
token_id=token_id,
event_format=event_formatter,
only_event_fields=only_fields,
Expand Down
Loading