Skip to content

Commit 664f0e8

Browse files
committed
Merge branch 'release-v1.135' into develop
2 parents ea87853 + caf5f01 commit 664f0e8

15 files changed

Lines changed: 247 additions & 58 deletions

File tree

CHANGES.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
# Synapse 1.135.0rc2 (2025-07-30)
2+
3+
### Bugfixes
4+
5+
- Fix user failing to deactivate with MAS when `/_synapse/mas` is handled by a worker. ([\#18716](https://github.com/element-hq/synapse/issues/18716))
6+
7+
### Internal Changes
8+
9+
- Fix performance regression introduced in [#18238](https://github.com/element-hq/synapse/issues/18238) by adding a cache to `is_server_admin`. ([\#18747](https://github.com/element-hq/synapse/issues/18747))
10+
11+
12+
13+
114
# Synapse 1.135.0rc1 (2025-07-22)
215

316
### Features

debian/changelog

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
matrix-synapse-py3 (1.135.0~rc2) stable; urgency=medium
2+
3+
* New Synapse release 1.135.0rc2.
4+
5+
-- Synapse Packaging team <packages@matrix.org> Wed, 30 Jul 2025 12:19:14 +0100
6+
17
matrix-synapse-py3 (1.135.0~rc1) stable; urgency=medium
28

39
* New Synapse release 1.135.0rc1.

docker/configure_workers_and_start.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@
178178
"^/_matrix/client/(api/v1|r0|v3|unstable)/login$",
179179
"^/_matrix/client/(api/v1|r0|v3|unstable)/account/3pid$",
180180
"^/_matrix/client/(api/v1|r0|v3|unstable)/account/whoami$",
181+
"^/_matrix/client/(api/v1|r0|v3|unstable)/account/deactivate$",
181182
"^/_matrix/client/(api/v1|r0|v3|unstable)/devices(/|$)",
182183
"^/_matrix/client/(r0|v3)/delete_devices$",
183184
"^/_matrix/client/versions$",

docs/workers.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ information.
238238
^/_matrix/client/unstable/im.nheko.summary/summary/.*$
239239
^/_matrix/client/(r0|v3|unstable)/account/3pid$
240240
^/_matrix/client/(r0|v3|unstable)/account/whoami$
241+
^/_matrix/client/(r0|v3|unstable)/account/deactivate$
241242
^/_matrix/client/(r0|v3)/delete_devices$
242243
^/_matrix/client/(api/v1|r0|v3|unstable)/devices(/|$)
243244
^/_matrix/client/versions$

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ module-name = "synapse.synapse_rust"
101101

102102
[tool.poetry]
103103
name = "matrix-synapse"
104-
version = "1.135.0rc1"
104+
version = "1.135.0rc2"
105105
description = "Homeserver for the Matrix decentralised comms protocol"
106106
authors = ["Matrix.org Team and Contributors <packages@matrix.org>"]
107107
license = "AGPL-3.0-or-later"

synapse/handlers/auth.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ def __init__(self, hs: "HomeServer"):
222222
self._password_localdb_enabled = hs.config.auth.password_localdb_enabled
223223
self._third_party_rules = hs.get_module_api_callbacks().third_party_event_rules
224224
self._account_validity_handler = hs.get_account_validity_handler()
225+
self._pusher_pool = hs.get_pusherpool()
225226

226227
# Ratelimiter for failed auth during UIA. Uses same ratelimit config
227228
# as per `rc_login.failed_attempts`.
@@ -1662,7 +1663,7 @@ async def delete_local_threepid(
16621663
)
16631664

16641665
if medium == "email":
1665-
await self.store.delete_pusher_by_app_id_pushkey_user_id(
1666+
await self._pusher_pool.remove_pusher(
16661667
app_id="m.email", pushkey=address, user_id=user_id
16671668
)
16681669

synapse/handlers/deactivate_account.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
from synapse.api.constants import Membership
2626
from synapse.api.errors import SynapseError
2727
from synapse.metrics.background_process_metrics import run_as_background_process
28+
from synapse.replication.http.deactivate_account import (
29+
ReplicationNotifyAccountDeactivatedServlet,
30+
)
2831
from synapse.types import Codes, Requester, UserID, create_requester
2932

3033
if TYPE_CHECKING:
@@ -45,6 +48,7 @@ def __init__(self, hs: "HomeServer"):
4548
self._room_member_handler = hs.get_room_member_handler()
4649
self._identity_handler = hs.get_identity_handler()
4750
self._profile_handler = hs.get_profile_handler()
51+
self._pusher_pool = hs.get_pusherpool()
4852
self.user_directory_handler = hs.get_user_directory_handler()
4953
self._server_name = hs.hostname
5054
self._third_party_rules = hs.get_module_api_callbacks().third_party_event_rules
@@ -53,10 +57,16 @@ def __init__(self, hs: "HomeServer"):
5357
self._user_parter_running = False
5458
self._third_party_rules = hs.get_module_api_callbacks().third_party_event_rules
5559

60+
self._notify_account_deactivated_client = None
61+
5662
# Start the user parter loop so it can resume parting users from rooms where
5763
# it left off (if it has work left to do).
58-
if hs.config.worker.run_background_tasks:
64+
if hs.config.worker.worker_app is None:
5965
hs.get_reactor().callWhenRunning(self._start_user_parting)
66+
else:
67+
self._notify_account_deactivated_client = (
68+
ReplicationNotifyAccountDeactivatedServlet.make_client(hs)
69+
)
6070

6171
self._account_validity_enabled = (
6272
hs.config.account_validity.account_validity_enabled
@@ -146,7 +156,7 @@ async def deactivate_account(
146156
# Most of the pushers will have been deleted when we logged out the
147157
# associated devices above, but we still need to delete pushers not
148158
# associated with devices, e.g. email pushers.
149-
await self.store.delete_all_pushers_for_user(user_id)
159+
await self._pusher_pool.delete_all_pushers_for_user(user_id)
150160

151161
# Add the user to a table of users pending deactivation (ie.
152162
# removal from all the rooms they're a member of)
@@ -170,10 +180,6 @@ async def deactivate_account(
170180
logger.info("Marking %s as erased", user_id)
171181
await self.store.mark_user_erased(user_id)
172182

173-
# Now start the process that goes through that list and
174-
# parts users from rooms (if it isn't already running)
175-
self._start_user_parting()
176-
177183
# Reject all pending invites and knocks for the user, so that the
178184
# user doesn't show up in the "invited" section of rooms' members list.
179185
await self._reject_pending_invites_and_knocks_for_user(user_id)
@@ -194,15 +200,37 @@ async def deactivate_account(
194200
# Delete any server-side backup keys
195201
await self.store.bulk_delete_backup_keys_and_versions_for_user(user_id)
196202

203+
# Notify modules and start the room parting process.
204+
await self.notify_account_deactivated(user_id, by_admin=by_admin)
205+
206+
return identity_server_supports_unbinding
207+
208+
async def notify_account_deactivated(
209+
self,
210+
user_id: str,
211+
by_admin: bool = False,
212+
) -> None:
213+
"""Notify modules and start the room parting process.
214+
Goes through replication if this is not the main process.
215+
"""
216+
if self._notify_account_deactivated_client is not None:
217+
await self._notify_account_deactivated_client(
218+
user_id=user_id,
219+
by_admin=by_admin,
220+
)
221+
return
222+
223+
# Now start the process that goes through that list and
224+
# parts users from rooms (if it isn't already running)
225+
self._start_user_parting()
226+
197227
# Let modules know the user has been deactivated.
198228
await self._third_party_rules.on_user_deactivation_status_changed(
199229
user_id,
200230
True,
201-
by_admin,
231+
by_admin=by_admin,
202232
)
203233

204-
return identity_server_supports_unbinding
205-
206234
async def _reject_pending_invites_and_knocks_for_user(self, user_id: str) -> None:
207235
"""Reject pending invites and knocks addressed to a given user ID.
208236

synapse/push/pusherpool.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@
3232
)
3333
from synapse.push import Pusher, PusherConfig, PusherConfigException
3434
from synapse.push.pusher import PusherFactory
35-
from synapse.replication.http.push import ReplicationRemovePusherRestServlet
35+
from synapse.replication.http.push import (
36+
ReplicationDeleteAllPushersForUserRestServlet,
37+
ReplicationRemovePusherRestServlet,
38+
)
3639
from synapse.types import JsonDict, RoomStreamToken, StrCollection
3740
from synapse.util.async_helpers import concurrently_execute
3841
from synapse.util.threepids import canonicalise_email
@@ -84,10 +87,14 @@ def __init__(self, hs: "HomeServer"):
8487

8588
# We can only delete pushers on master.
8689
self._remove_pusher_client = None
90+
self._delete_all_pushers_for_user_client = None
8791
if hs.config.worker.worker_app:
8892
self._remove_pusher_client = ReplicationRemovePusherRestServlet.make_client(
8993
hs
9094
)
95+
self._delete_all_pushers_for_user_client = (
96+
ReplicationDeleteAllPushersForUserRestServlet.make_client(hs)
97+
)
9198

9299
# Record the last stream ID that we were poked about so we can get
93100
# changes since then. We set this to the current max stream ID on
@@ -468,6 +475,13 @@ async def remove_pusher(self, app_id: str, pushkey: str, user_id: str) -> None:
468475
app_id, pushkey, user_id
469476
)
470477

478+
async def delete_all_pushers_for_user(self, user_id: str) -> None:
479+
"""Deletes all pushers for a user."""
480+
if self._delete_all_pushers_for_user_client is not None:
481+
await self._delete_all_pushers_for_user_client(user_id=user_id)
482+
else:
483+
await self.store.delete_all_pushers_for_user(user_id=user_id)
484+
471485
def maybe_stop_pusher(self, app_id: str, pushkey: str, user_id: str) -> None:
472486
"""Stops a pusher with the given app ID and push key if one is running.
473487

synapse/replication/http/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from synapse.http.server import JsonResource
2424
from synapse.replication.http import (
2525
account_data,
26+
deactivate_account,
2627
delayed_events,
2728
devices,
2829
federation,
@@ -64,3 +65,4 @@ def register_servlets(self, hs: "HomeServer") -> None:
6465
login.register_servlets(hs, self)
6566
register.register_servlets(hs, self)
6667
delayed_events.register_servlets(hs, self)
68+
deactivate_account.register_servlets(hs, self)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#
2+
# This file is licensed under the Affero General Public License (AGPL) version 3.
3+
#
4+
# Copyright (C) 2023 New Vector, Ltd
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU Affero General Public License as
8+
# published by the Free Software Foundation, either version 3 of the
9+
# License, or (at your option) any later version.
10+
#
11+
# See the GNU Affero General Public License for more details:
12+
# <https://www.gnu.org/licenses/agpl-3.0.html>.
13+
#
14+
# Originally licensed under the Apache License, Version 2.0:
15+
# <http://www.apache.org/licenses/LICENSE-2.0>.
16+
#
17+
# [This file includes modifications made by New Vector Limited]
18+
#
19+
#
20+
21+
import logging
22+
from typing import TYPE_CHECKING, Tuple
23+
24+
from twisted.web.server import Request
25+
26+
from synapse.http.server import HttpServer
27+
from synapse.replication.http._base import ReplicationEndpoint
28+
from synapse.types import JsonDict
29+
30+
if TYPE_CHECKING:
31+
from synapse.server import HomeServer
32+
33+
logger = logging.getLogger(__name__)
34+
35+
36+
class ReplicationNotifyAccountDeactivatedServlet(ReplicationEndpoint):
37+
"""Notify that an account has been deactivated.
38+
39+
Request format:
40+
41+
POST /_synapse/replication/notify_account_deactivated/:user_id
42+
43+
{
44+
"by_admin": true,
45+
}
46+
47+
"""
48+
49+
NAME = "notify_account_deactivated"
50+
PATH_ARGS = ("user_id",)
51+
52+
def __init__(self, hs: "HomeServer"):
53+
super().__init__(hs)
54+
self.deactivate_account_handler = hs.get_deactivate_account_handler()
55+
56+
@staticmethod
57+
async def _serialize_payload( # type: ignore[override]
58+
user_id: str,
59+
by_admin: bool,
60+
) -> JsonDict:
61+
"""
62+
Args:
63+
user_id: The user ID which has been deactivated.
64+
by_admin: Whether the user was deactivated by an admin.
65+
"""
66+
return {
67+
"by_admin": by_admin,
68+
}
69+
70+
async def _handle_request( # type: ignore[override]
71+
self, request: Request, content: JsonDict, user_id: str
72+
) -> Tuple[int, JsonDict]:
73+
by_admin = content["by_admin"]
74+
await self.deactivate_account_handler.notify_account_deactivated(
75+
user_id, by_admin=by_admin
76+
)
77+
return 200, {}
78+
79+
80+
def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None:
81+
ReplicationNotifyAccountDeactivatedServlet(hs).register(http_server)

0 commit comments

Comments
 (0)