Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSC2675: Serverside aggregations of message relationships #2675

Merged
merged 109 commits into from
Jan 15, 2022
Merged
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
c9794d4
initial version of serverside aggregations proposal
uhoreg Jul 7, 2020
03ce398
fix MSC numbers
uhoreg Jul 7, 2020
e5d133b
clarification
uhoreg Jun 2, 2021
2d36c36
add e2ee section from 2674 here, as it is only needed for server-side…
bwindels Jul 1, 2021
8eec329
move edge case wrt to calling /context on a relation here from 2674
bwindels Jul 5, 2021
343afff
fix typo
bwindels Nov 23, 2021
8e6c0a3
clarify which APIs should bundle relations
bwindels Nov 23, 2021
d5bbc72
move stale_events over to future extensions section
bwindels Nov 23, 2021
2091034
summarize stale_events and make tone conditional to mark that is not …
bwindels Nov 23, 2021
68c95a4
casing and wording
bwindels Nov 23, 2021
84a4a75
clarify in summary an API for requesting relations is also proposed
bwindels Nov 23, 2021
ca4ceff
remove proposal for batch get event api as is unused and unimplemented
bwindels Nov 24, 2021
7de182c
attempt to clarify relations vs aggregations
bwindels Nov 24, 2021
143a70e
clarify pagination and align it with synapse impl already in the wild
bwindels Nov 25, 2021
ad159f4
conciseness
bwindels Nov 25, 2021
948f7cd
better headers
bwindels Nov 25, 2021
5d7b404
clarify that relations are always returned, contrary to aggregations
bwindels Nov 25, 2021
e8aca3e
document the limitation of the event type not being known in e2ee rooms
bwindels Nov 25, 2021
657617f
specify that redacted relations are not aggregated
bwindels Nov 25, 2021
ffa3995
remove type in (non-binding) example as synapse doesn't do this
bwindels Nov 25, 2021
561cfc3
mention that these are just examples
bwindels Nov 25, 2021
42221ab
clarify that this is a non-normative example
bwindels Nov 25, 2021
037cab3
Update proposals/2675-aggregations-server.md
bwindels Nov 25, 2021
d7aa3ed
add http method for endpoint list
bwindels Nov 25, 2021
30cf5f8
line break
bwindels Nov 26, 2021
ee152e0
remove "unbundled relations" term, it's just confusing
bwindels Nov 26, 2021
483224a
some more restructuring of text after changing doc structure
bwindels Nov 26, 2021
16723da
mention original_event for m.replace relations
bwindels Nov 26, 2021
8e532ac
remove dir param as it is unused and unimplemented
bwindels Nov 29, 2021
6cf4d58
clarify that relating pending events should happen by transaction_id
bwindels Nov 29, 2021
b0fbee1
remove unimplemented /aggregations/{eventID}//{eventType}/{key}
bwindels Nov 29, 2021
53a30fc
Update proposals/2675-aggregations-server.md
bwindels Nov 30, 2021
f959119
mention that the server might not be aware of all the relations
bwindels Nov 30, 2021
dead293
clarify that redacted events should still return their relations and …
bwindels Nov 30, 2021
7299a8c
remove /context edge case, it should not be special-cased
bwindels Nov 30, 2021
37db984
Update proposals/2675-aggregations-server.md
bwindels Nov 30, 2021
36fb70b
Update proposals/2675-aggregations-server.md
bwindels Nov 30, 2021
061a104
Update proposals/2675-aggregations-server.md
bwindels Nov 30, 2021
bf9340e
bad example, replies doesn't use relations
bwindels Nov 30, 2021
c8533ec
clarify that we dont bundle discrete events
bwindels Nov 30, 2021
51bc1da
clarify that we dont bundle discrete events, again
bwindels Nov 30, 2021
7559275
improve example
bwindels Nov 30, 2021
c475bb9
clarify this MSC does not use a prefix
bwindels Nov 30, 2021
872d3f5
better english
bwindels Nov 30, 2021
7119947
clarify pagination in example
bwindels Nov 30, 2021
0d7b525
better english
bwindels Nov 30, 2021
764d785
remove contradication: m.reference doesn't support pagination but exa…
bwindels Nov 30, 2021
26376de
double punctuation
bwindels Nov 30, 2021
382bb1d
clarify that only the bundled aggregation limit for truncation can't …
bwindels Nov 30, 2021
a0af573
move e2ee limitation to limitations section
bwindels Nov 30, 2021
2557d23
clarify prefixes
bwindels Nov 30, 2021
c62165a
mention that state events never bundle aggregations
bwindels Dec 2, 2021
e26e465
Update proposals/2675-aggregations-server.md
bwindels Dec 3, 2021
503a569
add that the visibility of relations can derive from that of the target
bwindels Dec 14, 2021
049863b
typsos
bwindels Dec 14, 2021
44d04f4
be more explicit
bwindels Dec 14, 2021
1160b4a
moar rewording
bwindels Dec 14, 2021
0abf2ce
keep related parts together
bwindels Dec 14, 2021
e460a27
don't make a relation invisible because the target event isn't
bwindels Dec 14, 2021
58cb3ef
Update proposals/2675-aggregations-server.md
bwindels Dec 14, 2021
de00362
better words
bwindels Dec 14, 2021
3dcf0ce
Update proposals/2675-aggregations-server.md
bwindels Dec 15, 2021
9d96311
Update proposals/2675-aggregations-server.md
bwindels Dec 15, 2021
541632f
Update proposals/2675-aggregations-server.md
bwindels Dec 15, 2021
a9943c0
Update proposals/2675-aggregations-server.md
bwindels Dec 15, 2021
f97df03
be more precise when clients should ensure the key is shared
bwindels Dec 15, 2021
798835a
mention that ignored users can cause different aggregations for users
bwindels Dec 15, 2021
fe78152
move visibility rule changes to MSC3570
bwindels Dec 15, 2021
17122bf
don't overspecify visibility limitation, allow for unspecified behaviour
bwindels Dec 15, 2021
ba59c43
Update proposals/2675-aggregations-server.md
bwindels Dec 15, 2021
68b302e
Update proposals/2675-aggregations-server.md
bwindels Dec 15, 2021
d182b9a
Update proposals/2675-aggregations-server.md
bwindels Dec 15, 2021
67d60b1
Update proposals/2675-aggregations-server.md
bwindels Dec 15, 2021
71757a4
Update proposals/2675-aggregations-server.md
bwindels Dec 15, 2021
48c3063
move non-normative note to below example
bwindels Dec 15, 2021
f93143b
make rel_type mandatory as the response structure doesn't allow for m…
bwindels Dec 15, 2021
29fcd87
fix typo/thinko
bwindels Dec 15, 2021
6a76e5c
make pagination forward only as there is no use case for backwards
bwindels Dec 15, 2021
f61d8dc
Update proposals/2675-aggregations-server.md
bwindels Dec 15, 2021
c65b467
add non-normative aggregation examples
bwindels Dec 15, 2021
0717a51
Update proposals/2675-aggregations-server.md
bwindels Dec 15, 2021
71e4e5c
use relation type rather than rel_type
bwindels Dec 15, 2021
942cfcb
change trailing slashes remark to event_type, rel_type is mandatory now
bwindels Dec 16, 2021
b680406
reword and split out client-side aggregation section
bwindels Dec 16, 2021
ef57449
rename parent event to target event, the term used elsewhere
bwindels Dec 16, 2021
33471e6
apply suggestion
bwindels Dec 16, 2021
9399b0f
apply suggestion
bwindels Dec 16, 2021
6e8ff69
remove pagination
bwindels Dec 16, 2021
8033733
remove mentions of /aggregations endpoint after removing pagination
bwindels Dec 16, 2021
3b5f05c
add note about not bundling into state events
bwindels Dec 16, 2021
513cd91
restructure headers so more of the aggregations stuff is under section
bwindels Dec 16, 2021
178976a
make rel_type mandatory for /relations and better wording
bwindels Dec 16, 2021
06fcc15
remove confusion that aggregations contain more info than relations
bwindels Dec 16, 2021
ea8ada3
Update proposals/2675-aggregations-server.md
bwindels Dec 16, 2021
6cdf9e7
Update proposals/2675-aggregations-server.md
bwindels Dec 16, 2021
bcf7d15
mention that tokens from /sync, /messages can be used on /relations
bwindels Dec 16, 2021
0114090
try not to be overly prescriptive
bwindels Dec 16, 2021
3c77504
remove edge case of ignoring events without target event, as ignoring…
bwindels Dec 17, 2021
8a3d9b1
clarify limitation for encrypted rooms
bwindels Dec 17, 2021
8460f64
make rel_type optional again for /relations
bwindels Dec 17, 2021
e1e2593
Update proposals/2675-aggregations-server.md
bwindels Jan 7, 2022
e72837a
Update proposals/2675-aggregations-server.md
bwindels Jan 7, 2022
ae20bcd
Update proposals/2675-aggregations-server.md
bwindels Jan 7, 2022
1dbe2f0
Update proposals/2675-aggregations-server.md
bwindels Jan 7, 2022
88892a5
Update proposals/2675-aggregations-server.md
bwindels Jan 7, 2022
67fa56e
mention requires auth and rate-limited on /relations
bwindels Jan 7, 2022
be6b1c7
replace hypothetical examples for bundled aggregations with non-norma…
bwindels Jan 10, 2022
8f3a28a
move to MSC 2676 as it's specific to edits
bwindels Jan 10, 2022
ae7dc26
dont repeat how local echo using transaction_id works
bwindels Jan 10, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
320 changes: 320 additions & 0 deletions proposals/2675-aggregations-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
# MSC2675: Serverside aggregations of message relationships
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@revidee says:

So regarding Handling limited (gappy) syncs: I couldn't find any MSC Proposal handling this, is there any?

IMHO, this is the most important part, since for example:
If we received and event via sync, which may has annotations bundled into it and keep it up-to-date via local aggregation using all future m.relates_to events, but then go offline for a day and come back, without syncing all events in-between, we end up in an incorrect state for the local aggregations of the already received event.

If we would simply use a locally cached copy of the locally aggregated event, it would not match a refreshed version from the server. Thus, in this situation, we can't really trust any local copies of events, except we query all relational events, that happened in-between. This would make "jumping back" to the locally saved event somewhat incorrect (mismatches a fresh copy from the server). And refreshing all locally saved events would diminish any possible traffic savings gained by local caching.

Only case I see in which we could realistically save traffic is an initial sync, with filters enabled to not fetch all m.annotation / ... / events, but include then in consecutive /sync calls. Other than that, this makes locally caching/saving events, in order to not re-query them, a step harder, since we have to be really careful to keep all the local events in sync & up-to-date.

This problem is (imho) rather drastic in bigger rooms with large amounts of daily traffic. When loading older messages we may end up loading batches upon batches of events filled with relational events only until we load a real m.text message that the client could display. (e.g. if many reactions have happened) But we cannot exclude them, if we choose to locally aggregate already received events, since we might miss some otherwise.

