matrix-synapse 1.139.2__cp39-abi3-manylinux_2_28_aarch64.whl → 1.140.0rc1__cp39-abi3-manylinux_2_28_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of matrix-synapse might be problematic. Click here for more details.

Files changed (158) hide show
  1. {matrix_synapse-1.139.2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/METADATA +5 -3
  2. {matrix_synapse-1.139.2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/RECORD +157 -154
  3. synapse/_scripts/generate_workers_map.py +6 -1
  4. synapse/_scripts/synapse_port_db.py +0 -2
  5. synapse/_scripts/update_synapse_database.py +1 -6
  6. synapse/api/auth/base.py +1 -3
  7. synapse/api/auth/mas.py +6 -8
  8. synapse/api/auth/msc3861_delegated.py +6 -8
  9. synapse/api/errors.py +3 -0
  10. synapse/app/_base.py +101 -39
  11. synapse/app/admin_cmd.py +2 -4
  12. synapse/app/appservice.py +1 -1
  13. synapse/app/client_reader.py +1 -1
  14. synapse/app/event_creator.py +1 -1
  15. synapse/app/federation_reader.py +1 -1
  16. synapse/app/federation_sender.py +1 -1
  17. synapse/app/frontend_proxy.py +1 -1
  18. synapse/app/generic_worker.py +17 -11
  19. synapse/app/homeserver.py +85 -47
  20. synapse/app/media_repository.py +1 -1
  21. synapse/app/phone_stats_home.py +16 -14
  22. synapse/app/pusher.py +1 -1
  23. synapse/app/synchrotron.py +1 -1
  24. synapse/app/user_dir.py +1 -1
  25. synapse/appservice/__init__.py +29 -2
  26. synapse/appservice/scheduler.py +8 -8
  27. synapse/config/_base.py +32 -14
  28. synapse/config/_base.pyi +5 -3
  29. synapse/config/experimental.py +3 -0
  30. synapse/config/homeserver.py +27 -1
  31. synapse/config/logger.py +3 -4
  32. synapse/config/matrixrtc.py +67 -0
  33. synapse/crypto/keyring.py +18 -4
  34. synapse/events/auto_accept_invites.py +0 -1
  35. synapse/federation/federation_client.py +39 -0
  36. synapse/federation/federation_server.py +1 -1
  37. synapse/federation/send_queue.py +3 -0
  38. synapse/federation/sender/__init__.py +24 -8
  39. synapse/federation/sender/per_destination_queue.py +31 -8
  40. synapse/federation/sender/transaction_manager.py +12 -0
  41. synapse/federation/transport/client.py +29 -0
  42. synapse/handlers/account_validity.py +2 -4
  43. synapse/handlers/appservice.py +5 -7
  44. synapse/handlers/deactivate_account.py +2 -3
  45. synapse/handlers/delayed_events.py +10 -13
  46. synapse/handlers/device.py +14 -14
  47. synapse/handlers/e2e_keys.py +4 -3
  48. synapse/handlers/federation.py +7 -11
  49. synapse/handlers/federation_event.py +5 -6
  50. synapse/handlers/message.py +16 -10
  51. synapse/handlers/pagination.py +3 -7
  52. synapse/handlers/presence.py +21 -25
  53. synapse/handlers/profile.py +1 -1
  54. synapse/handlers/read_marker.py +3 -1
  55. synapse/handlers/register.py +8 -1
  56. synapse/handlers/room.py +13 -4
  57. synapse/handlers/room_member.py +11 -7
  58. synapse/handlers/room_policy.py +96 -2
  59. synapse/handlers/sso.py +1 -1
  60. synapse/handlers/stats.py +5 -3
  61. synapse/handlers/sync.py +20 -13
  62. synapse/handlers/typing.py +5 -10
  63. synapse/handlers/user_directory.py +12 -11
  64. synapse/handlers/worker_lock.py +19 -15
  65. synapse/http/client.py +18 -13
  66. synapse/http/federation/matrix_federation_agent.py +6 -1
  67. synapse/http/federation/well_known_resolver.py +3 -1
  68. synapse/http/matrixfederationclient.py +50 -11
  69. synapse/http/proxy.py +2 -2
  70. synapse/http/server.py +36 -2
  71. synapse/http/site.py +109 -17
  72. synapse/logging/context.py +165 -63
  73. synapse/logging/opentracing.py +30 -6
  74. synapse/logging/scopecontextmanager.py +161 -0
  75. synapse/media/_base.py +2 -1
  76. synapse/media/media_repository.py +20 -6
  77. synapse/media/url_previewer.py +5 -6
  78. synapse/metrics/_gc.py +3 -1
  79. synapse/metrics/background_process_metrics.py +128 -24
  80. synapse/metrics/common_usage_metrics.py +3 -5
  81. synapse/module_api/__init__.py +42 -5
  82. synapse/notifier.py +10 -3
  83. synapse/push/emailpusher.py +5 -4
  84. synapse/push/httppusher.py +6 -6
  85. synapse/push/pusherpool.py +3 -8
  86. synapse/replication/http/devices.py +0 -41
  87. synapse/replication/tcp/client.py +8 -5
  88. synapse/replication/tcp/handler.py +2 -3
  89. synapse/replication/tcp/protocol.py +14 -7
  90. synapse/replication/tcp/redis.py +16 -11
  91. synapse/replication/tcp/resource.py +5 -4
  92. synapse/replication/tcp/streams/__init__.py +2 -0
  93. synapse/res/providers.json +6 -5
  94. synapse/rest/__init__.py +2 -0
  95. synapse/rest/admin/__init__.py +4 -0
  96. synapse/rest/admin/events.py +69 -0
  97. synapse/rest/admin/media.py +70 -2
  98. synapse/rest/client/matrixrtc.py +52 -0
  99. synapse/rest/client/push_rule.py +1 -1
  100. synapse/rest/client/room.py +2 -3
  101. synapse/rest/client/sync.py +1 -0
  102. synapse/rest/client/transactions.py +1 -1
  103. synapse/server.py +271 -38
  104. synapse/server_notices/server_notices_manager.py +1 -0
  105. synapse/state/__init__.py +4 -1
  106. synapse/storage/_base.py +1 -1
  107. synapse/storage/background_updates.py +8 -3
  108. synapse/storage/controllers/persist_events.py +4 -3
  109. synapse/storage/controllers/purge_events.py +2 -3
  110. synapse/storage/controllers/state.py +5 -5
  111. synapse/storage/database.py +12 -7
  112. synapse/storage/databases/main/__init__.py +7 -2
  113. synapse/storage/databases/main/cache.py +4 -3
  114. synapse/storage/databases/main/censor_events.py +1 -1
  115. synapse/storage/databases/main/client_ips.py +9 -8
  116. synapse/storage/databases/main/deviceinbox.py +7 -6
  117. synapse/storage/databases/main/devices.py +4 -4
  118. synapse/storage/databases/main/end_to_end_keys.py +6 -3
  119. synapse/storage/databases/main/event_federation.py +7 -6
  120. synapse/storage/databases/main/event_push_actions.py +13 -13
  121. synapse/storage/databases/main/events_bg_updates.py +1 -1
  122. synapse/storage/databases/main/events_worker.py +6 -8
  123. synapse/storage/databases/main/lock.py +17 -13
  124. synapse/storage/databases/main/media_repository.py +2 -2
  125. synapse/storage/databases/main/metrics.py +6 -6
  126. synapse/storage/databases/main/monthly_active_users.py +3 -4
  127. synapse/storage/databases/main/receipts.py +1 -1
  128. synapse/storage/databases/main/registration.py +18 -19
  129. synapse/storage/databases/main/roommember.py +1 -1
  130. synapse/storage/databases/main/session.py +3 -3
  131. synapse/storage/databases/main/sliding_sync.py +2 -2
  132. synapse/storage/databases/main/transactions.py +3 -3
  133. synapse/storage/databases/state/store.py +2 -0
  134. synapse/synapse_rust/http_client.pyi +4 -0
  135. synapse/synapse_rust.abi3.so +0 -0
  136. synapse/util/async_helpers.py +36 -24
  137. synapse/util/batching_queue.py +16 -6
  138. synapse/util/caches/__init__.py +1 -1
  139. synapse/util/caches/deferred_cache.py +4 -0
  140. synapse/util/caches/descriptors.py +14 -2
  141. synapse/util/caches/dictionary_cache.py +6 -1
  142. synapse/util/caches/expiringcache.py +16 -5
  143. synapse/util/caches/lrucache.py +14 -26
  144. synapse/util/caches/response_cache.py +11 -1
  145. synapse/util/clock.py +215 -39
  146. synapse/util/constants.py +2 -0
  147. synapse/util/daemonize.py +5 -1
  148. synapse/util/distributor.py +9 -5
  149. synapse/util/metrics.py +35 -6
  150. synapse/util/ratelimitutils.py +4 -1
  151. synapse/util/retryutils.py +7 -4
  152. synapse/util/task_scheduler.py +11 -14
  153. synapse/logging/filter.py +0 -38
  154. {matrix_synapse-1.139.2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/AUTHORS.rst +0 -0
  155. {matrix_synapse-1.139.2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/LICENSE-AGPL-3.0 +0 -0
  156. {matrix_synapse-1.139.2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/LICENSE-COMMERCIAL +0 -0
  157. {matrix_synapse-1.139.2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/WHEEL +0 -0
  158. {matrix_synapse-1.139.2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/entry_points.txt +0 -0
@@ -18,7 +18,8 @@
18
18
  # [This file includes modifications made by New Vector Limited]
19
19
  #
20
20
  #
21
- from ._base import RootConfig
21
+
22
+ from ._base import ConfigError, RootConfig
22
23
  from .account_validity import AccountValidityConfig
23
24
  from .api import ApiConfig
24
25
  from .appservice import AppServiceConfig
@@ -37,6 +38,7 @@ from .jwt import JWTConfig
37
38
  from .key import KeyConfig
38
39
  from .logger import LoggingConfig
39
40
  from .mas import MasConfig
41
+ from .matrixrtc import MatrixRtcConfig
40
42
  from .metrics import MetricsConfig
41
43
  from .modules import ModulesConfig
42
44
  from .oembed import OembedConfig
@@ -66,6 +68,10 @@ from .workers import WorkerConfig
66
68
 
67
69
 
68
70
  class HomeServerConfig(RootConfig):
71
+ """
72
+ Top-level config object for Synapse homeserver (main process and workers).
73
+ """
74
+
69
75
  config_classes = [
70
76
  ModulesConfig,
71
77
  ServerConfig,
@@ -80,6 +86,7 @@ class HomeServerConfig(RootConfig):
80
86
  OembedConfig,
81
87
  CaptchaConfig,
82
88
  VoipConfig,
89
+ MatrixRtcConfig,
83
90
  RegistrationConfig,
84
91
  AccountValidityConfig,
85
92
  MetricsConfig,
@@ -113,3 +120,22 @@ class HomeServerConfig(RootConfig):
113
120
  # This must be last, as it checks for conflicts with other config options.
114
121
  MasConfig,
115
122
  ]
123
+
124
+ def validate_config(
125
+ self,
126
+ ) -> None:
127
+ if (
128
+ self.registration.enable_registration
129
+ and not self.registration.enable_registration_without_verification
130
+ ):
131
+ if (
132
+ not self.captcha.enable_registration_captcha
133
+ and not self.registration.registrations_require_3pid
134
+ and not self.registration.registration_requires_token
135
+ ):
136
+ raise ConfigError(
137
+ "You have enabled open registration without any verification. This is a known vector for "
138
+ "spam and abuse. If you would like to allow public registration, please consider adding email, "
139
+ "captcha, or token-based verification. Otherwise this check can be removed by setting the "
140
+ "`enable_registration_without_verification` config option to `true`."
141
+ )
synapse/config/logger.py CHANGED
@@ -40,7 +40,6 @@ from twisted.logger import (
40
40
  )
41
41
 
42
42
  from synapse.logging.context import LoggingContextFilter
43
- from synapse.logging.filter import MetadataFilter
44
43
  from synapse.synapse_rust import reset_logging_config
45
44
  from synapse.types import JsonDict
46
45
 
@@ -213,13 +212,11 @@ def _setup_stdlib_logging(
213
212
  # writes.
214
213
 
215
214
  log_context_filter = LoggingContextFilter()
216
- log_metadata_filter = MetadataFilter({"server_name": config.server.server_name})
217
215
  old_factory = logging.getLogRecordFactory()
218
216
 
219
217
  def factory(*args: Any, **kwargs: Any) -> logging.LogRecord:
220
218
  record = old_factory(*args, **kwargs)
221
219
  log_context_filter.filter(record)
222
- log_metadata_filter.filter(record)
223
220
  return record
224
221
 
225
222
  logging.setLogRecordFactory(factory)
@@ -348,7 +345,9 @@ def setup_logging(
348
345
  # Add a SIGHUP handler to reload the logging configuration, if one is available.
349
346
  from synapse.app import _base as appbase
350
347
 
351
- appbase.register_sighup(_reload_logging_config, log_config_path)
348
+ appbase.register_sighup(
349
+ hs.get_instance_id(), _reload_logging_config, log_config_path
350
+ )
352
351
 
353
352
  # Log immediately so we can grep backwards.
354
353
  logger.warning("***** STARTING SERVER *****")
@@ -0,0 +1,67 @@
1
+ #
2
+ # This file is licensed under the Affero General Public License (AGPL) version 3.
3
+ #
4
+ # Copyright (C) 2025 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
+ # [This file includes modifications made by New Vector Limited]
15
+ #
16
+ #
17
+
18
+ from typing import Any, Optional
19
+
20
+ from pydantic import ValidationError
21
+
22
+ from synapse._pydantic_compat import Field, StrictStr, validator
23
+ from synapse.types import JsonDict
24
+ from synapse.util.pydantic_models import ParseModel
25
+
26
+ from ._base import Config, ConfigError
27
+
28
+
29
+ class TransportConfigModel(ParseModel):
30
+ type: StrictStr
31
+
32
+ livekit_service_url: Optional[StrictStr] = Field(default=None)
33
+ """An optional livekit service URL. Only required if type is "livekit"."""
34
+
35
+ @validator("livekit_service_url", always=True)
36
+ def validate_livekit_service_url(cls, v: Any, values: dict) -> Any:
37
+ if values.get("type") == "livekit" and not v:
38
+ raise ValueError(
39
+ "You must set a `livekit_service_url` when using the 'livekit' transport."
40
+ )
41
+
42
+ return v
43
+
44
+
45
+ class MatrixRtcConfigModel(ParseModel):
46
+ transports: list = []
47
+
48
+
49
+ class MatrixRtcConfig(Config):
50
+ section = "matrix_rtc"
51
+
52
+ def read_config(
53
+ self, config: JsonDict, allow_secrets_in_config: bool, **kwargs: Any
54
+ ) -> None:
55
+ matrix_rtc = config.get("matrix_rtc", {})
56
+ if matrix_rtc is None:
57
+ matrix_rtc = {}
58
+
59
+ try:
60
+ parsed = MatrixRtcConfigModel(**matrix_rtc)
61
+ except ValidationError as e:
62
+ raise ConfigError(
63
+ "Could not validate matrix_rtc config",
64
+ ("matrix_rtc",),
65
+ ) from e
66
+
67
+ self.transports = parsed.transports
synapse/crypto/keyring.py CHANGED
@@ -172,7 +172,7 @@ class Keyring:
172
172
  _FetchKeyRequest, Dict[str, Dict[str, FetchKeyResult]]
173
173
  ] = BatchingQueue(
174
174
  name="keyring_server",
175
- server_name=self.server_name,
175
+ hs=hs,
176
176
  clock=hs.get_clock(),
177
177
  # The method called to fetch each key
178
178
  process_batch_callback=self._inner_fetch_key_requests,
@@ -194,6 +194,14 @@ class Keyring:
194
194
  valid_until_ts=2**63, # fake future timestamp
195
195
  )
196
196
 
197
+ def shutdown(self) -> None:
198
+ """
199
+ Prepares the KeyRing for garbage collection by shutting down it's queues.
200
+ """
201
+ self._fetch_keys_queue.shutdown()
202
+ for key_fetcher in self._key_fetchers:
203
+ key_fetcher.shutdown()
204
+
197
205
  async def verify_json_for_server(
198
206
  self,
199
207
  server_name: str,
@@ -316,7 +324,7 @@ class Keyring:
316
324
  if key_result.valid_until_ts < verify_request.minimum_valid_until_ts:
317
325
  continue
318
326
 
319
- await self._process_json(key_result.verify_key, verify_request)
327
+ await self.process_json(key_result.verify_key, verify_request)
320
328
  verified = True
321
329
 
322
330
  if not verified:
@@ -326,7 +334,7 @@ class Keyring:
326
334
  Codes.UNAUTHORIZED,
327
335
  )
328
336
 
329
- async def _process_json(
337
+ async def process_json(
330
338
  self, verify_key: VerifyKey, verify_request: VerifyJsonRequest
331
339
  ) -> None:
332
340
  """Processes the `VerifyJsonRequest`. Raises if the signature can't be
@@ -479,11 +487,17 @@ class KeyFetcher(metaclass=abc.ABCMeta):
479
487
  self.server_name = hs.hostname
480
488
  self._queue = BatchingQueue(
481
489
  name=self.__class__.__name__,
482
- server_name=self.server_name,
490
+ hs=hs,
483
491
  clock=hs.get_clock(),
484
492
  process_batch_callback=self._fetch_keys,
485
493
  )
486
494
 
495
+ def shutdown(self) -> None:
496
+ """
497
+ Prepares the KeyFetcher for garbage collection by shutting down it's queue.
498
+ """
499
+ self._queue.shutdown()
500
+
487
501
  async def get_keys(
488
502
  self, server_name: str, key_ids: List[str], minimum_valid_until_ts: int
489
503
  ) -> Dict[str, FetchKeyResult]:
@@ -119,7 +119,6 @@ class InviteAutoAccepter:
119
119
  event.state_key,
120
120
  event.room_id,
121
121
  "join",
122
- bg_start_span=False,
123
122
  )
124
123
 
125
124
  if is_direct_message:
@@ -148,6 +148,7 @@ class FederationClient(FederationBase):
148
148
  self._get_pdu_cache: ExpiringCache[str, Tuple[EventBase, str]] = ExpiringCache(
149
149
  cache_name="get_pdu_cache",
150
150
  server_name=self.server_name,
151
+ hs=self.hs,
151
152
  clock=self._clock,
152
153
  max_len=1000,
153
154
  expiry_ms=120 * 1000,
@@ -167,6 +168,7 @@ class FederationClient(FederationBase):
167
168
  ] = ExpiringCache(
168
169
  cache_name="get_room_hierarchy_cache",
169
170
  server_name=self.server_name,
171
+ hs=self.hs,
170
172
  clock=self._clock,
171
173
  max_len=1000,
172
174
  expiry_ms=5 * 60 * 1000,
@@ -495,6 +497,43 @@ class FederationClient(FederationBase):
495
497
  )
496
498
  return RECOMMENDATION_OK
497
499
 
500
+ @trace
501
+ @tag_args
502
+ async def ask_policy_server_to_sign_event(
503
+ self, destination: str, pdu: EventBase, timeout: Optional[int] = None
504
+ ) -> Optional[JsonDict]:
505
+ """Requests that the destination server (typically a policy server)
506
+ sign the event as not spam.
507
+
508
+ If the policy server could not be contacted or the policy server
509
+ returned an error, this returns no signature.
510
+
511
+ Args:
512
+ destination: The remote homeserver to ask (a policy server)
513
+ pdu: The event to sign
514
+ timeout: How long to try (in ms) the destination for before
515
+ giving up. None indicates no timeout.
516
+ Returns:
517
+ The signature from the policy server, structured in the same was as the 'signatures'
518
+ JSON in the event e.g { "$policy_server_via_domain" : { "ed25519:policy_server": "signature_base64" }}
519
+ """
520
+ logger.debug(
521
+ "ask_policy_server_to_sign_event for event_id=%s from %s",
522
+ pdu.event_id,
523
+ destination,
524
+ )
525
+ try:
526
+ return await self.transport_layer.ask_policy_server_to_sign_event(
527
+ destination, pdu, timeout=timeout
528
+ )
529
+ except Exception as e:
530
+ logger.warning(
531
+ "ask_policy_server_to_sign_event: server %s responded with error: %s",
532
+ destination,
533
+ e,
534
+ )
535
+ return None
536
+
498
537
  @trace
499
538
  @tag_args
500
539
  async def get_pdu(
@@ -159,7 +159,7 @@ class FederationServer(FederationBase):
159
159
  # with FederationHandlerRegistry.
160
160
  hs.get_directory_handler()
161
161
 
162
- self._server_linearizer = Linearizer("fed_server")
162
+ self._server_linearizer = Linearizer(name="fed_server", clock=hs.get_clock())
163
163
 
164
164
  # origins that we are currently processing a transaction from.
165
165
  # a dict from origin to txn id.
@@ -144,6 +144,9 @@ class FederationRemoteSendQueue(AbstractFederationSender):
144
144
 
145
145
  self.clock.looping_call(self._clear_queue, 30 * 1000)
146
146
 
147
+ def shutdown(self) -> None:
148
+ """Stops this federation sender instance from sending further transactions."""
149
+
147
150
  def _next_pos(self) -> int:
148
151
  pos = self.pos
149
152
  self.pos += 1
@@ -168,7 +168,6 @@ from synapse.metrics import (
168
168
  events_processed_counter,
169
169
  )
170
170
  from synapse.metrics.background_process_metrics import (
171
- run_as_background_process,
172
171
  wrap_as_background_process,
173
172
  )
174
173
  from synapse.types import (
@@ -232,6 +231,11 @@ WAKEUP_INTERVAL_BETWEEN_DESTINATIONS_SEC = 5
232
231
 
233
232
 
234
233
  class AbstractFederationSender(metaclass=abc.ABCMeta):
234
+ @abc.abstractmethod
235
+ def shutdown(self) -> None:
236
+ """Stops this federation sender instance from sending further transactions."""
237
+ raise NotImplementedError()
238
+
235
239
  @abc.abstractmethod
236
240
  def notify_new_events(self, max_token: RoomStreamToken) -> None:
237
241
  """This gets called when we have some new events we might want to
@@ -326,6 +330,7 @@ class _DestinationWakeupQueue:
326
330
  _MAX_TIME_IN_QUEUE = 30.0
327
331
 
328
332
  sender: "FederationSender" = attr.ib()
333
+ hs: "HomeServer" = attr.ib()
329
334
  server_name: str = attr.ib()
330
335
  """
331
336
  Our homeserver name (used to label metrics) (`hs.hostname`).
@@ -453,18 +458,30 @@ class FederationSender(AbstractFederationSender):
453
458
  1.0 / hs.config.ratelimiting.federation_rr_transactions_per_room_per_second
454
459
  )
455
460
  self._destination_wakeup_queue = _DestinationWakeupQueue(
456
- self, self.server_name, self.clock, max_delay_s=rr_txn_interval_per_room_s
461
+ self,
462
+ hs,
463
+ self.server_name,
464
+ self.clock,
465
+ max_delay_s=rr_txn_interval_per_room_s,
457
466
  )
458
467
 
468
+ # It is important for `_is_shutdown` to be instantiated before the looping call
469
+ # for `wake_destinations_needing_catchup`.
470
+ self._is_shutdown = False
471
+
459
472
  # Regularly wake up destinations that have outstanding PDUs to be caught up
460
473
  self.clock.looping_call_now(
461
- run_as_background_process,
474
+ self.hs.run_as_background_process,
462
475
  WAKEUP_RETRY_PERIOD_SEC * 1000.0,
463
476
  "wake_destinations_needing_catchup",
464
- self.server_name,
465
477
  self._wake_destinations_needing_catchup,
466
478
  )
467
479
 
480
+ def shutdown(self) -> None:
481
+ self._is_shutdown = True
482
+ for queue in self._per_destination_queues.values():
483
+ queue.shutdown()
484
+
468
485
  def _get_per_destination_queue(
469
486
  self, destination: str
470
487
  ) -> Optional[PerDestinationQueue]:
@@ -503,16 +520,15 @@ class FederationSender(AbstractFederationSender):
503
520
  return
504
521
 
505
522
  # fire off a processing loop in the background
506
- run_as_background_process(
523
+ self.hs.run_as_background_process(
507
524
  "process_event_queue_for_federation",
508
- self.server_name,
509
525
  self._process_event_queue_loop,
510
526
  )
511
527
 
512
528
  async def _process_event_queue_loop(self) -> None:
513
529
  try:
514
530
  self._is_processing = True
515
- while True:
531
+ while not self._is_shutdown:
516
532
  last_token = await self.store.get_federation_out_pos("events")
517
533
  (
518
534
  next_token,
@@ -1123,7 +1139,7 @@ class FederationSender(AbstractFederationSender):
1123
1139
 
1124
1140
  last_processed: Optional[str] = None
1125
1141
 
1126
- while True:
1142
+ while not self._is_shutdown:
1127
1143
  destinations_to_wake = (
1128
1144
  await self.store.get_catch_up_outstanding_destinations(last_processed)
1129
1145
  )
@@ -28,6 +28,8 @@ from typing import TYPE_CHECKING, Dict, Hashable, Iterable, List, Optional, Tupl
28
28
  import attr
29
29
  from prometheus_client import Counter
30
30
 
31
+ from twisted.internet import defer
32
+
31
33
  from synapse.api.constants import EduTypes
32
34
  from synapse.api.errors import (
33
35
  FederationDeniedError,
@@ -41,7 +43,6 @@ from synapse.handlers.presence import format_user_presence_state
41
43
  from synapse.logging import issue9533_logger
42
44
  from synapse.logging.opentracing import SynapseTags, set_tag
43
45
  from synapse.metrics import SERVER_NAME_LABEL, sent_transactions_counter
44
- from synapse.metrics.background_process_metrics import run_as_background_process
45
46
  from synapse.types import JsonDict, ReadReceipt
46
47
  from synapse.util.retryutils import NotRetryingDestination, get_retry_limiter
47
48
  from synapse.visibility import filter_events_for_server
@@ -79,6 +80,7 @@ MAX_PRESENCE_STATES_PER_EDU = 50
79
80
  class PerDestinationQueue:
80
81
  """
81
82
  Manages the per-destination transmission queues.
83
+ Runs until `shutdown()` is called on the queue.
82
84
 
83
85
  Args:
84
86
  hs
@@ -94,6 +96,7 @@ class PerDestinationQueue:
94
96
  destination: str,
95
97
  ):
96
98
  self.server_name = hs.hostname
99
+ self._hs = hs
97
100
  self._clock = hs.get_clock()
98
101
  self._storage_controllers = hs.get_storage_controllers()
99
102
  self._store = hs.get_datastores().main
@@ -117,6 +120,8 @@ class PerDestinationQueue:
117
120
 
118
121
  self._destination = destination
119
122
  self.transmission_loop_running = False
123
+ self._transmission_loop_enabled = True
124
+ self.active_transmission_loop: Optional[defer.Deferred] = None
120
125
 
121
126
  # Flag to signal to any running transmission loop that there is new data
122
127
  # queued up to be sent.
@@ -171,6 +176,20 @@ class PerDestinationQueue:
171
176
  def __str__(self) -> str:
172
177
  return "PerDestinationQueue[%s]" % self._destination
173
178
 
179
+ def shutdown(self) -> None:
180
+ """Instruct the queue to stop processing any further requests"""
181
+ self._transmission_loop_enabled = False
182
+ # The transaction manager must be shutdown before cancelling the active
183
+ # transmission loop. Otherwise the transmission loop can enter a new cycle of
184
+ # sleeping before retrying since the shutdown flag of the _transaction_manager
185
+ # hasn't been set yet.
186
+ self._transaction_manager.shutdown()
187
+ try:
188
+ if self.active_transmission_loop is not None:
189
+ self.active_transmission_loop.cancel()
190
+ except Exception:
191
+ pass
192
+
174
193
  def pending_pdu_count(self) -> int:
175
194
  return len(self._pending_pdus)
176
195
 
@@ -309,11 +328,14 @@ class PerDestinationQueue:
309
328
  )
310
329
  return
311
330
 
331
+ if not self._transmission_loop_enabled:
332
+ logger.warning("Shutdown has been requested. Not sending transaction")
333
+ return
334
+
312
335
  logger.debug("TX [%s] Starting transaction loop", self._destination)
313
336
 
314
- run_as_background_process(
337
+ self.active_transmission_loop = self._hs.run_as_background_process(
315
338
  "federation_transaction_transmission_loop",
316
- self.server_name,
317
339
  self._transaction_transmission_loop,
318
340
  )
319
341
 
@@ -321,13 +343,13 @@ class PerDestinationQueue:
321
343
  pending_pdus: List[EventBase] = []
322
344
  try:
323
345
  self.transmission_loop_running = True
324
-
325
346
  # This will throw if we wouldn't retry. We do this here so we fail
326
347
  # quickly, but we will later check this again in the http client,
327
348
  # hence why we throw the result away.
328
349
  await get_retry_limiter(
329
350
  destination=self._destination,
330
351
  our_server_name=self.server_name,
352
+ hs=self._hs,
331
353
  clock=self._clock,
332
354
  store=self._store,
333
355
  )
@@ -339,7 +361,7 @@ class PerDestinationQueue:
339
361
  # not caught up yet
340
362
  return
341
363
 
342
- while True:
364
+ while self._transmission_loop_enabled:
343
365
  self._new_data_to_send = False
344
366
 
345
367
  async with _TransactionQueueManager(self) as (
@@ -352,8 +374,8 @@ class PerDestinationQueue:
352
374
  # If we've gotten told about new things to send during
353
375
  # checking for things to send, we try looking again.
354
376
  # Otherwise new PDUs or EDUs might arrive in the meantime,
355
- # but not get sent because we hold the
356
- # `transmission_loop_running` flag.
377
+ # but not get sent because we currently have an
378
+ # `_active_transmission_loop` running.
357
379
  if self._new_data_to_send:
358
380
  continue
359
381
  else:
@@ -442,6 +464,7 @@ class PerDestinationQueue:
442
464
  )
443
465
  finally:
444
466
  # We want to be *very* sure we clear this after we stop processing
467
+ self.active_transmission_loop = None
445
468
  self.transmission_loop_running = False
446
469
 
447
470
  async def _catch_up_transmission_loop(self) -> None:
@@ -469,7 +492,7 @@ class PerDestinationQueue:
469
492
  last_successful_stream_ordering: int = _tmp_last_successful_stream_ordering
470
493
 
471
494
  # get at most 50 catchup room/PDUs
472
- while True:
495
+ while self._transmission_loop_enabled:
473
496
  event_ids = await self._store.get_catch_up_room_event_ids(
474
497
  self._destination, last_successful_stream_ordering
475
498
  )
@@ -72,6 +72,12 @@ class TransactionManager:
72
72
  # HACK to get unique tx id
73
73
  self._next_txn_id = int(self.clock.time_msec())
74
74
 
75
+ self._is_shutdown = False
76
+
77
+ def shutdown(self) -> None:
78
+ self._is_shutdown = True
79
+ self._transport_layer.shutdown()
80
+
75
81
  @measure_func("_send_new_transaction")
76
82
  async def send_new_transaction(
77
83
  self,
@@ -86,6 +92,12 @@ class TransactionManager:
86
92
  edus: List of EDUs to send
87
93
  """
88
94
 
95
+ if self._is_shutdown:
96
+ logger.warning(
97
+ "TransactionManager has been shutdown, not sending transaction"
98
+ )
99
+ return
100
+
89
101
  # Make a transaction-sending opentracing span. This span follows on from
90
102
  # all the edus in that transaction. This needs to be done since there is
91
103
  # no active span here, so if the edus were not received by the remote the
@@ -70,6 +70,9 @@ class TransportLayerClient:
70
70
  self.client = hs.get_federation_http_client()
71
71
  self._is_mine_server_name = hs.is_mine_server_name
72
72
 
73
+ def shutdown(self) -> None:
74
+ self.client.shutdown()
75
+
73
76
  async def get_room_state_ids(
74
77
  self, destination: str, room_id: str, event_id: str
75
78
  ) -> JsonDict:
@@ -170,6 +173,32 @@ class TransportLayerClient:
170
173
  timeout=timeout,
171
174
  )
172
175
 
176
+ async def ask_policy_server_to_sign_event(
177
+ self, destination: str, event: EventBase, timeout: Optional[int] = None
178
+ ) -> JsonDict:
179
+ """Requests that the destination server (typically a policy server)
180
+ sign the event as not spam.
181
+
182
+ If the policy server could not be contacted or the policy server
183
+ returned an error, this raises that error.
184
+
185
+ Args:
186
+ destination: The host name of the policy server / homeserver.
187
+ event: The event to sign.
188
+ timeout: How long to try (in ms) the destination for before giving up.
189
+ None indicates no timeout.
190
+ Returns:
191
+ The signature from the policy server, structured in the same was as the 'signatures'
192
+ JSON in the event e.g { "$policy_server_via_domain" : { "ed25519:policy_server": "signature_base64" }}
193
+ """
194
+ return await self.client.post_json(
195
+ destination=destination,
196
+ path="/_matrix/policy/unstable/org.matrix.msc4284/sign",
197
+ data=event.get_pdu_json(),
198
+ ignore_backoff=True,
199
+ timeout=timeout,
200
+ )
201
+
173
202
  async def backfill(
174
203
  self, destination: str, room_id: str, event_tuples: Collection[str], limit: int
175
204
  ) -> Optional[Union[JsonDict, list]]:
@@ -37,10 +37,8 @@ logger = logging.getLogger(__name__)
37
37
 
38
38
  class AccountValidityHandler:
39
39
  def __init__(self, hs: "HomeServer"):
40
- self.hs = hs
41
- self.server_name = (
42
- hs.hostname
43
- ) # nb must be called this for @wrap_as_background_process
40
+ self.hs = hs # nb must be called this for @wrap_as_background_process
41
+ self.server_name = hs.hostname
44
42
  self.config = hs.config
45
43
  self.store = hs.get_datastores().main
46
44
  self.send_email_handler = hs.get_send_email_handler()
@@ -47,7 +47,6 @@ from synapse.metrics import (
47
47
  event_processing_loop_room_count,
48
48
  )
49
49
  from synapse.metrics.background_process_metrics import (
50
- run_as_background_process,
51
50
  wrap_as_background_process,
52
51
  )
53
52
  from synapse.storage.databases.main.directory import RoomAliasMapping
@@ -76,9 +75,8 @@ events_processed_counter = Counter(
76
75
 
77
76
  class ApplicationServicesHandler:
78
77
  def __init__(self, hs: "HomeServer"):
79
- self.server_name = (
80
- hs.hostname
81
- ) # nb must be called this for @wrap_as_background_process
78
+ self.server_name = hs.hostname
79
+ self.hs = hs # nb must be called this for @wrap_as_background_process
82
80
  self.store = hs.get_datastores().main
83
81
  self.is_mine_id = hs.is_mine_id
84
82
  self.appservice_api = hs.get_application_service_api()
@@ -98,7 +96,7 @@ class ApplicationServicesHandler:
98
96
  self.is_processing = False
99
97
 
100
98
  self._ephemeral_events_linearizer = Linearizer(
101
- name="appservice_ephemeral_events"
99
+ name="appservice_ephemeral_events", clock=hs.get_clock()
102
100
  )
103
101
 
104
102
  def notify_interested_services(self, max_token: RoomStreamToken) -> None:
@@ -171,8 +169,8 @@ class ApplicationServicesHandler:
171
169
  except Exception:
172
170
  logger.error("Application Services Failure")
173
171
 
174
- run_as_background_process(
175
- "as_scheduler", self.server_name, start_scheduler
172
+ self.hs.run_as_background_process(
173
+ "as_scheduler", start_scheduler
176
174
  )
177
175
  self.started_scheduler = True
178
176
 
@@ -24,7 +24,6 @@ from typing import TYPE_CHECKING, Optional
24
24
 
25
25
  from synapse.api.constants import Membership
26
26
  from synapse.api.errors import SynapseError
27
- from synapse.metrics.background_process_metrics import run_as_background_process
28
27
  from synapse.replication.http.deactivate_account import (
29
28
  ReplicationNotifyAccountDeactivatedServlet,
30
29
  )
@@ -272,8 +271,8 @@ class DeactivateAccountHandler:
272
271
  pending deactivation, if it isn't already running.
273
272
  """
274
273
  if not self._user_parter_running:
275
- run_as_background_process(
276
- "user_parter_loop", self.server_name, self._user_parter_loop
274
+ self.hs.run_as_background_process(
275
+ "user_parter_loop", self._user_parter_loop
277
276
  )
278
277
 
279
278
  async def _user_parter_loop(self) -> None: