MSC4140: put delay_id in unsigned data for sender#19479
MSC4140: put delay_id in unsigned data for sender#19479reivilibre merged 10 commits intoelement-hq:developfrom
Conversation
When persisting a delayed event to the timeline, include its delay_id in the event's unsigned section in /sync responses to the event sender.
reivilibre
left a comment
There was a problem hiding this comment.
Seems reasonable overall
|
|
||
| txn_id: str | ||
| """The transaction ID, if it was set when the event was created.""" | ||
| delay_id: str |
There was a problem hiding this comment.
I'm not sure everything else in this file does it correctly right now, but I would be in favour of making this optional
| delay_id: str | |
| delay_id: str | None |
This will also involve changing the Rust part to return Nones when needed.
I'm not dead-set on this, so open to your thoughts? But it seems like it is more typechecker-friendly this way.
There was a problem hiding this comment.
My vote is to leave this as it is for now, as much as I appreciate proper typing, because there appears to be some nuance to this that I don't fully grasp at the moment & am hesitant to muck around with.
From what I can gather from the Rust code, there are some fields in the metadata object that are always present but may set to None (like stream_ordering and instance_name), and others that may be absent but must always be non-None when present (like txn_id and device_id).
So, it looks like there is a functional difference between the not-typed-as-optional fields that Python code looks up with getattr, and ones that are typed as optional. Since delay_id is used in much the same way as txn_id, IMO it's sufficient for the time being to use the same code patterns of the latter for the former.
synapse/events/utils.py
Outdated
| # unsigned section of the event if the event was sent by the same session as the one | ||
| # requesting the event. | ||
| txn_id: str | None = getattr(e.internal_metadata, "txn_id", None) | ||
| delay_id: str | None = getattr(e.internal_metadata, "delay_id", None) |
There was a problem hiding this comment.
(we could then avoid this getattr call if we did make it natively str | None)
synapse/events/utils.py
Outdated
| # requesting the event. | ||
| txn_id: str | None = getattr(e.internal_metadata, "txn_id", None) | ||
| delay_id: str | None = getattr(e.internal_metadata, "delay_id", None) | ||
| if ( |
There was a problem hiding this comment.
Arguably this should have been done earlier, but now we're adding more things that use this logic, it feels even stronger that it would be worth pulling out a _should_include_same_session_metadata() -> bool
function (or something along those lines) that would let us isolate the condition logic from the actual addition of the fields.
There was a problem hiding this comment.
Part of the condition is the presence of the fields to be added to the event, which IMO makes it not worth splitting this out into a standalone function, lest it just moves the data/logic coupling somewhere else rather than decoupling them.
There was a problem hiding this comment.
Hmm, not necessarily standalone.
I guess what I was going for was to replace the if-else block starting R483 with one that, instead of setting the fields directly, sets should_include_same_session_metadata = True and have a single block to populate that metadata afterwards (i.e. separate condition from action, also meaning we don't have to write the action twice).
There was a problem hiding this comment.
reivilibre
left a comment
There was a problem hiding this comment.
Overall looks good. Some MSC divergences I spotted though, I think.
synapse/events/utils.py
Outdated
| if delay_id is not None: | ||
| d["unsigned"]["delay_id"] = delay_id |
There was a problem hiding this comment.
I'm re-reading the MSC now and noticing that the MSC says we should include the delay_id to the event sender, but here we only include when the person is using the exact same device.
Which one is correct?
The MSC also says it's only included on /sync (same ref), yet at first glance it seems like this is included on any event-returning CSAPI request.
There was a problem hiding this comment.
I'm re-reading the MSC now and noticing that the MSC says we should include the delay_id to the event sender, but here we only include when the person is using the exact same device. ... Which one is correct?
It should be included for all sessions. Will write a change for this.
The MSC also says it's only included on /sync (same ref), yet at first glance it seems like this is included on any event-returning CSAPI request.
Ah, I had put this code here to do the same as what's done for including the transaction_id in an event, thinking that it only came through /synced events as well. Does the transaction_id get included in any event-returning request? If so, then I'd argue for delay_id to follow suit, and would adjust the MSC accordingly.
There was a problem hiding this comment.
Actually, including delay_id for more than just /sync would definitely be useful, regardless of what is done for transaction_id (or any other unsigned data, for that matter). MSC discussion is here: matrix-org/matrix-spec-proposals#4140 (comment)
As for including delay_id for all sessions, see d699205
There was a problem hiding this comment.
For the record, the MSC discussion settled on including delay_id in unsigned data for all requests by the event's sender, not just /sync.
There was a problem hiding this comment.
Makes sense. I think returning metadata differently in different endpoints would be a strange choice because it makes it hard for clients to be able to reliably cache events.
Check for requester == sender before looking up keys to include in the unsigned data. Do this in an if branch instead of storing the check to a variable, because otherwise mypy doesn't know that the requester is not None.
This PR contains the following updates: | Package | Update | Change | |---|---|---| | [element-hq/synapse](https://github.com/element-hq/synapse) | minor | `v1.149.1` → `v1.150.0` | --- ### Release Notes <details> <summary>element-hq/synapse (element-hq/synapse)</summary> ### [`v1.150.0`](https://github.com/element-hq/synapse/releases/tag/v1.150.0) [Compare Source](element-hq/synapse@v1.149.1...v1.150.0) ### Synapse 1.150.0 (2026-03-24) No significant changes since 1.150.0rc1. ### Synapse 1.150.0rc1 (2026-03-17) #### Features - Add experimental support for the [MSC4370](matrix-org/matrix-spec-proposals#4370) Federation API `GET /extremities` endpoint. ([#​19314](element-hq/synapse#19314)) - [MSC4140: Cancellable delayed events](matrix-org/matrix-spec-proposals#4140): When persisting a delayed event to the timeline, include its `delay_id` in the event's `unsigned` section in `/sync` responses to the event sender. ([#​19479](element-hq/synapse#19479)) - Expose [MSC4354 Sticky Events](matrix-org/matrix-spec-proposals#4354) over the legacy (v3) /sync API. ([#​19487](element-hq/synapse#19487)) - When Matrix Authentication Service (MAS) integration is enabled, allow MAS to set the user locked status in Synapse. ([#​19554](element-hq/synapse#19554)) #### Bugfixes - Fix `Build and push complement image` CI job pointing to non-existent image. ([#​19523](element-hq/synapse#19523)) - Fix a bug introduced in v1.26.0 that caused deactivated, erased users to not be removed from the user directory. ([#​19542](element-hq/synapse#19542)) #### Improved Documentation - In the Admin API documentation, always express path parameters as `/<param>` instead of as `/$param`. ([#​19307](element-hq/synapse#19307)) - Update docs to clarify `outbound_federation_restricted_to` can also be used with the [Secure Border Gateway (SBG)](https://element.io/en/server-suite/secure-border-gateways). ([#​19517](element-hq/synapse#19517)) - Unify Complement developer docs. ([#​19518](element-hq/synapse#19518)) #### Internal Changes - Put membership updates in a background resumable task when changing the avatar or the display name. ([#​19311](element-hq/synapse#19311)) - Add in-repo Complement test to sanity check Synapse version matches git checkout (testing what we think we are). ([#​19476](element-hq/synapse#19476)) - Migrate `dev` dependencies to [PEP 735](https://peps.python.org/pep-0735/) dependency groups. ([#​19490](element-hq/synapse#19490)) - Remove the optional `systemd-python` dependency and the `systemd` extra on the `synapse` package. ([#​19491](element-hq/synapse#19491)) - Avoid re-computing the event ID when cloning events. ([#​19527](element-hq/synapse#19527)) - Allow caching of the `/versions` and `/auth_metadata` public endpoints. ([#​19530](element-hq/synapse#19530)) - Add a few labels to the number groupings in the `Processed request` logs. ([#​19548](element-hq/synapse#19548)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My44NC4yIiwidXBkYXRlZEluVmVyIjoiNDMuODQuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW1hZ2UiXX0=--> Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/5040 Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net> Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
Implements matrix-org/matrix-spec-proposals@49b200d
Pull Request Checklist
EventStoretoEventWorkerStore.".code blocks.