In that light, it almost seems more logical and traffic-saving to discard local copies all together in order to take advantage of aggregations and bundling. In that case, we can always apply a filter for the events received in /sync and can be sure that the data received is up-to-date. (after loading/paginating through the /aggregations endpoint if there is a next_batch present)

Am I missing something obvious here, or am I correct that making local copies in combination with this MSC seems like a pain?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So regarding Handling limited (gappy) syncs: I couldn't find any MSC Proposal handling this, is there any?

Correct, there is currently not MSC which proposes this behavior.

IMHO, this is the most important part, since for example:
If we received and event via sync, which may has annotations bundled into it and keep it up-to-date via local aggregation using all future m.relates_to events, but then go offline for a day and come back, without syncing all events in-between, we end up in an incorrect state for the local aggregations of the already received event.

Correct, the local client might not know some of the related messages without filling the entire gap. (Note that I don't think this is any different than if only local aggregation was done -- so I'm unsure if this MSC really changes that behavior.)

If we would simply use a locally cached copy of the locally aggregated event, it would not match a refreshed version from the server. Thus, in this situation, we can't really trust any local copies of events, except we query all relational events, that happened in-between. This would make "jumping back" to the locally saved event somewhat incorrect (mismatches a fresh copy from the server). And refreshing all locally saved events would diminish any possible traffic savings gained by local caching.

I think this is pretty much what the gappy syncs issue in the MSC describes. I'm unsure what current clients do -- @gsouquet do you know if Element Web does anything special in this case?

Only case I see in which we could realistically save traffic is an initial sync, with filters enabled to not fetch all m.annotation / ... / events, but include then in consecutive /sync calls. Other than that, this makes locally caching/saving events, in order to not re-query them, a step harder, since we have to be really careful to keep all the local events in sync & up-to-date.

