Skip to content

Simplified Sliding Sync drops a departed user's own membership from required_state after a history purge #19884

Description

@codyfinn

As discussed in #synapse-dev:matrix.org,

Description

After purge_history runs over the point in a room's timeline where a user left / was kicked / was banned, Simplified Sliding Sync (MSC4186 / org.matrix.simplified_msc3575) stops returning that user's own m.room.member event in the room's required_state even though the membership is still durably recorded in current-state tables. The room is still returned in the response, just with no $ME membership event.

Steps to reproduce

I created a Complement test which reproduces the problem: https://github.com/codyfinn/synapse/blob/c117852b8aa27ec01996c8cc28690cc268a22203/complement/tests/sliding_sync_purged_membership_regression_test.go

Manual steps:

  • Register @alice (room admin) and @bob.
  • As @alice, create a public room: POST /_matrix/client/v3/createRoom {"preset": "public_chat"}!room:hs1.
  • As @bob, join: POST /_matrix/client/v3/join/!room:hs1. Wait for the join to sync.
  • As @alice, send a few m.room.message events so there is joined-era history before the departure (gives the purge something to outlier behind).
  • Depart @bob — any of the three reproduces:
    • kick: POST /_matrix/client/v3/rooms/!room:hs1/kick {"user_id": "@bob:hs1"}
    • ban: POST /_matrix/client/v3/rooms/!room:hs1/ban {"user_id": "@bob:hs1"}
    • leave: POST /_matrix/client/v3/rooms/!room:hs1/leave (as @bob)
  • As @alice, send one more m.room.message after the departure and record its event_id ($boundary). This is the purge boundary — everything topologically before it (including bob's m.room.member event) will be purged.
  • As admin, purge history up to the boundary:
    • POST /_synapse/admin/v1/purge_history/!room:hs1 with {"purge_up_to_event_id": "$boundary", "delete_local_events": true}
    • poll GET /_synapse/admin/v1/purge_history_status/{purge_id} until "status": "complete"
  • As @bob, issue a Simplified Sliding Sync request asking for his own membership:
    POST /_matrix/client/unstable/org.matrix.simplified_msc3575/sync
    {
      "lists": { "all": { "ranges": [[0, 99]],
        "required_state": [["m.room.member", "@bob:hs1"]], "timeline_limit": 1 } },
      "room_subscriptions": { "!room:hs1": {
        "required_state": [["m.room.member", "@bob:hs1"]], "timeline_limit": 1 } }
    }

Homeserver

A personal homeserver

Synapse Version

1.155.0

Installation Method

Docker (matrixdotorg/synapse)

Database

PostgreSQL single server. Never ran SQLite. Never restored from a backup

Workers

Multiple workers

Platform

Reproduced on Complement Synapse image — Debian trixie, Python 3.13, running under Docker on macOS

Running in a Kubernetes deployment

Configuration

No response

Relevant log output

I can't share server logs, but it is reproducible with a Complement test found here: https://github.com/codyfinn/synapse/blob/c117852b8aa27ec01996c8cc28690cc268a22203/complement/tests/sliding_sync_purged_membership_regression_test.go

Anything else that would be useful to know?

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions