Commit Graph
4120 Commits
Author SHA1 Message Date
Jason Robinson b0950f98df Merge remote-tracking branch 'origin/develop' into anoa/msc4429 2026-06-29 12:57:21 +03:00
Jason Robinson b459512725 Use include_profile_updates_in_sync config item instead of an experimental flag + alias.
The requirement for this comes from a customer commitment.
2026-06-26 18:08:12 +03:00
Will HuntandGitHub 2bb3aac839 Add metric to count number of non-deactivated users. (#19848)
This reports the total count of users (split by appservice) which is
meant to be the monthless counterpart to the MAU metric.

Context:

> So this is largely for billing purposes and wanting to know the change
in the number of users. If a user is deactivated then we no longer want
to count them. Consumers *might* want to count appservice users, and
maybe count them based on the service (perhaps you change more for users
under bridge X or bridge Y).
>
> *-- https://github.com/element-hq/synapse/pull/19848#discussion_r3402216234*
2026-06-25 11:57:36 -05:00
Jason Robinson 5f761ab5b4 Use set for uniqueness 2026-06-25 17:39:42 +03:00
Jason Robinson b657d2f8db Add a prune job for the profile updates stream tables
Taken and adapted from https://github.com/element-hq/synapse/pull/19473

Didn't add the "safety" table due to afaik that has more relevance with the device updates, and not so much here for the profile updates.
2026-06-24 19:11:41 +03:00
Jason Robinson 7c728ec61c Optimize profile handler user_joined_room and user_left_room
Save a db query if there are no users to add profile updates for.
2026-06-24 13:45:13 +03:00
Jason Robinson d3aacee235 Fix test_update_profile_set_field_writes_to_per_user_profile_tracking_table test
And remove unused `get_profile_updates_per_user_for_user` function
2026-06-24 13:32:19 +03:00
Jason Robinson 9d58d19934 Merge remote-tracking branch 'origin/develop' into anoa/msc4429 2026-06-24 11:52:58 +03:00
Jason Robinson 8504386b1f Send down full profile of user if user has joined during incremental sync
Also refactor the "user left" to not listen to the "user_left_room" dispatch hook since we only want to do this once, thus call the profile handler immediately in the instance responsible for receiving the event.
2026-06-23 16:44:39 +03:00
16fe5fb462 Expose MSC4354 Sticky Events over MSC4186 (Simplified) Sliding Sync. (#19591)
Follows: #19487
Part of: MSC4354 whose experimental feature tracking issue is #19409

This PR implements the Sliding Sync (MSC4186) extension described in
MSC4354, allowing sliding sync clients
to receive sticky events in a reliable way.

The logic is much the same as for oldschool sync (implementation in
#19487),
although in the sliding sync extension, the client can choose their own
limit
and must control their own pagination through an extra token in the
extension request/response bodies.

Note this does not yet send down existing sticky events in the
room when the room has been newly-joined.
This newly-discovered gap is tracked at #19662 and will be addressed for
both current sync and MSC4186 SSS soon.

---------

Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
Co-authored-by: Eric Eastwood <erice@element.io>
2026-06-23 13:04:36 +00:00
Jason Robinson 2dbaec329b Send down a null when user has left all shared rooms
Restore test and switch from {} to null.
2026-06-23 15:37:35 +03:00
Jason Robinson 09e212537e Fix incremental lazy sync to send down profiles for users without profile updates
Remove the checking of any stream token changes and ensure we always collect profiles for users even if they have not done profile updates, if they have events in the timeline. Also fix the cache.
2026-06-23 14:53:33 +03:00
Stefan CeriuandGitHub 6d289f7ce0 Fix permanent badge inflation from read receipts before first rotation (#19785) 2026-06-23 11:31:32 +00:00
Jason Robinson 5819dccd91 Use a cache to ensure we don't re-send the full profile fields of users in every incremental sync all the time when lazy loading 2026-06-23 13:10:21 +03:00
Jason Robinson daa8d7a28d Ensure lazy loaded profile updates in sync contain full profiles for users in timeline 2026-06-23 12:39:07 +03:00
Jason Robinson 874dc359ac Merge remote-tracking branch 'origin/develop' into anoa/msc4429 2026-06-22 22:54:16 +03:00
Andrew Morgan ab9c917ca2 Remove test_incremental_sync_sends_down_null_profile_if_user_no_longer_sharing_rooms
We might just have clients do this instead (based on leave membership events).
2026-06-21 23:02:04 +01:00
Andrew Morgan 4a3fc09856 fix the tests 2026-06-21 22:53:29 +01:00
d3fc81974a MSC1763: Implement /_matrix/client/unstable/org.matrix.msc1763/retention/configuration (#19853)
Implements https://github.com/matrix-org/matrix-spec-proposals/pull/1763
Tracking issue https://github.com/element-hq/synapse/issues/19852

This only implements the MSC's configuration endpoint and does not add
new configuration fields or options. The reason for including this is to
allow clients to run global retention against locally stored events, and
to be able to offer configuration UI.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-18 17:37:02 +00:00
2487b31422 MSC4140: allow auth on management endpoints for delayed events (#19794)
Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-18 13:27:37 +00:00
FrenchGithubUserandGitHub 75ee23a16b feat: Include allowed_room_ids in the /summary client-server API response for rooms with restricted join rules, as required by Matrix 1.15. (#19762)
Currently the `/summary` endpoint on the client and federation APIs does
not include the allowed_room_ids field, that is present on the
/hierarchy API. This is required by the 1.15 spec and this is what this
PR does.

Complement test: https://github.com/matrix-org/complement/pull/873

Part of https://github.com/element-hq/synapse/issues/18731
2026-06-18 14:12:51 +01:00
Olivier 'reivilibreandGitHub 2d06667661 Fix local events being deleted despite delete_local_events=False in room versions other than 1 and 2. (#19850)
Not aware of an open ticket for this.

I came across it when I accidentally broke the feature even more (as
part of another piece of work),
then discovered there weren't tests for this.

So this is overall a low-priority drive-by fix.

Requires a fix to SyTest https://github.com/matrix-org/sytest/pull/1426
(as it depended on the bug).

<ol>
<li>

Add a test for purging rooms with `delete_local_events=False` \
Parameterised by room version, this test currently succeeds
on v2 but fails on v12.

This is because the condition checking for local events relies
on the old event ID format, which has not been used since v2.


</li>
<li>

Fix delete_local_events=False for room versions above v2 \
The event ID format changing means that we have to rely on `sender`
to know the origin of an event


</li>
</ol>

---------

Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
2026-06-17 11:03:59 +01:00
1b7807ae6b Fix /sync caching transient errors for the sync_response_cache_duration. (#19845)
Fixes: #19844

Concretely, this changes `ResponseCache` to unset cache entries once
they resolve to a `Failure`.

---------

Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
Co-authored-by: Eric Eastwood <erice@element.io>
2026-06-16 11:12:29 +01:00
d7e9a3ff83 Fix inflated notification counts after /purge_history (#19834)
The sytest `After /purge_history users still get pushed for new
messages` is flaky. The flakiness exposes a real bug rather than a
test-timing issue.

Notification counts are stored in two places: `event_push_actions` (one
row per unread event) and `event_push_summary` (aggregate counts
populated periodically by `_rotate_notifs`, which runs on a 30-second
timer). `_purge_history_txn` deletes the purged events' rows from
`event_push_actions` but never adjusts `event_push_summary` (only the
full-room `purge_room` drops that table).

So the result depends on a race: if rotation hasn't fired, counts come
live from `event_push_actions`, the purge removes the right rows, and
the count is correct. If rotation fires before the purge — more likely
under the slower
multi-postgres/workers/asyncio CI config — the events get folded into
`event_push_summary`, the purge then deletes the underlying
`event_push_actions` rows but leaves the summary untouched, and the
count comes out inflated.

### Fix

Before deleting the rotated rows from `event_push_actions`, decrement
`event_push_summary` by the amount attributable to the events being
deleted. The decrement mirrors the counting logic in
`_rotate_notifs_before_txn`: only rows that were already rotated
(`stream_ordering <= event_push_summary_stream_ordering`) and that fall
after the summary's receipt are subtracted, so it stays correct in the
presence of read receipts and unread/highlight rows. The SQL avoids
`UPDATE ... FROM` and CTEs so it works on both SQLite and Postgres.
End-of-purge cache invalidation already covers
`get_unread_event_push_actions_by_room_for_user`.

### Tests

Adds `test_count_aggregation_after_purge`, which forces a rotation
before purging and asserts the aggregate count reflects only the
surviving events, covering read receipts and a subsequent re-rotation.
It fails (`3 != 1`) without the fix.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 16:31:26 +01:00
Eric EastwoodandGitHub d11b012a81 Force keyword-args for clear default_config(server_name="test") usage (#19849)
I.e. `default_config("test")` and `default_config("test", False)` are so
opaque and hard to connect the dots with.

Spawning from trying to figure out what the `server_name` is set as for
our `HomeserverTestCase` in order to reference it in
https://github.com/element-hq/synapse/pull/19848#discussion_r3397455309
2026-06-12 09:45:22 -05:00
ff034c1f62 Fix flakey test failing with "twisted.protocols.amp.TooLong" (#19832)
This was all done through claude.

## Fix flaky `test_edu_large_messages_not_splitting_one_user` (`TooLong`
under `trial -jN`)

  ### Problem

  The "Build .deb packages" CI step intermittently failed with:

  ```
  twisted.protocols.amp.TooLong
...in
tests.rest.client.test_sendtodevice.SendToDeviceTestCase.test_edu_large_messages_not_splitting_one_user
  ```

The deb build runs the suite with `twisted.trial -j2`. In that mode,
worker log events are shipped to the manager process over Twisted's AMP
protocol, which encodes each value with a 2-byte length prefix — so any
single log line of **64 KiB or more** raises `TooLong`.

  ### Root cause

Not a bug in the to-device/EDU logic, and **not** debug logging enabled
by the build (it runs at the default `ERROR` level). It's a **log-level
leak between tests sharing a `-j2` worker**:

- `tests/logging/test_loggers.py::ExplicitlyConfiguredLoggerTestCase`
calls `root_logger.setLevel(logging.DEBUG)` directly and never restores
it (no `setUp`/`tearDown`/`addCleanup`).
- When that test runs before
`test_edu_large_messages_not_splitting_one_user` **in the same worker
process**, the root logger is left at `DEBUG`.
- That test deliberately builds an EDU of exactly `SOFT_MAX_EDU_SIZE -
1` (65 535) bytes. Storing it triggers `synapse/storage/database.py`'s
`[SQL values]` DEBUG log, which dumps the full query params — producing
a **65 708-byte**
  line that overflows AMP's cap.
  
It looks "flaky" purely because of `-j2` scheduling: whether the two
tests land on the same worker, and in what order.
  
  ### Fix

  Three commits:

1. **Restore the root logger level in
`ExplicitlyConfiguredLoggerTestCase`** — `addCleanup` to put the level
back. Fixes the root cause.
2. **Truncate oversized log lines in the test log handler**
(`ToTwistedHandler.emit`) — caps lines at 1000 chars so no debug line
can break `trial -jN`, regardless of which query is logged (defense in
depth).
3. **Truncate values in `[SQL values]` debug logging** — caps the logged
param repr at 1000 chars (guarded by `isEnabledFor(DEBUG)` to keep the
hot path lazy). Keeps production debug logs sane too.

  ### Testing

- The reproduction (`trial -j2 tests.logging.test_loggers <the EDU
test>`) went from **~3/8 failing** to **10/10 passing**.
- Confirmed the root level is restored after the logging tests, and that
the `[SQL values]` line is now capped (~50 KB with a `[truncated]`
marker, was 65 708).
- `ruff` + `mypy` clean; `tests.logging.test_loggers` and
`tests.rest.client.test_sendtodevice` pass (14/14).

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Eric Eastwood <erice@element.io>
2026-06-10 17:33:21 +01:00
Erik JohnstonandGitHub ac21bf08f3 Port Requester class to Rust. (#19828)
This is in prep for converting the event serialization to Rust.

This is a fairly mechanical port, except that we store the appservice ID
rather than the appservice object. This avoids us having to store a
`Py<..>` (or port the appservice object over).
2026-06-09 10:26:04 +01:00
8c9b1ff877 Reintroduce #19714 - Send a SSS response immediately if the config has changed (#19792)
Reintroduces #19714, after being reverted in #19784.

Fix https://github.com/element-hq/synapse/issues/18844
Fix https://github.com/element-hq/synapse/issues/19783
Fix https://github.com/element-hq/synapse/issues/18880

This PR also adds a fix so that we don't always return immediately when
using the e2ee extension.

---------

Co-authored-by: Benjamin Bouvier <benjamin@bouvier.cc>
Co-authored-by: Eric Eastwood <madlittlemods@gmail.com>
Co-authored-by: Eric Eastwood <erice@element.io>
2026-06-08 11:42:57 +01:00
Jason Robinson e90096ef57 Use a "per user profile updates" tracking table
This makes writes heavier when profile updates happen, but reduces the effort to produce an incremental sync response by not needing to look whether users share rooms.
2026-06-05 19:17:12 +03:00
Jason Robinson 877074f38e Lint tests/handlers/test_sync.py 2026-06-05 15:30:23 +03:00
Jason Robinson 28587d7907 Fix the lazy loading test cases 2026-06-05 13:49:27 +03:00
Jason Robinson 2d9f943034 Collect lazy loaded members for profile updates in sync response from events 2026-06-04 22:04:41 +03:00
Jason Robinson 36f0f64f62 Add "action" to profile updates stream
This allows distinguishing between "a field was updated" or "something else happened". In this case, we want to know if the user left a room. Seeing this action while building the sync response we can then check if the user is still a member of the same rooms as the syncing user, and if not, we can tell the client to remove their profile.
2026-06-04 11:26:50 +03:00
Noah MarkertandGitHub 003febe399 Add tests to ensure that email notification links are sanitized (#19741)
Fix #2860

Also cleans up comments around plans to define `ALLOWED_SCHEMES` as we can rely on Bleach's `ALLOWED_PROTOCOLS` defaults (`http`, `https`  and `mailto`).
2026-06-03 17:36:58 -05:00
Jason Robinson b61f748928 Fix some broken tests due to sync token changes 2026-06-03 18:08:36 +03:00
Erik JohnstonandGitHub e8e5a42180 Fix parsing events that have large integers (#19819)
Follow on from https://github.com/element-hq/synapse/pull/19701.

Unfortunately serde has a bug when using `#[serde(flatten)]` with
`arbitrary-precision` feature when handling integers that fit in a i128
when doing `serde_json::from_value`. See
https://github.com/serde-rs/serde/issues/2230.

The `depythonize` hits the same issue. To fix this we make it so we only
parse events from strings and not values.
2026-06-03 14:52:05 +01:00
Jason Robinson bd40add5d5 Add some tests for profile handler updating profile_updates stream table and waking up notifiers 2026-06-03 13:55:22 +03:00
Jason Robinson e236ddb32b Add SyncProfileUpdatesTestCase
For initial sync
2026-06-02 21:35:34 +03:00
5bf825c016 Limit to-device EDU sizes (#19617)
This is based on https://github.com/element-hq/synapse/pull/18416, which
got reverted (#19614) due to it incorrectly rejecting to-device messages
to users with many devices (and thus breaking message sending).

Fix https://github.com/element-hq/synapse/issues/17035

A to-device message content looks like:

```jsonc
{
  "@user:domain": {"device1": {...}, "device2": {...}},
  ...
}
```

The previous PR would split up into multiple EDUs, each with a subset of
the users. However, if one user's entry was too large it would not
further split it up and then error out.

The main change in this PR is to allow splitting up a single user into
multiple EDUs.

Other changes:
1. Rename to `SOFT_MAX_EDU_SIZE` to indicate that we sometimes send EDUs
with larger size than that, and its more a target than a hard limit.
2. Check early if any to-device message (to a specific device) is too
large to send, even if we're not going to send it over federation. This
ensures that we catch issues where clients try to send too large
to-device.

This still means that if a client send a large individual to-device
message it will fail, but I don't believe we ever send such large
to-device messages (normally they're in the range of a few KB).

---

I ended up changing the implementation a bunch to make it easy to reuse
the code to split up dictionaries. Instead of repeatedly splitting up
the EDU until each bit fits into the size, we instead record the size of
each entry in the dict and instead split up based on cumulative size.
This means we call `encode_canonical_json` on each entry rather than
once on the entire struct, but its not significantly slower to do so.

--

cc @MatMaul @MadLittleMods

---------

Co-authored-by: Mathieu Velten <matmaul@gmail.com>
Co-authored-by: mcalinghee <mcalinghee.dev@gmail.com>
Co-authored-by: Eric Eastwood <madlittlemods@gmail.com>
2026-06-02 15:57:55 +00:00
9e2a076144 Port Event class to Rust (#19701)
Ports the event class to Rust.

The main difference here are:
1. There is now a single event class
2. We now validate a lot more at event construction time than we
previously did (we basically checked nothing before). This required some
changes to the tests, including
https://github.com/matrix-org/sytest/pull/1423

Reviewable commit-by-commit.

### Overview of Event Rust structure

The format of the event struct in Rust is quite different than that in
Python.

The top-level looks like:

```rust
pub struct Event {
    /// The parsed event JSON.
    fields: FormattedEvent,

    /// The event ID. For format v1 this is read directly from the JSON;
    /// for v2+ it is computed from the canonical-JSON hash at
    /// construction time and cached here.
    event_id: Arc<str>,

    /// Synapse-internal per-event state that lives outside the federated
    /// JSON (e.g. outlier flag, soft-failure, stream positions).
    #[pyo3(get)]
    internal_metadata: EventInternalMetadata,

    /// The room version this event was parsed for.
    #[pyo3(get)]
    room_version: &'static RoomVersion,

    /// `None` for accepted events; otherwise a short reason set by auth
    /// when the event was rejected.
    rejected_reason: Option<Box<str>>,
}
```

which includes the actual parsed event in `FormattedEvent`, plus the
rest of the event metadata.

```rust
pub struct FormattedEvent<E = Arc<EventFormatEnum>> {
    #[serde(default)]
    pub signatures: Signatures,

    #[serde(default)]
    pub unsigned: Unsigned,

    #[serde(flatten)]
    pub specific_fields: E,

    #[serde(flatten)]
    pub common_fields: Arc<EventCommonFields>,
}
```

The struct is further split into the common fields, format specific
fields, plus the signatures and unsigned. We split out the signature and
unsigned fields as they are mutable, so when we clone the event we can
still share the common and specific fields and only copy signature and
unsigned.

The `specific_fields` are the fields that depend on the format version.
They can either be a specific format (e.g. `E = EventFormatV1`) or a
type-erased enum `EventFormatEnum` that is across all room versions:

```rust
pub enum EventFormatEnum {
    V1(EventFormatV1),
    V2V3(EventFormatV2V3),
    V4(EventFormatV4),
    VMSC4242(EventFormatVMSC4242),
}
```

For example:

```rust
/// Shared flat-list encoding of `auth_events` and `prev_events`, reused
/// by every format from v2/v3 onwards.
#[derive(Serialize, Deserialize)]
pub struct SimpleAuthPrevEvents {
    pub auth_events: Vec<String>,
    pub prev_events: Vec<String>,
}

/// Version-specific fields for room versions 3-10.
#[derive(Serialize, Deserialize)]
pub struct EventFormatV2V3 {
    pub room_id: Box<str>,
    #[serde(flatten)]
    pub auth_prev_events: SimpleAuthPrevEvents,
}
```


### Dev notes

As discussed in
[`#element-backend-internal:matrix.org`](https://matrix.to/#/!SGNQGPGUwtcPBUotTL:matrix.org/$3gTjDO440GbAz57cXcCawwiyFLiD0crrarvS1uhzKOY?via=jki.re&via=element.io&via=matrix.org)

---------

Co-authored-by: Eric Eastwood <erice@element.io>
2026-06-02 11:05:38 +01:00
306d8b23bd Feat/gcp json formatter (#19775)
Co-authored-by: Andrew Morgan <andrew@amorgan.xyz>
2026-05-29 10:57:33 +00:00
Tulir AsokanandGitHub 0e39c0c8f6 Fix policy server signature merging again (#19797)
Fixes #19796
2026-05-26 14:12:20 -05:00
Joe GroocockandGitHub 966e193e4e Fix sending heroes in SSS when m.room.name="" (#19468)
As per the spec, a room with m.room.name value that is absent, null or
empty should be treated as if there is no m.room.name event at all:
https://spec.matrix.org/v1.17/client-server-api/#mroomname

This fetches the full m.room.name event and checks the content.name
instead of only checking the existence of the m.room.name event. This
results in correctly sending heroes for those rooms.

Fixes: https://github.com/element-hq/synapse/issues/19447

Signed-off-by: Joe Groocock <me@frebib.net>
2026-05-20 12:10:54 +01:00
FrenchGithubUserandGitHub 87095ae500 fix: invalidate access token cache on device deletion (#19483)
when an access token had a refresh token associated to it in the
database, deleting this refresh token (for example when deleting the
device using it) would cascade delete the access token, which wouldn't
be returned by the sql query that was supposed to delete it on its own,
and an empty array was passed to the cache invalidation function.
2026-05-20 11:06:16 +01:00
d8b4ffdf2d Fix validation of frozen message event with mentions. (#19634)
Fixes: #19689

# What

This PR fixes a bug I found when I run synapse (from dockerhub) and
register a `check_event_allowed` callback and my client makes use of the
mentions field in messages (`cinny:latest`). The bug doesn't appear when
the `check_event_allowed` callback is not loaded.

After some digging I noticed that the current validation of the mentions
doesn't work when an event has been frozen with `event.freeze()`. For
the messages this seems to happen when a the `check_event_allowed` is
registered (but not otherwise), see [where the event is frozen for
check_event_allowed
callback](https://github.com/element-hq/synapse/blob/b0fc0b7a612a42e6f15b87dee2a1db4c383645fb/synapse/module_api/callbacks/third_party_event_rules_callbacks.py#L289)
and [where the validation function is
called](https://github.com/element-hq/synapse/blob/b0fc0b7a612a42e6f15b87dee2a1db4c383645fb/synapse/handlers/message.py#L1404).

To have a minimal reproduction example, the following scripts fails on
`develop` but succeeds in this branch:

``` python
from synapse.api.room_versions import RoomVersions
from synapse.events import EventBase, make_event_from_dict
from synapse.events.validator import EventValidator

from tests.utils import default_config


def make_message_event(content: dict) -> EventBase:
    return make_event_from_dict(
        {
            "room_id": "!room:test",
            "type": "m.room.message",
            "sender": "@alice:test",
            "content": content,
            "auth_events": [],
            "prev_events": [],
            "hashes": {"sha256": "aGVsbG8="},
            "signatures": {},
            "depth": 1,
            "origin_server_ts": 1000,
        },
        room_version=RoomVersions.V9,
    )


event = make_message_event(
    {
        "msgtype": "m.text",
        "body": "@moderator:example.com hello",
        "m.mentions": {"user_ids": ["@moderator:jailbreak-challenge.aqtiveguard.com"]},
    }
)

EventValidator().validate_new(event, default_config)  # Ok
event.freeze()
EventValidator().validate_new(event, default_config)  # throws
# pydantic_core._pydantic_core.ValidationError: 1 validation error for Mentions
#   Input should be a valid dictionary or instance of Mentions [type=model_type, input_value=immutabledict({'user_ids'...nge.aqtiveguard.com',)}), input_type=immutabledict]
#     For further information visit https://errors.pydantic.dev/2.12/v/model_type
```

# How

I made the validation logic also validate the transformation performed
by the freezing process, namely:
- `immutabledict` validates as `dict`. (was already implemented for
POWER_LEVELS)
- `tuple` validates as array (added this to the validator in this PR).


---------

Co-authored-by: Eric Eastwood <madlittlemods@gmail.com>
Co-authored-by: Olivier 'reivilibre <oliverw@matrix.org>
2026-05-18 10:27:10 +01:00
Eric EastwoodandGitHub 8eb220a5e2 Replace wait_for_quarantined_media_stream_id(...) with standard wait_for_stream_token(...) (#19764)
In order to be able to use `wait_for_stream_token(...)`, we have to add
the `quarantined_media` stream to the `StreamToken`. Even though we
don't care about `/sync`'ing `quarantined_media`, this aligns with the
future where all endpoints should probably use `StreamToken`, see
https://github.com/element-hq/synapse/issues/19647

Follow-up to https://github.com/element-hq/synapse/pull/19558 and
https://github.com/element-hq/synapse/pull/19644
2026-05-15 13:51:03 -05:00
Olivier 'reivilibreandGitHub 3f0f03d536 Revert "Send a SSS response immediately if the config has changed and there are new results to sync (#19714)" (#19784)
Reverts: #19714

Opens: #19783

Closes: https://github.com/element-hq/backend-internal/issues/242

Related: #18880 (the performance problem that is aggravated by #19714)

This reverts commit 2691d0b8b1.

---------

Signed-off-by: Olivier 'reivilibre <oliverw@matrix.org>
2026-05-15 10:36:47 +00:00
Erik JohnstonandGitHub ff55aff5b2 Fix up event-construction in tests ahead of the Rust event port (#19781)
When we port the `Event` class to Rust, the constructor will check for
the existence of required fields. To support that, we tidy up the test
code where we construct fake events to add all the required fields.

There should be no behavioural changes.

Review commit-by-commit.
2026-05-15 10:12:42 +01:00
Eric EastwoodandGitHub b233892a13 Update wait_for_stream_token(...) patterns and fix sync fetching with unbounded token (#19644)
Spawning from trying to find the proper way to wait for a token, see
https://github.com/element-hq/synapse/pull/19558#discussion_r2977673208

- Update `wait_for_stream_token(...)` patterns so
validation/sanitization is handled upstream in usage.
- Fix sync waiting for bounded token but using unbounded token to fetch
data. Noticed while working on adding the new method.

Part of https://github.com/element-hq/synapse/issues/19647
2026-05-14 14:53:16 -05:00
Erik JohnstonandGitHub 86a1e73ef4 Consolidate MSC4242 state DAG checks via a TypeIs helper (#19774)
The reason for the change is to make it easier to support these checks
when porting event class to Rust.

Previously, code that needed to access `prev_state_events` had to
combine a `room_version.msc4242_state_dags` boolean check with an
`isinstance(event, FrozenEventVMSC4242)` cast (or `cast()`) for the type
checker. Introduce `supports_msc4242_state_dag()` in a new
`synapse/events/py_protocol.py` which does both in one step via
`TypeIs[MSC4242Event]`, removing the need to import the concrete
`FrozenEventVMSC4242` class at every call site.

`MSC4242Event` is an `EventBase` subclass used purely for type narrowing
— it's marked with a metaclass that rejects `isinstance()` to make
accidental runtime use loud.

No behavioural change: callers continue to gate on the same room version
flag and access the same `prev_state_events` attribute.
2026-05-14 18:02:33 +00:00