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

Spec lazy-loading room members #2035

Merged
merged 19 commits into from
Jun 11, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
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
13 changes: 13 additions & 0 deletions api/client-server/definitions/room_event_filter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ allOf:
- type: object
title: RoomEventFilter
properties:
lazy_load_members:
type: boolean
description: |-
If ``true``, enables lazy-loading of membership events. See
`Lazy-loading room members <#lazy-loading-room-members>`_
for more information. Defaults to ``false``.
include_redundant_members:
type: boolean
description: |-
If ``true``, enables redudant membership events. Does not
turt2live marked this conversation as resolved.
Show resolved Hide resolved
apply unless ``lazy_load_members`` is ``true``. See
`Lazy-loading room members <#lazy-loading-room-members>`_
for more information. Defaults to ``false``.
not_rooms:
description: A list of room IDs to exclude. If this list is absent then no rooms
are excluded. A matching room will be excluded even if it is listed in the ``'rooms'``
Expand Down
29 changes: 1 addition & 28 deletions api/client-server/definitions/sync_filter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ properties:
not_rooms:
description: A list of room IDs to exclude. If this list is absent then no rooms
are excluded. A matching room will be excluded even if it is listed in the ``'rooms'``
filter. This filter is applied before the filters in ``ephemeral``,
filter. This filter is applied before the filters in ``ephemeral``,
``state``, ``timeline`` or ``account_data``
items:
type: string
Expand All @@ -73,33 +73,6 @@ properties:
allOf:
- $ref: room_event_filter.yaml
description: The state events to include for rooms.
properties:
lazy_load_members:
type: boolean
description: |-
If ``true``, the only ``m.room.member`` events returned in
the ``state`` section of the ``/sync`` response are those
which are definitely necessary for a client to display
the ``sender`` of the timeline events in that response.
If ``false``, ``m.room.member`` events are not filtered.
By default, servers should suppress duplicate redundant
lazy-loaded ``m.room.member`` events from being sent to a given
client across multiple calls to ``/sync``, given that most clients
cache membership events (see ``include_redundant_members``
to change this behaviour).
include_redundant_members:
type: boolean
description: |-
If ``true``, the ``state`` section of the ``/sync`` response will
always contain the ``m.room.member`` events required to display
the ``sender`` of the timeline events in that response, assuming
``lazy_load_members`` is enabled. This means that redundant
duplicate member events may be returned across multiple calls to
``/sync``. This is useful for naive clients who never track
membership data. If ``false``, duplicate ``m.room.member`` events
may be suppressed by the server across multiple calls to ``/sync``.
If ``lazy_load_members`` is ``false`` this field is ignored.

timeline:
allOf:
- $ref: room_event_filter.yaml
Expand Down
3 changes: 3 additions & 0 deletions api/client-server/event_context.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ paths:
This API returns a number of events that happened just before and
after the specified event. This allows clients to get the context
surrounding an event.

*Note*: This endpoint supports lazy-loading. See `Filtering <#filtering>`_
turt2live marked this conversation as resolved.
Show resolved Hide resolved
for more information.
operationId: getEventContext
security:
- accessToken: []
Expand Down
20 changes: 20 additions & 0 deletions api/client-server/message_pagination.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ paths:
description: |-
This API returns a list of message and state events for a room. It uses
pagination query parameters to paginate history in the room.

*Note*: This endpoint supports lazy-loading. See `Filtering <#filtering>`_
turt2live marked this conversation as resolved.
Show resolved Hide resolved
for more information.
operationId: getRoomEvents
security:
- accessToken: []
Expand Down Expand Up @@ -108,6 +111,23 @@ paths:
type: object
title: RoomEvent
"$ref": "definitions/event-schemas/schema/core-event-schema/room_event.yaml"
state:
type: array
description: |-
A list of state events relevant to showing the ``chunk``. For example, if
lazy-loading members is enabled in the filter then this will contain any
turt2live marked this conversation as resolved.
Show resolved Hide resolved
applicable membership events. Servers should be careful to not exclude
turt2live marked this conversation as resolved.
Show resolved Hide resolved
membership events which are older than ones already sent to the client.
turt2live marked this conversation as resolved.
Show resolved Hide resolved
Likewise, clients should be cautious and avoid using older membership
events as the current membership event when paginating backwards.