This might work, but I doubt the savings in traffic would be the tremendous, unless the majority of your events are annotations, specifically. (I think you would still want to fetch references / edits / threads since the aggregation of those somewhat assumes you're going to fetch the full event anyway).

In that light, it almost seems more logical and traffic-saving to discard local copies all together in order to take advantage of aggregations and bundling. In that case, we can always apply a filter for the events received in /sync and can be sure that the data received is up-to-date. (after loading/paginating through the /aggregations endpoint if there is a next_batch present)

Note that the /aggregations endpoint was removed from this MSC (and from Synapse). MSC3571 includes the bits split out of MSC2675.

Am I missing something obvious here, or am I correct that making local copies in combination with this MSC seems like a pain?

I don't think you're missing anything obvious, but maybe @gsouquet has some ideas of how the clients deal with this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for raising this @revidee

Just like @clokep said, I believe you're pretty much on point and your assessment of the situation is pretty correct.

Besides threads, Element Web does not use bundled aggregation.

If we take the example of reactions, the bundled aggregations does not give you enough information to generate the user interface that Element wants to provide. To be able to do that you will need a complete list of the annotations, which is not practical and comes with quite a big network overhead

The way that threads went around this problem is by providing a set of information to render the initial event tile without having to fetch all events that belong to a thread (we're currently giving the number of replies to a thread, whether the logged in user has participated to it, and the last event of that thread). That means that client only have to fetch the root event of that thread to refresh the bundled relationship on app load.
This is more practical but comes at the cost of slightly coupling your server implementation with your UI

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alphapapa says:

@ara4n @turt2live I guess it's pointless to comment here since the proposal has been merged, but I feel like chiming in, as a client developer:

I've been reluctant to implement anything not yet in a released version of the spec, but in my client I did implement support for reaction annotations, since they're so widely used in Element and other clients already. But now I'm seeing my client not render annotations in some cases, which I've found is because the server is now aggregating some of them.

I understand the motivations for this proposal, but at the same time, it increases the complexity of developing clients: rather than simply handling each reaction annotation event, a client must handle both aggregated reaction annotations and individual reaction annotation events. This means that, when an individual event comes in, I have to emulate the server-side aggregation in my client, adding it to the annotated event's unsigned data myself, so that I can still have a single path for rendering an event.

Fundamentally, this means that a single logical action (reacting to a message) may now be represented in a room's timeline in two different ways: as individual events, or as aggregations of metadata on the annotated event. So this does not seem to make it easier for clients, but rather, more complicated.

I feel like Matrix would do well to emulate the Zen of Python: there should be one, and preferably only one, way to represent a logical event in a room's timeline. This would greatly simplify implementations on both clients and servers, which would do much to help ensure Matrix's adoption and longevity.

My two cents. Thanks.


It's common to want to send events in Matrix which relate to existing events -
for instance, reactions, edits and even replies/threads.

Clients typically need to track the related events alongside the original
event they relate to, in order to correctly display them. For instance,
reaction events need to be aggregated together by summing and be shown next to
the event they react to; edits need to be aggregated together by replacing the
original event and subsequent edits, etc.

It is possible to treat relations as normal events and aggregate them
clientside, but to do so comprehensively could be very resource intensive, as
the client would need to spider all possible events in a room to find
relationships and maintain a correct view.

Instead, this proposal seeks to solve this problem by defining APIs to let the
server calculate the aggregations on behalf of the client, and so bundle the
aggregated data with the original event where appropriate. It also proposes an
API to let clients paginate through all relations of an event.

This proposal is one in a series of proposals that defines a mechanism for
events to relate to each other. Together, these proposals replace
[MSC1849](https://github.com/matrix-org/matrix-doc/pull/1849).

* [MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674) defines a
standard shape for indicating events which relate to other events.
* This proposal defines APIs to let the server calculate the aggregations on
behalf of the client, and so bundle the aggregated data with the original
event where appropriate.
* [MSC2676](https://github.com/matrix-org/matrix-doc/pull/2676) defines how
users can edit messages using this mechanism.
* [MSC2677](https://github.com/matrix-org/matrix-doc/pull/2677) defines how
users can annotate events, such as reacting to events with emoji, using this
mechanism.

## Proposal

### Aggregations

richvdh marked this conversation as resolved.
Show resolved Hide resolved
Relation events can be aggregated per relation type by the server.
The format of the aggregated value (hereafter called "aggregation")
depends on the relation type.

Some relation types might group the aggregations by the `key` property
in the relation and aggregate to an array, while
others might aggregate to a single object or any other value really.

Here are some non-normative examples of what aggregations can look like:

Example aggregation for [`m.thread`](https://github.com/matrix-org/matrix-doc/pull/3440) (which
aggregates all relations into a single object):
```
{
"latest_event": {
"content": { ... },
...
},
"count": 7,
"current_user_participated": true
}
```

Example aggregation for [`m.annotation`](https://github.com/matrix-org/matrix-doc/pull/2677) (which
aggregates relations into a list of objects, grouped by `key`).
```
[
{
"key": "👍",
"origin_server_ts": 1562763768320,
"count": 3
},
{
"key": "👎",
"origin_server_ts": 1562763768320,
"count": 2
}
]
```

#### Bundling

Other than during non-gappy incremental syncs, timeline events that have other
events relate to them should include the aggregation of those related events
in the `m.relations` property of their unsigned data. This process is
referred to as "bundling", and the aggregated relations included via
this mechanism are called "bundled aggregations".
turt2live marked this conversation as resolved.
Show resolved Hide resolved

By sending a summary of the relations, bundling
avoids us having to always send lots of individual relation events
to the client.

Aggregations are never bundled into state events. This is a current
implementation detail that could be revisited later,
rather than a specific design decision.

The following client-server APIs should bundle aggregations
with events they return:
bwindels marked this conversation as resolved.
Show resolved Hide resolved

- `GET /rooms/{roomId}/messages`
- `GET /rooms/{roomId}/context/{eventId}`
- `GET /rooms/{roomId}/event/{eventId}`
- `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.
- `GET /relations`, as proposed in this MSC.

Deprecated APIs like `/initialSync` and `/events/{eventId}` are *not* required
to bundle aggregations.

The bundled aggregations are grouped according to their relation type.
The format of `m.relations` (here with *non-normative* examples of
the [`m.replace`](https://github.com/matrix-org/matrix-doc/pull/2676) and
[`m.annotation`](https://github.com/matrix-org/matrix-doc/pull/2677) relation
types) is as follows:

```json
{
"event_id": "abc",
"unsigned": {
"m.relations": {
"m.annotation": {
"key": "👍",
"origin_server_ts": 1562763768320,
"count": 3
},
"m.replace": {
"event_id": "$edit_event_id",
"origin_server_ts": 1562763768320,
"sender": "@alice:localhost"
},
}
}
}
```

#### Client-side aggregation

Bundled aggregations on an event give a snapshot of what relations were known
at the time the event was received. When relations are received through `/sync`,
clients should locally aggregate (as they might have done already before
supporting this MSC) the relation on top of any bundled aggregation the server
might have sent along previously with the target event, to get an up to date
view of the aggregations for the target event. The aggregation algorithm is the
same as the one described here for the server.

### Querying relations
bwindels marked this conversation as resolved.
Show resolved Hide resolved

A single event can have lots of associated relations, and we do not want to
overload the client by, for example, including them all bundled with the
related-to event. Instead, we also provide a new `/relations` API in
order to paginate over the relations, which behaves in a similar way to
`/messages`, except using `next_batch` and `prev_batch` names
(in line with `/sync` API). Tokens from `/sync` or `/messages` can be
passed to `/relations` to only get relating events from a section of
the timeline.

The `/relations` API returns the discrete relation events
associated with an event that the server is aware of
in standard topological order. Note that events may be missing,
see [limitations](#servers-might-not-be-aware-of-all-relations-of-an-event).
You can optionally filter by a given relation type and the event type of the
relating event:

```
GET /_matrix/client/v1/rooms/{roomID}/relations/{event_id}[/{rel_type}[/{event_type}]][?from=token][&to=token][&limit=amount]
```

```
{
"chunk": [
{
"type": "m.reaction",
"sender": "...",
"content": {
"m.relates_to": {
"rel_type": "m.annotation",
...
}
}
}
],
"prev_batch": "some_token",
"next_batch": "some_token",
}
```

The endpoint does not have any trailing slashes. It requires authentication
and is not rate-limited.

The `from` and `limit` query parameters are used for pagination, and work
just like described for the `/messages` endpoint.

Note that [MSC2676](https://github.com/matrix-org/matrix-doc/pull/2676)
adds the related-to event in `original_event` property of the response.
This way the full history (e.g. also the first, original event) of the event
is obtained without further requests. See that MSC for further details.

### End to end encryption
bwindels marked this conversation as resolved.
Show resolved Hide resolved

Since the server has to be able to aggregate relation events, structural
information about relations must be visible to the server, and so the
`m.relates_to` field must be included in the plaintext.

A future MSC may define a method for encrypting certain parts of the
`m.relates_to` field that may contain sensitive information.
bwindels marked this conversation as resolved.
Show resolved Hide resolved

### Redactions
bwindels marked this conversation as resolved.
Show resolved Hide resolved

Redacted relations should not be taken into consideration in
bundled aggregations, nor should they be returned from `/relations`.

Requesting `/relations` on a redacted event should
still return any existing relation events.
This is in line with other APIs like `/context` and `/messages`.

### Local echo

For the best possible user experience, clients should also include unsent
relations into the client-side aggregation. When adding a relation to the send
queue, clients should locally aggregate it into the relations of the target
event, ideally regardless of the target event having received an `event_id`
already or still being pending. If the client gives up on sending the relation
for some reason, the relation should be de-aggregated from the relations of
the target event. If the client offers the user a possibility of manually
retrying to send the relation, it should be re-aggregated when the user does so.

De-aggregating a relation refers to rerunning the aggregation for a given
target event while not considering the de-aggregated event any more.

Upon receiving the remote echo for any relations, a client is likely to remove
the pending event from the send queue. Here, it should also de-aggregate the
pending event from the target event's relations, and re-aggregate the received
remote event from `/sync` to make sure the client-side aggregation happens with
the same event data as on the server.

When adding a redaction for a relation to the send queue, the relation
referred to should be de-aggregated from the relations of the target of the
relation. Similar to a relation, when the sending of the redaction fails or
is cancelled, the relation should be aggregated again.

If the target event is still pending and hasn't received its `event_id` yet,
clients can locally relate relation events to their target by using
`transaction_id` like they already do for detecting remote echos when sending
events.

## Edge cases
bwindels marked this conversation as resolved.
Show resolved Hide resolved

### How do you handle ignored users?

* Information about relations sent from ignored users must never be sent to
the client, either in aggregations or discrete relation events.
This is to let you block someone from harassing you with emoji reactions
(or using edits as a side-channel to harass you). Therefore, it is possible
that different users will see different aggregations (a different last edit,
or a different reaction count) on an event.

## Limitations

### Relations can be missed while not being in the room

Relation events behave no different from other events in terms of room history visibility,
which means that some relations might not be visible to a user while they are not invited
or have not joined the room. This can cause a user to see an incomplete edit history or reaction count
based on discrete relation events upon (re)joining a room.

Ideally the server would not include these events in aggregations, as it would mean breaking
the room history visibility rules, but this MSC defers addressing this limitation and
specifying the exact server behaviour to [MSC3570](https://github.com/matrix-org/matrix-doc/pull/3570).

### Servers might not be aware of all relations of an event

The response of `/relations` might be incomplete because the homeserver
potentially doesn't have the full DAG of the room. The federation API doens't
have an equivalent of the `/relations` API, so has no way but to fetch the
full DAG over federation to assure itself that it is aware of all relations.

[MSC2836](https://github.com/matrix-org/matrix-doc/pull/2836) provided a proposal for following relationships over federation in the "Querying relationships over federation" section via a `/_matrix/federation/v1/event_relationships` API

### Event type based aggregation and filtering won't work well in encrypted rooms

The `/relations` endpoint allows filtering by event type,
which for encrypted rooms will be `m.room.encrypted`, rendering this filtering
less useful for encrypted rooms. Aggregation algorithms that take the type of
the relating events they aggregate into account will suffer from the same
limitation.

## Future extensions

### Handling limited (gappy) syncs

For the special case of a gappy incremental sync, many relations (particularly
reactions) may have occurred during the gap. It would be inefficient to send
each one individually to the client, but it would also be inefficient to send
all possible bundled aggregations to the client.

The server could tell the client the event IDs of events which
predate the gap which received relations during the gap. This means that the
client could invalidate its copy of those events (if any) and then requery them
(including their bundled relations) from the server if/when needed,
for example using an extension of the `/event` API for batch requests.

The server could do this with a new `stale_events` field of each room object
in the sync response. The `stale_events` field would list all the event IDs
prior to the gap which had updated relations during the gap. The event IDs
would be grouped by relation type,
and paginated as per the normal Matrix pagination model.

This was originally part of this MSC but left out to limit the scope
to what is implemented at the time of writing.
bwindels marked this conversation as resolved.
Show resolved Hide resolved

## Prefix

While this MSC is not considered stable, the endpoints become:

- `GET /_matrix/client/unstable/rooms/{roomID}/relations/{eventID}[/{relationType}[/{eventType}]]`

None of the newly introduced identifiers should use a prefix though, as this MSC
tries to document relation support already being used in
the wider matrix ecosystem.