MSC4140: Cancellable delayed events - #4140
Conversation
Signed-off-by: Timo K <toger5@hotmail.de>
Signed-off-by: Timo K <toger5@hotmail.de>
2bc07c4 to
0eb1abc
Compare
Signed-off-by: Timo K <toger5@hotmail.de>
0eb1abc to
8bf6db7
Compare
Signed-off-by: Timo K <toger5@hotmail.de>
Signed-off-by: Timo K <toger5@hotmail.de>
3e54c2a to
c82adf7
Compare
Signed-off-by: Timo K <toger5@hotmail.de>
c82adf7 to
54fff99
Compare
…is used to trigger on of the actions Signed-off-by: Timo K <toger5@hotmail.de>
Signed-off-by: Timo K <toger5@hotmail.de>
Add event type to the body Add event id template variable
Co-authored-by: Andrew Ferrazzutti <af_0_af@hotmail.com>
| If a requested delay exceeds this maximum, the homeserver will respond with HTTP 400 | ||
| and a [standard error response](https://spec.matrix.org/v1.18/client-server-api/#standard-error-response) | ||
| with an `errcode` of `M_INVALID_PARAM`. |
There was a problem hiding this comment.
To go even further with #4140 (comment), how about returning M_FORBIDDEN & HTTP 403 even for this case of requesting a delay longer than allowed?
This would prevent needing a special case error response for delayed events being entirely disallowed, as both that case & this one would then both give the same response.
It also looks like the spec uses M_INVALID_PARAM only for parameter values that are always invalid (like a malformed room alias or MXID), as opposed to values that are disallowed by server config that may change later.
There was a problem hiding this comment.
Erm, actually, the special case would still be needed for when the maximum per-user amount of delayed events is 0, lest the response would be M_LIMIT_EXCEEDED & HTTP 429 for a limit that can never be satisfied.
But the point stands about M_FORBIDDEN & HTTP 403 potentially being more appropriate than M_INVALID_PARAM / HTTP 400.
There was a problem hiding this comment.
But the point stands about
M_FORBIDDEN& HTTP 403 potentially being more appropriate thanM_INVALID_PARAM/ HTTP 400.
This sounds sensible to me.
There was a problem hiding this comment.
M_FORBIDDEN for a parameter exceeding a limit sounds weird, maybe it should just be a new dedicated error code instead?
There was a problem hiding this comment.
A dedicated error code was the original plan, but was decided against after opting to express the max-allowed delay in a capability: #4140 (comment), #4140 (comment).
There is also precedent of the spec using M_FORBIDDEN for disallowed parameter values, like an invalid password or login token for the user-interactive login API, and an unsupported media type for the media upload endpoint.
There was a problem hiding this comment.
Personally I'd err towards adding new error codes even if they're not technically necessary, rather than reusing existing codes in a potentially confusing way. I don't feel too strongly about that though, so either way is fine
an unsupported media type for the media upload endpoint
not related to this MSC, but ???? HTTP 415 exists
Also reword the special case error response now that only the 0-limit case is special
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Move delegation to an alternative. It is planned to be given its own MSC.
|
This looks ready for broader SCT review, even with designated review not being fully complete. SCT members are encouraged to review this MSC. |
| There is no guarantee for a client that events will be available | ||
| if they exceed the limits of their homeserver. | ||
| Additionally, a homeserver MAY discard finalised delayed events that have been returned by a | ||
| `GET /_matrix/client/v1/delayed_events/{delay_id}` response. |
There was a problem hiding this comment.
This sounds kind of dangerous from a request retrying perspective since network errors can cause responses to be lost. I guess storing finalized events is best-effort anyway and clients shouldn't rely on any of them being stored?
There was a problem hiding this comment.
I guess storing finalized events is best-effort anyway and clients shouldn't rely on any of them being stored?
Correct.
Eventually, finalized delayed events should appear in /sync responses (and appservice transactions), which will reliably communicate them.
| If a requested delay exceeds this maximum, the homeserver will respond with HTTP 400 | ||
| and a [standard error response](https://spec.matrix.org/v1.18/client-server-api/#standard-error-response) | ||
| with an `errcode` of `M_INVALID_PARAM`. |
There was a problem hiding this comment.
M_FORBIDDEN for a parameter exceeding a limit sounds weird, maybe it should just be a new dedicated error code instead?
| ## Alternatives | ||
|
|
There was a problem hiding this comment.
The opening paragraph has multiple use cases, but there are also various limits that could interfere with clients actually using this for all those use cases. For example, if a server sets a low pending delayed event limit, then a client uses delayed events for self-destructing messages, would calls just fail because the user's limit is used up?
The 429 behavior is also mildly scary if clients aren't smart enough to ignore too long Retry-After fields (like if all the delayed events are for scheduled or self-destructing messages in 3 months, starting a call might just get stuck in a sleep()). That's mostly a client implementation concern though, the MSC is probably fine as is.
I'm not sure if it should be fixed in this MSC or just documented as a possible alternative/future MSC. The fix would probably be having the server tell the client what use case it's targeting so the server can have per-use-case limits
There was a problem hiding this comment.
For example, if a server sets a low pending delayed event limit, then a client uses delayed events for self-destructing messages, would calls just fail because the user's limit is used up?
The 429 behavior is also mildly scary if clients aren't smart enough to ignore too long Retry-After fields (like if all the delayed events are for scheduled or self-destructing messages in 3 months, starting a call might just get stuck in a sleep()).
I suppose in cases like that, it's the client's responsibility to be proactive in checking server capabilities to verify that a desired usecase is feasible. Then all that'd be required from this MSC is to ensure that enough capabilities are exposed for such checking, which IMO they already are.
|
|
||
| The homeserver MUST NOT send the event before the scheduled time. | ||
| To support batch sending, homeservers MAY add up to 30 seconds to the scheduled send time. | ||
| Note: clients might find that their events are delayed further due to server load and similar conditions. |
There was a problem hiding this comment.
Here might be a good place to also mention that the same rate limits will apply as if the client sent the events in the standard way at the time.
| If a delayed event fails to be sent at its scheduled send time due to a rate limit failure, | ||
| the homeserver SHOULD NOT retry sending the event. Instead, the event will be stored as a finalised delayed event | ||
| with its `error` field set, available to be retrieved by a client for the user who requested the event. | ||
| It then becomes the user's responsibility to fetch this error and retry sending the event as appropriate. |
There was a problem hiding this comment.
This seems odd? Why? I think I would expect it to be retried, otherwise this makes the mechanism a chunk less reliable if it might also fail permanently due to transient rate limiting.
| ### Inability to filter and paginate delayed events | ||
|
|
||
| `GET /_matrix/client/v1/delayed_events` lacks request parameters for filtering and pagination. It also | ||
| doesn't allow querying finalised delayed events. This could be limiting in some cases. A future proposal |
There was a problem hiding this comment.
It also doesn't allow querying finalised delayed events
Could you clarify this sentence - I'm not sure exactly what it's saying (unless it's fetching only finalised events, but isn't that the same as filtering?)
| All delayed event related endpoints are available to guest accounts. | ||
| This allows guest accounts to participate in MatrixRTC sessions. | ||
|
|
||
| ## Potential issues |
There was a problem hiding this comment.
A fairly significant potential issue in using this for VoIP hangups is that a client now needs to be able to make regular, timely HTTP requests to not get a call hung up. I frequently make calls in network conditions that are fine for getting UDP voice packets through but don't work for making a small HTTP request.
There was a problem hiding this comment.
that's the reason why there is delegation of delayed events. In the VoIP example the media server is taking over control.
There was a problem hiding this comment.
Oh right, of course - I think I was generally failing to understand that this also acts as a way for a client to effectively hand the SFU a signed and sealed envelope with a hangup event that it can send (or trigger to be sent) later. Could add a thing to clarify this maybe.
There was a problem hiding this comment.
However, as it was decided to move the LiveKit Auth service into the homeserver, the initial idea of "delegation of delayed events" was cut down to be homeserver internal (e.g., app services). It's subject to a new MSC once there is demand to delegate to an external service.
Due to batch sending, scheduled send times are allowed to be delayed somewhat, making delayed events unreliable for timer usecases.
i.e. the MUST is not just one way around
| The body for requests to this endpoint is a JSON object containing the following fields: | ||
|
|
||
| - `delay_ms` - Required. A positive non-zero number of milliseconds the homeserver should wait before sending the event. | ||
| - `state_key` - The state key for the event to be sent, if it is to be a state event; absent otherwise. | ||
| - `content` - Required. The content of the event to be sent. |
There was a problem hiding this comment.
Having a very different body than /send but the same path is very confusing.
It seems like it would be better to mirror the existing /send and /state endpoints (or not even bother with /state if it isn't needed yet).
There was a problem hiding this comment.
The MSC isn't using the same path? That was an older revision, but it was replaced with a dedicated /delayed_events path
There was a problem hiding this comment.
The MSC isn't using the same path?
Yes, but it purposefully mirrors the path, I think it'll be quite confusing to have completely different API shapes.
There was a problem hiding this comment.
It's fine IMHO.
I guess if you wanted to mirror the existing endpoints, you'd have PUT /_matrix/client/v3/rooms/{roomId}/delayed_send/{eventType}/{txnId} and PUT /_matrix/client/v3/rooms/{roomId}/delayed_state/{eventType}/{stateKey}. I woudn't be dead against that, but I'm unconvinced it's worth changing.
There was a problem hiding this comment.
Another point against having split delayed send/state endpoints is that the semantics of sending a delayed event are the same for both message & state events, unlike for non-delayed events where /send uses a transaction ID to distinguish requests while /state uses the state key.
| generated by the homeserver. | ||
|
|
||
| The homeserver MUST NOT send the event before the scheduled time. | ||
| To support batch sending, homeservers MAY add up to 30 seconds to the scheduled send time. |
There was a problem hiding this comment.
Why does this need to batch sending? It seems very at odds with the feature.
There was a problem hiding this comment.
If the user (or different users) schedule multiple events at timestamps close to each other, the homeserver may want to batch the sends together for efficiency reasons
There was a problem hiding this comment.
OK, that seems like an implementation detail though. I think what this is trying to say is "don't expect your event to get send at exactly the millisecond it is scheduled to, but I think that's kind of obvious?
The homeserver MUST NOT send the event before the scheduled time, but
MAY send it shortly after the scheduled send time.
| If a user's request to schedule a delayed event would exceed this limit, the homeserver will respond with HTTP 429, | ||
| a [standard error response](https://spec.matrix.org/v1.18/client-server-api/#standard-error-response) | ||
| with an `errcode` of `M_LIMIT_EXCEEDED`, and a `Retry-After` header whose value is set to the time of/until | ||
| the scheduled send time of the next of the user's delayed events to be sent, | ||
| rounded up to the nearest second. |
There was a problem hiding this comment.
M_USER_LIMIT_EXCEEDED seems to the the standard error code?
This should be a 400.
There was a problem hiding this comment.
I think M_USER_LIMIT_EXCEEDED was introduced to deal with premium accounts; the general implication is that it's a quota you can pay to increase. Accordingly, M_LIMIT_EXCEEDED is probably appropriate here. And normally we would use a 429.
There was a problem hiding this comment.
M_USER_LIMIT_EXCEEDED was considered for this, but that is meant for quota limits that can be bypassed (such as with a "premium" subscription / payment-gated feature), whereas this is a server-imposed limit for the sake of appropriate resource allocation (akin to something like a maximum file upload size limit).
See: #4140 (comment)
| The `delay_id` of a sent delayed event MUST be included in the resulting room event's `unsigned` data | ||
| if, and only if, the client being given the event (e.g. over `/sync` or `/messages`) is authenticated as the event's sender. | ||
|
|
||
| #### Power levels are evaluated at the point of sending |
There was a problem hiding this comment.
What about all the other ways events can fail validation?
There was a problem hiding this comment.
Yes, this part should call out how malformed events are handled.
Synapse validates the event at the time of scheduling, as opposed to at the scheduled send time, because unlike power levels, event validation rules are unlikely to change over time. So IMO this section should at enforce at least a schedule-time validation check.
| need to account for eventual consistency anyway, which may appear as a delayed event or | ||
| attached to a disjointed part of the DAG. | ||
|
|
||
| ### Conflicting delayed state events |
There was a problem hiding this comment.
Do we even need this feature for state events today? This seems like a big foot gun without a use case.
There was a problem hiding this comment.
There is indeed no longer a planned usecase for delayed state events, or at least one that relies on any particular behaviour of them. f0ad189 removes this section.
| events due to federation delay. Potentially addressing this situation is, therefore, left to a future | ||
| proposal. | ||
|
|
||
| ### Inability to filter and paginate delayed events |
There was a problem hiding this comment.
Leaving pagination to a future proposal is not acceptable IMO.
@mscbot concern List endpoint is unbounded and needs pagination.
There was a problem hiding this comment.
The easiest solution to this may be to get rid of the list endpoint, if we don't have a usecase for it?
There was a problem hiding this comment.
In any case, I'm not sure I agree. This MSC is big enough as it is, and pagination was broken out to MSC4486 deliberately. Let's just land this MSC without adding more clutter.
There was a problem hiding this comment.
Agreed this is lengthy.
Moving the entire list endpoint to a separate MSC that provides all the needed filtering/pagination probably makes the most sense.
But also if there's no use-case, why include it?
There was a problem hiding this comment.
The use-case is to give clients the ability to check if any delayed events failed to be sent.
Though admittedly, if a client really cares about the status of any particular delayed event, it would very likely have its delay_id on hand & use the single-item lookup to find it.
Still, it feels incomplete to not be able to simply list all available delayed events, especially now that it's contained in its own MSC.
| [^eventId]: An event's ID is computed from its [reference hash](https://spec.matrix.org/v1.18/rooms/v11/#event-ids) | ||
| which is obtained by combining several event properties including `origin_server_ts`. The latter is | ||
| only available once the event has actually been sent, however. Since scheduled delayed events may be | ||
| cancelled or re-scheduled, the `origin_server_ts` and, thus, the event ID cannot be determined ahead | ||
| of time. |
There was a problem hiding this comment.
Is this the only field stopping event ID calculation?
There was a problem hiding this comment.
prev_events and auth_events are also not known ahead of time and we don't want to mess up the DAG
There was a problem hiding this comment.
Right, those are much stronger rationale IMO. I could see it being reasonable to use the original schedule time as origin_server_ts or something like that if it was the only issue.
|
@mscbot concern open comment threads |
Co-authored-by: Patrick Cloke <clokep@users.noreply.github.com>
| The body for requests to this endpoint is a JSON object containing the following fields: | ||
|
|
||
| - `delay_ms` - Required. A positive non-zero number of milliseconds the homeserver should wait before sending the event. | ||
| - `state_key` - The state key for the event to be sent, if it is to be a state event; absent otherwise. | ||
| - `content` - Required. The content of the event to be sent. |
There was a problem hiding this comment.
It's fine IMHO.
I guess if you wanted to mirror the existing endpoints, you'd have PUT /_matrix/client/v3/rooms/{roomId}/delayed_send/{eventType}/{txnId} and PUT /_matrix/client/v3/rooms/{roomId}/delayed_state/{eventType}/{stateKey}. I woudn't be dead against that, but I'm unconvinced it's worth changing.
| If a user's request to schedule a delayed event would exceed this limit, the homeserver will respond with HTTP 429, | ||
| a [standard error response](https://spec.matrix.org/v1.18/client-server-api/#standard-error-response) | ||
| with an `errcode` of `M_LIMIT_EXCEEDED`, and a `Retry-After` header whose value is set to the time of/until | ||
| the scheduled send time of the next of the user's delayed events to be sent, | ||
| rounded up to the nearest second. |
There was a problem hiding this comment.
I think M_USER_LIMIT_EXCEEDED was introduced to deal with premium accounts; the general implication is that it's a quota you can pay to increase. Accordingly, M_LIMIT_EXCEEDED is probably appropriate here. And normally we would use a 429.
| or if the `action` is `cancel` and the delayed event has already been sent, | ||
| the homeserver will respond with HTTP 409 | ||
| and a [standard error response](https://spec.matrix.org/v1.18/client-server-api/#standard-error-response) | ||
| with an `errcode` of `M_UNKNOWN`. |
There was a problem hiding this comment.
I'm not a big fan of us deliberately specifying M_UNNOWN here. Maybe we can define an M_CONFLICT or something.
| If the action is `send` and the delayed event is unable to be sent due to an error, | ||
| the homeserver will respond with that error (e.g. HTTP 403 | ||
| and a [standard error response](https://spec.matrix.org/v1.18/client-server-api/#standard-error-response) | ||
| with an `errcode` of `M_FORBIDDEN` if the user doesn't have permission to send the event at the time of sending, |
| events due to federation delay. Potentially addressing this situation is, therefore, left to a future | ||
| proposal. | ||
|
|
||
| ### Inability to filter and paginate delayed events |
There was a problem hiding this comment.
The easiest solution to this may be to get rid of the list endpoint, if we don't have a usecase for it?
| events due to federation delay. Potentially addressing this situation is, therefore, left to a future | ||
| proposal. | ||
|
|
||
| ### Inability to filter and paginate delayed events |
There was a problem hiding this comment.
In any case, I'm not sure I agree. This MSC is big enough as it is, and pagination was broken out to MSC4486 deliberately. Let's just land this MSC without adding more clutter.
as it no longer has a planned use case and is thus not worth mentioning, even as an alternative
| the homeserver. This includes APIs for scheduling delayed events and managing scheduled delayed | ||
| events. | ||
|
|
||
| [Selective Forwarding Units (SFUs)]: https://trueconf.com/blog/wiki/sfu |
There was a problem hiding this comment.
nit: this link reference definition seems to be orphaned
| `GET /_matrix/client/v1/delayed_events` responds with | ||
| a list of details about scheduled delayed events owned by the requesting user. | ||
|
|
||
| Delayed events are returned in chronological order of their intended send time, which is `scheduled_at` + `delay_ms`. |
There was a problem hiding this comment.
Yes; I thought "chronological" also implied ascending time. If not, that can be clarified here.
|
|
||
| ### Additional homeserver behaviour | ||
|
|
||
| #### `delay_id` in `unsigned` event data |
There was a problem hiding this comment.
Does the server also include the transaction_id property in unsigned, like it does with events sent from the other event-sending endpoints?
There was a problem hiding this comment.
No, because the transaction_id only distinguishes requests, not the delayed events themselves.
Rendered
This could also supersede MSC2228 (by making it possible to send a redaction with the
/sendendpoint. This is the case as mentioned here)Implementations
Known differences between current implementations and the proposal
M_MAX_DELAY_EXCEEDED, HTTP 400, and a response propertymax_delaywhen the server refuses to schedule an event because the requested delay is too large. The proposal has since switched to the error codeM_FORBIDDEN, HTTP 403, and publishes the maximum allowed delay in them.delayed_eventscapability (so that clients can discover it ahead of making the request)./sendand/statewith a new query parameterdelayfor scheduling delayed events. The proposal has since switched to a dedicated endpointPUT /_matrix/client/v3/rooms/{roomId}/delayed_event/{eventType}/{txnId}where the delay is included in the request body./versionskey would be required to manage the migration from the previous unstable implementation.delayandmax_delayrather thandelay_msandmax_delay_ms.running_sincerather thanscheduled_at.delay_id-based authentication on the management endpoints, rather than expecting standard authentication with a user access token.Implementations in Element Call via the Widget API
These are only informational and shouldn't be relevant for the proposal process. The MSC doesn't depend on widgets and widgets are themselves not part of the spec.
SCT stuff:
MSC checklist
FCP tickyboxes
Designated reviewers: