diff --git a/changelog.d/19460.bugfix b/changelog.d/19460.bugfix new file mode 100644 index 0000000000..fe44c2ddf6 --- /dev/null +++ b/changelog.d/19460.bugfix @@ -0,0 +1 @@ +Fix `/sync` missing membership event in `state_after` (experimental [MSC4222](https://github.com/matrix-org/matrix-spec-proposals/pull/4222) implementation) in some scenarios. diff --git a/docker/complement/conf/workers-shared-extra.yaml.j2 b/docker/complement/conf/workers-shared-extra.yaml.j2 index 120b3b9496..9fd7fc954a 100644 --- a/docker/complement/conf/workers-shared-extra.yaml.j2 +++ b/docker/complement/conf/workers-shared-extra.yaml.j2 @@ -141,6 +141,8 @@ experimental_features: msc4306_enabled: true # Sticky Events msc4354_enabled: true + # `/sync` `state_after` + msc4222_enabled: true server_notices: system_mxid_localpart: _server diff --git a/scripts-dev/complement.sh b/scripts-dev/complement.sh index 9a18a621ee..fec005fdb1 100755 --- a/scripts-dev/complement.sh +++ b/scripts-dev/complement.sh @@ -264,6 +264,7 @@ main() { ./tests/msc4140 ./tests/msc4155 ./tests/msc4306 + ./tests/msc4222 ) # Export the list of test packages as a space-separated environment variable, so other diff --git a/synapse/handlers/sync.py b/synapse/handlers/sync.py index 2f405004de..a32748c2a9 100644 --- a/synapse/handlers/sync.py +++ b/synapse/handlers/sync.py @@ -1046,9 +1046,18 @@ class SyncHandler: if event.sender not in first_event_by_sender_map: first_event_by_sender_map[event.sender] = event - # We need the event's sender, unless their membership was in a - # previous timeline event. - if (EventTypes.Member, event.sender) not in timeline_state: + # When using `state_after`, there is no special treatment with + # regards to state also being in the `timeline`. Always fetch + # relevant membership regardless of whether the state event is in + # the `timeline`. + if sync_config.use_state_after: + members_to_fetch.add(event.sender) + # For `state`, the client is supposed to do a flawed re-construction + # of state over time by starting with the given `state` and layering + # on state from the `timeline` as you go (flawed because state + # resolution). In this case, we only need their membership in + # `state` when their membership isn't already in the `timeline`. + elif (EventTypes.Member, event.sender) not in timeline_state: members_to_fetch.add(event.sender) # FIXME: we also care about invite targets etc.