Unless ``include_redundant_members`` is ``true``, the server should remove
turt2live marked this conversation as resolved.
Show resolved Hide resolved
redundant members which would have already been sent to clients in prior calls
turt2live marked this conversation as resolved.
Show resolved Hide resolved
to lazy-loading aware endpoints with the same filter.
items:
type: object
title: RoomStateEvent
$ref: "definitions/event-schemas/schema/core-event-schema/state_event.yaml"
examples:
application/json: {
"start": "t47429-4392820_219380_26003_2265",
Expand Down
38 changes: 38 additions & 0 deletions api/client-server/rooms.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,44 @@ paths:
description: The room to get the member events for.
required: true
x-example: "!636q39766251:example.com"
- in: query
name: at
type: string
description: |-
The point in time (pagination token) to return members for in the room.
This token can be obtained from a ``prev_batch`` token returned for
each room by the sync API. Defaults to the current state of the room,
as determined by the server.
x-example: "YWxsCgpOb25lLDM1ODcwOA"
# XXX: As mentioned in MSC1227, replacing `[not_]membership` with a JSON
# filter might be a better alternative.
# See https://github.com/matrix-org/matrix-doc/issues/1337
- in: query
name: membership
type: string
enum:
richvdh marked this conversation as resolved.
Show resolved Hide resolved
- join
- invite
- leave
- ban
description: |-
The kind of membership to filter for. Defaults to no filtering if
unspecified. When specified alongside ``not_membership``, the two
parameters create an 'or' condition: either the membership *is*
the same as ``membership`` **or** *is not* the same as ``not_membership``.
x-example: "join"
- in: query
name: not_membership
type: string
enum:
- join
- invite
- leave
- ban
description: |-
The kind of membership to exclude from the results. Defaults to no
filtering if unspecified.
x-example: leave
security:
- accessToken: []
responses:
Expand Down
64 changes: 64 additions & 0 deletions api/client-server/sync.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ paths:
Clients use this API when they first log in to get an initial snapshot
of the state on the server, and then continue to call this API to get
incremental deltas to the state, and to receive new messages.

*Note*: This endpoint supports lazy-loading. See `Filtering <#filtering>`_
for more information. Lazy-loading members is only supported on a ``StateFilter``
for this endpoint. When lazy-loading is enabled, servers MUST include the
syncing user's own membership event when they join a room, or when the
full state of rooms is requested. The user's own membership event is eligible
for being considered redudant by the server. When a sync is ``limited``,
turt2live marked this conversation as resolved.
Show resolved Hide resolved
the server MUST return membership events for the timeline, even if the
turt2live marked this conversation as resolved.
Show resolved Hide resolved
applicable events are not in the response, regardless as to whether or not
they are redundant.
turt2live marked this conversation as resolved.
Show resolved Hide resolved
operationId: sync
security:
- accessToken: []
Expand All @@ -49,6 +59,8 @@ paths:
requests. Creating a filter using the filter API is recommended for
clients that reuse the same filter multiple times, for example in
long poll requests.

See `Filtering <#filtering>`_ for more information.
x-example: "66696p746572"
- in: query
name: since
Expand Down Expand Up @@ -125,6 +137,50 @@ paths:
title: Joined Room
type: object
properties:
summary:
title: RoomSummary
type: object
description: |-
Information about the room which clients may need to
correctly render it to users.
properties:
"m.heroes":
Copy link
Member

Choose a reason for hiding this comment

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

are these properties not required ?

Copy link
Member Author

Choose a reason for hiding this comment

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

no, because if they are omitted then the client should interpret it as "no change".

type: array
description: |-
The users which can be used to generate a room name
if the room does not have one. Required if the room
does not have a ``m.room.name`` or ``m.room.canonical_alias``
state event with non-empty content.
turt2live marked this conversation as resolved.
Show resolved Hide resolved

This should be the first 5 members of the room, ordered
by stream ordering, which are joined or invited. The
list must never include the client's own user ID. When
no joined or invited members are available, this should
consist of the banned and left users. More than 5 members
may be provided, however less than 5 should only be provided
when there are less than 5 members to represent.

When lazy-loading room members is enabled, the membership
events for the heroes MUST be included in the ``state``,
unless they are redundant. When the list of users changes,
the server notifies the client by sending a fresh list of
heroes. If there are no changes since the last sync, this
field may be omitted.
items:
type: string
"m.joined_member_count":
type: integer
description: |-
The number of users with ``membership`` of ``join``,
including the client's own user ID. If this field has
not changed since the last sync, it may be omitted.
Required otherwise.
"m.invited_member_count":
type: integer
description: |-
The number of users with ``membership`` of ``invite``.
If this field has not changed since the last sync, it
may be omitted. Required otherwise.
state:
title: State
type: object
Expand Down Expand Up @@ -330,6 +386,14 @@ paths:
"rooms": {
"join": {
"!726s6s6q:example.com": {
"summary": {
"m.heroes": [
"@alice:example.com",
"@bob:example.com"
],
"m.joined_member_count": 2,
"m.invited_member_count": 0
},
"state": {
"events": [
{
Expand Down
1 change: 1 addition & 0 deletions changelogs/client_server/newsfragments/2035.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the option to lazy-load room members for increased client performance.
38 changes: 38 additions & 0 deletions specification/client_server_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1268,6 +1268,44 @@ Filters can be created on the server and can be passed as as a parameter to APIs
which return events. These filters alter the data returned from those APIs.
Not all APIs accept filters.

Lazy-loading room members
turt2live marked this conversation as resolved.
Show resolved Hide resolved
~~~~~~~~~~~~~~~~~~~~~~~~~

Membership events often take significant resources for clients to track. In an
effort to reduce the number of resources used, clients can enable "lazy-loading"
for room members. By doing this, servers will only ever send membership events
which are relevant to the client.

In terms of filters, this means enabling ``lazy_load_members`` on a ``StateFilter``
or ``RoomEventFilter``. When enabled, lazy-loading aware endpoints (see below)
turt2live marked this conversation as resolved.
Show resolved Hide resolved
turt2live marked this conversation as resolved.
Show resolved Hide resolved
will only include membership events for the ``sender`` of events being included
in the response. For example, if a client makes a ``/sync`` request with lazy-loading
enabled, the server will only return membership events for the ``sender`` of events
in the timeline, not all members of a room.

Repeated calls to lazy-loading aware endpoints will result in redundant membership
turt2live marked this conversation as resolved.
Show resolved Hide resolved
events being excluded by default. Clients often track which membership events they
already have, therefore making the extra information not as useful to the client.
Clients can always request redundant members by setting ``include_redundant_members``
turt2live marked this conversation as resolved.
Show resolved Hide resolved
to true in the filter.

Servers should be cautious about which events they consider redundant. Membership
events can change over time, and should be included as relevant to maintain the
historical record. Likewise, clients should be cautious about treating an older event
as the current membership event for a user.

.. Note::
Repeated calls using the same filter to *any* lazy-loading aware endpoint may
result in redundant members being excluded from future calls. For example, a
turt2live marked this conversation as resolved.
Show resolved Hide resolved
request to ``/sync`` followed by a request to ``/messages`` may result in a
future call to ``/sync`` excluding members included by the ``/messages`` call.
turt2live marked this conversation as resolved.
Show resolved Hide resolved

The current endpoints which support lazy-loading room members are:

* |/sync|_
* |/rooms/<room_id>/messages|_
* |/rooms/{roomId}/context/{eventId}|_

{{filter_cs_http_api}}

Events
Expand Down
98 changes: 35 additions & 63 deletions specification/modules/instant_messaging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -278,70 +278,42 @@ choose a name:
#. If the room has an `m.room.canonical_alias`_ state event with a non-empty
``alias`` field, use the alias given by that field as the name.

#. If neither of the above conditions are met, a name should be composed based
on the members of the room. Clients should consider `m.room.member`_ events
for users other than the logged-in user, with ``membership: join`` or
``membership: invite``.

.. _active_members:

i. If there is only one such event, the display name for the room should be
the `disambiguated display name`_ of the corresponding user.

#. If there are two such events, they should be lexicographically sorted by
their ``state_key`` (i.e. the corresponding user IDs), and the display
name for the room should be the `disambiguated display name`_ of both
users: "<user1> and <user2>", or a localised variant thereof.

#. If there are three or more such events, the display name for the room
should be based on the disambiguated display name of the user
corresponding to the first such event, under a lexicographical sorting
according to their ``state_key``. The display name should be in the
format "<user1> and <N> others" (or a localised variant thereof), where N
is the number of `m.room.member`_ events with ``membership: join`` or
``membership: invite``, excluding the logged-in user and "user1".

For example, if Alice joins a room, where Bob (whose user id is
``@superuser:example.com``), Carol (user id ``@carol:example.com``) and
Dan (user id ``@dan:matrix.org``) are in conversation, Alice's
client should show the room name as "Carol and 2 others".

.. TODO-spec
Sorting by user_id certainly isn't ideal, as IDs at the start of the
alphabet will end up dominating room names: they will all be called
"Arathorn and 15 others". Furthermore - user_ids are not necessarily
ASCII, which means we need to either specify a collation order, or specify
how to choose one.

Ideally we might sort by the time when the user was first invited to, or
first joined the room. But we don't have this information.

See https://matrix.org/jira/browse/SPEC-267 for further discussion.

#. If the room has no valid ``m.room.name`` or ``m.room.canonical_alias``
event, and no active members other than the current user, clients should
consider ``m.room.member`` events with ``membership: leave``. If such events
exist, a display name such as "Empty room (was <user1> and <N> others)" (or
a localised variant thereof) should be used, following similar rules as for
active members (see `above <active_members_>`_).

#. A complete absence of room name, canonical alias, and room members is likely
to indicate a problem with creating the room or synchronising the state
table; however clients should still handle this situation. A display name
such as "Empty room" (or a localised variant thereof) should be used in this
situation.

.. _`disambiguated display name`: `Calculating the display name for a user`_

Clients SHOULD NOT use `m.room.aliases`_ events as a source for room names, as
it is difficult for clients to agree on the best alias to use, and aliases can
change unexpectedly.

.. TODO-spec
How can we make this less painful for clients to implement, without forcing
an English-language implementation on them all? See
https://matrix.org/jira/browse/SPEC-425.
#. If neither of the above conditions are met, the client can optionally guess
an alias from the ``m.room.alias`` events in the room. This is a temporary
measure while clients do not promote canonical aliases as prominently. This
step may be removed in a future version of the specification.

#. If none of the above conditions are met, a name should be composed based
on the members of the room. Clients should consider `m.room.member`_ events
for users other than the logged-in user, as defined below.

i. If the ``m.heroes`` for the room are greater or equal to
turt2live marked this conversation as resolved.
Show resolved Hide resolved
``m.joined_member_count + m.invited_member_count - 1``, then use the
membership events for the heroes to calculate display names for the
users (`disambiguating them if required`_) and concatenating them. For
example, the client may choose to show "Alice, Bob, and Charlie
(@charlie:example.org)" as the room name. The client may optionally
limit the number
turt2live marked this conversation as resolved.
Show resolved Hide resolved

#. If there are fewer heroes than ``m.joined_member_count + m.invited_member_count
- 1``, and ``m.joined_member_count + m.invited_member_count`` is greater
than 1, the client should use the heroes to calculate display names for
the users (`disambiguating them if required`_) and concatenating them
alongside a count of the remaining users. For example, "Alice, Bob, and
1234 others".

#. If ``m.joined_member_count + m.invited_member_count`` is less than or
equal to 1 (indicating the member is alone), the client should use the
rules above to indicate that the room was empty. For example, "Empty
Room (was Alice)", "Empty Room (was Alice and 1234 others)", or
"Empty Room" if there are no heroes.

Clients SHOULD internationalise the room name to the user's language when using
the ``m.heroes`` to calculate the name. Clients SHOULD use minimum 5 heroes to
calculate room names where possible, but may use more or less to fit better with
their user experience.

.. _`disambiguating them if required`: `Calculating the display name for a user`_

Forming relationships between events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down