Merge pull request #2035 from matrix-org/travis/1.0/msc688-msc1227-lazy-loading
Spec lazy-loading room members
This commit is contained in:
commit
19f017f9bd
9 changed files with 233 additions and 86 deletions
|
@ -16,6 +16,20 @@ 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``, sends all membership events for all events, even if they have already
|
||||
been sent to the client. Does not
|
||||
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'``
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 of room member events. See `Filtering <#lazy-loading-room-members>`_
|
||||
for more information.
|
||||
operationId: getEventContext
|
||||
security:
|
||||
- accessToken: []
|
||||
|
|
|
@ -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 of room member events. See `Filtering <#lazy-loading-room-members>`_
|
||||
for more information.
|
||||
operationId: getRoomEvents
|
||||
security:
|
||||
- accessToken: []
|
||||
|
@ -111,6 +114,21 @@ 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_load_members`` is enabled in the filter then this may contain
|
||||
the membership events for the senders of events in the ``chunk``.
|
||||
|
||||
Unless ``include_redundant_members`` is ``true``, the server
|
||||
may remove membership events which would have already been
|
||||
sent to the client in prior calls to this endpoint, assuming
|
||||
the membership of those members has not changed.
|
||||
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",
|
||||
|
|
|
@ -184,6 +184,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:
|
||||
- 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:
|
||||
|
|
|
@ -34,6 +34,20 @@ 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, to aid discovering the user's avatar &
|
||||
displayname.
|
||||
|
||||
Like other members, the user's own membership event is eligible
|
||||
for being considered redundant by the server. When a sync is ``limited``,
|
||||
the server MUST return membership events for events in the gap
|
||||
(between ``since`` and the start of the returned timeline), regardless
|
||||
as to whether or not they are redundant. This ensures that joins/leaves
|
||||
and profile changes which occur during the gap are not lost.
|
||||
operationId: sync
|
||||
security:
|
||||
- accessToken: []
|
||||
|
@ -49,6 +63,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
|
||||
|
@ -125,6 +141,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":
|
||||
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's
|
||||
``m.room.name`` or ``m.room.canonical_alias`` state events
|
||||
are unset or empty.
|
||||
|
||||
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
|
||||
|
@ -305,6 +365,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": [
|
||||
{
|
||||
|
|
1
changelogs/client_server/newsfragments/2035.feature
Normal file
1
changelogs/client_server/newsfragments/2035.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Add the option to lazy-load room members for increased client performance.
|
|
@ -1317,6 +1317,66 @@ 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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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 attempt to only send membership events
|
||||
which are relevant to the client.
|
||||
|
||||
It is important to understand that lazy-loading is not intended to be a
|
||||
perfect optimisation, and that it may not be practical for the server to
|
||||
calculate precisely which membership events are relevant to the client. As a
|
||||
result, it is valid for the server to send redundant membership events to the
|
||||
client to ease implementation, although such redundancy should be minimised
|
||||
where possible to conserve bandwidth.
|
||||
|
||||
In terms of filters, lazy-loading is enabled by enabling ``lazy_load_members``
|
||||
on a ``RoomEventFilter`` (or a ``StateFilter`` in the case of ``/sync`` only).
|
||||
When enabled, lazy-loading aware endpoints (see below) 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.
|
||||
|
||||
When processing a sequence of events (e.g. by looping on ``/sync`` or
|
||||
paginating ``/messages``), it is common for blocks of events in the sequence
|
||||
to share a similar set of senders. Rather than responses in the sequence
|
||||
sending duplicate membership events for these senders to the client, the
|
||||
server MAY assume that clients will remember membership events they have
|
||||
already been sent, and choose to skip sending membership events for members
|
||||
whose membership has not changed. These are called 'redundant membership
|
||||
events'. Clients may request that redundant membership events are always
|
||||
included in responses by setting ``include_redundant_members`` to true in the
|
||||
filter.
|
||||
|
||||
The expected pattern for using lazy-loading is currently:
|
||||
|
||||
* Client performs an initial /sync with lazy-loading enabled, and receives
|
||||
only the membership events which relate to the senders of the events it
|
||||
receives.
|
||||
* Clients which support display-name tab-completion or other operations which
|
||||
require rapid access to all members in a room should call /members for the
|
||||
currently selected room, with an ``?at`` parameter set to the /sync
|
||||
response's from token. The member list for the room is then maintained by
|
||||
the state in subsequent incremental /sync responses.
|
||||
* Clients which do not support tab-completion may instead pull in profiles for
|
||||
arbitrary users (e.g. read receipts, typing notifications) on demand by
|
||||
querying the room state or ``/profile``.
|
||||
|
||||
.. TODO-spec
|
||||
This implies that GET /state should also take an ``?at`` param
|
||||
|
||||
The current endpoints which support lazy-loading room members are:
|
||||
|
||||
* |/sync|_
|
||||
* |/rooms/<room_id>/messages|_
|
||||
* |/rooms/{roomId}/context/{eventId}|_
|
||||
|
||||
API endpoints
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
{{filter_cs_http_api}}
|
||||
|
||||
Events
|
||||
|
|
|
@ -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
|
||||
#. 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, with ``membership: join`` or
|
||||
``membership: invite``.
|
||||
for users other than the logged-in user, as defined below.
|
||||
|
||||
.. _active_members:
|
||||
i. If the number of ``m.heroes`` for the room are greater or equal to
|
||||
``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 of users it uses to generate a room name.
|
||||
|
||||
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 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 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 ``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.
|
||||
|
||||
#. 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.
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue