matrix-synapse 1.139.0rc2__cp39-abi3-musllinux_1_2_aarch64.whl → 1.140.0rc1__cp39-abi3-musllinux_1_2_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.
- {matrix_synapse-1.139.0rc2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/METADATA +5 -3
- {matrix_synapse-1.139.0rc2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/RECORD +158 -155
- synapse/_scripts/generate_workers_map.py +6 -1
- synapse/_scripts/synapse_port_db.py +0 -2
- synapse/_scripts/update_synapse_database.py +1 -6
- synapse/api/auth/base.py +1 -3
- synapse/api/auth/mas.py +6 -8
- synapse/api/auth/msc3861_delegated.py +6 -8
- synapse/api/errors.py +3 -0
- synapse/app/_base.py +101 -39
- synapse/app/admin_cmd.py +2 -4
- synapse/app/appservice.py +1 -1
- synapse/app/client_reader.py +1 -1
- synapse/app/event_creator.py +1 -1
- synapse/app/federation_reader.py +1 -1
- synapse/app/federation_sender.py +1 -1
- synapse/app/frontend_proxy.py +1 -1
- synapse/app/generic_worker.py +17 -11
- synapse/app/homeserver.py +85 -47
- synapse/app/media_repository.py +1 -1
- synapse/app/phone_stats_home.py +16 -14
- synapse/app/pusher.py +1 -1
- synapse/app/synchrotron.py +1 -1
- synapse/app/user_dir.py +1 -1
- synapse/appservice/__init__.py +29 -2
- synapse/appservice/scheduler.py +8 -8
- synapse/config/_base.py +32 -14
- synapse/config/_base.pyi +5 -3
- synapse/config/experimental.py +3 -0
- synapse/config/homeserver.py +27 -1
- synapse/config/logger.py +3 -4
- synapse/config/matrixrtc.py +67 -0
- synapse/crypto/keyring.py +18 -4
- synapse/events/auto_accept_invites.py +0 -1
- synapse/federation/federation_client.py +39 -0
- synapse/federation/federation_server.py +1 -1
- synapse/federation/send_queue.py +3 -0
- synapse/federation/sender/__init__.py +24 -8
- synapse/federation/sender/per_destination_queue.py +31 -8
- synapse/federation/sender/transaction_manager.py +12 -0
- synapse/federation/transport/client.py +29 -0
- synapse/handlers/account_validity.py +2 -4
- synapse/handlers/appservice.py +5 -7
- synapse/handlers/deactivate_account.py +2 -3
- synapse/handlers/delayed_events.py +10 -13
- synapse/handlers/device.py +14 -14
- synapse/handlers/e2e_keys.py +16 -11
- synapse/handlers/federation.py +7 -11
- synapse/handlers/federation_event.py +5 -6
- synapse/handlers/message.py +16 -10
- synapse/handlers/pagination.py +3 -7
- synapse/handlers/presence.py +21 -25
- synapse/handlers/profile.py +1 -1
- synapse/handlers/read_marker.py +3 -1
- synapse/handlers/register.py +8 -1
- synapse/handlers/room.py +13 -4
- synapse/handlers/room_member.py +11 -7
- synapse/handlers/room_policy.py +96 -2
- synapse/handlers/sso.py +1 -1
- synapse/handlers/stats.py +5 -3
- synapse/handlers/sync.py +20 -13
- synapse/handlers/typing.py +5 -10
- synapse/handlers/user_directory.py +12 -11
- synapse/handlers/worker_lock.py +19 -15
- synapse/http/client.py +18 -13
- synapse/http/federation/matrix_federation_agent.py +6 -1
- synapse/http/federation/well_known_resolver.py +3 -1
- synapse/http/matrixfederationclient.py +50 -11
- synapse/http/proxy.py +2 -2
- synapse/http/server.py +36 -2
- synapse/http/site.py +109 -17
- synapse/logging/context.py +201 -110
- synapse/logging/opentracing.py +30 -6
- synapse/logging/scopecontextmanager.py +161 -0
- synapse/media/_base.py +2 -1
- synapse/media/media_repository.py +20 -6
- synapse/media/url_previewer.py +5 -6
- synapse/metrics/_gc.py +3 -1
- synapse/metrics/background_process_metrics.py +128 -24
- synapse/metrics/common_usage_metrics.py +3 -5
- synapse/module_api/__init__.py +42 -5
- synapse/notifier.py +10 -3
- synapse/push/emailpusher.py +5 -4
- synapse/push/httppusher.py +6 -6
- synapse/push/pusherpool.py +3 -8
- synapse/replication/http/devices.py +0 -41
- synapse/replication/tcp/client.py +8 -5
- synapse/replication/tcp/handler.py +2 -3
- synapse/replication/tcp/protocol.py +14 -7
- synapse/replication/tcp/redis.py +16 -11
- synapse/replication/tcp/resource.py +5 -4
- synapse/replication/tcp/streams/__init__.py +2 -0
- synapse/res/providers.json +6 -5
- synapse/rest/__init__.py +2 -0
- synapse/rest/admin/__init__.py +4 -0
- synapse/rest/admin/events.py +69 -0
- synapse/rest/admin/media.py +70 -2
- synapse/rest/client/keys.py +147 -3
- synapse/rest/client/matrixrtc.py +52 -0
- synapse/rest/client/push_rule.py +1 -1
- synapse/rest/client/room.py +2 -3
- synapse/rest/client/sync.py +1 -3
- synapse/rest/client/transactions.py +1 -1
- synapse/server.py +271 -38
- synapse/server_notices/server_notices_manager.py +1 -0
- synapse/state/__init__.py +4 -1
- synapse/storage/_base.py +1 -1
- synapse/storage/background_updates.py +8 -3
- synapse/storage/controllers/persist_events.py +4 -3
- synapse/storage/controllers/purge_events.py +2 -3
- synapse/storage/controllers/state.py +5 -5
- synapse/storage/database.py +12 -7
- synapse/storage/databases/main/__init__.py +7 -2
- synapse/storage/databases/main/cache.py +4 -3
- synapse/storage/databases/main/censor_events.py +1 -1
- synapse/storage/databases/main/client_ips.py +9 -8
- synapse/storage/databases/main/deviceinbox.py +7 -6
- synapse/storage/databases/main/devices.py +4 -4
- synapse/storage/databases/main/end_to_end_keys.py +6 -3
- synapse/storage/databases/main/event_federation.py +7 -6
- synapse/storage/databases/main/event_push_actions.py +13 -13
- synapse/storage/databases/main/events_bg_updates.py +1 -1
- synapse/storage/databases/main/events_worker.py +6 -8
- synapse/storage/databases/main/lock.py +17 -13
- synapse/storage/databases/main/media_repository.py +2 -2
- synapse/storage/databases/main/metrics.py +6 -6
- synapse/storage/databases/main/monthly_active_users.py +3 -4
- synapse/storage/databases/main/receipts.py +1 -1
- synapse/storage/databases/main/registration.py +18 -19
- synapse/storage/databases/main/roommember.py +1 -1
- synapse/storage/databases/main/session.py +3 -3
- synapse/storage/databases/main/sliding_sync.py +2 -2
- synapse/storage/databases/main/transactions.py +3 -3
- synapse/storage/databases/state/store.py +2 -0
- synapse/synapse_rust/http_client.pyi +4 -0
- synapse/synapse_rust.abi3.so +0 -0
- synapse/util/async_helpers.py +36 -24
- synapse/util/batching_queue.py +16 -6
- synapse/util/caches/__init__.py +1 -1
- synapse/util/caches/deferred_cache.py +4 -0
- synapse/util/caches/descriptors.py +14 -2
- synapse/util/caches/dictionary_cache.py +6 -1
- synapse/util/caches/expiringcache.py +16 -5
- synapse/util/caches/lrucache.py +14 -26
- synapse/util/caches/response_cache.py +11 -1
- synapse/util/clock.py +215 -39
- synapse/util/constants.py +2 -0
- synapse/util/daemonize.py +5 -1
- synapse/util/distributor.py +9 -5
- synapse/util/metrics.py +35 -6
- synapse/util/ratelimitutils.py +4 -1
- synapse/util/retryutils.py +7 -4
- synapse/util/task_scheduler.py +11 -14
- synapse/logging/filter.py +0 -38
- {matrix_synapse-1.139.0rc2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/AUTHORS.rst +0 -0
- {matrix_synapse-1.139.0rc2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/LICENSE-AGPL-3.0 +0 -0
- {matrix_synapse-1.139.0rc2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/LICENSE-COMMERCIAL +0 -0
- {matrix_synapse-1.139.0rc2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/WHEEL +0 -0
- {matrix_synapse-1.139.0rc2.dist-info → matrix_synapse-1.140.0rc1.dist-info}/entry_points.txt +0 -0
synapse/util/caches/lrucache.py
CHANGED
|
@@ -45,14 +45,10 @@ from typing import (
|
|
|
45
45
|
overload,
|
|
46
46
|
)
|
|
47
47
|
|
|
48
|
-
from twisted.internet import defer
|
|
48
|
+
from twisted.internet import defer
|
|
49
49
|
|
|
50
50
|
from synapse.config import cache as cache_config
|
|
51
|
-
from synapse.metrics.background_process_metrics import (
|
|
52
|
-
run_as_background_process,
|
|
53
|
-
)
|
|
54
51
|
from synapse.metrics.jemalloc import get_jemalloc_stats
|
|
55
|
-
from synapse.types import ISynapseThreadlessReactor
|
|
56
52
|
from synapse.util import caches
|
|
57
53
|
from synapse.util.caches import CacheMetric, EvictionReason, register_cache
|
|
58
54
|
from synapse.util.caches.treecache import (
|
|
@@ -123,6 +119,7 @@ GLOBAL_ROOT = ListNode["_Node"].create_root_node()
|
|
|
123
119
|
|
|
124
120
|
def _expire_old_entries(
|
|
125
121
|
server_name: str,
|
|
122
|
+
hs: "HomeServer",
|
|
126
123
|
clock: Clock,
|
|
127
124
|
expiry_seconds: float,
|
|
128
125
|
autotune_config: Optional[dict],
|
|
@@ -228,9 +225,8 @@ def _expire_old_entries(
|
|
|
228
225
|
|
|
229
226
|
logger.info("Dropped %d items from caches", i)
|
|
230
227
|
|
|
231
|
-
return run_as_background_process(
|
|
228
|
+
return hs.run_as_background_process(
|
|
232
229
|
"LruCache._expire_old_entries",
|
|
233
|
-
server_name,
|
|
234
230
|
_internal_expire_old_entries,
|
|
235
231
|
clock,
|
|
236
232
|
expiry_seconds,
|
|
@@ -261,6 +257,7 @@ def setup_expire_lru_cache_entries(hs: "HomeServer") -> None:
|
|
|
261
257
|
_expire_old_entries,
|
|
262
258
|
30 * 1000,
|
|
263
259
|
server_name,
|
|
260
|
+
hs,
|
|
264
261
|
clock,
|
|
265
262
|
expiry_time,
|
|
266
263
|
hs.config.caches.cache_autotuning,
|
|
@@ -404,13 +401,13 @@ class LruCache(Generic[KT, VT]):
|
|
|
404
401
|
self,
|
|
405
402
|
*,
|
|
406
403
|
max_size: int,
|
|
404
|
+
clock: Clock,
|
|
407
405
|
server_name: str,
|
|
408
406
|
cache_name: str,
|
|
409
407
|
cache_type: Type[Union[dict, TreeCache]] = dict,
|
|
410
408
|
size_callback: Optional[Callable[[VT], int]] = None,
|
|
411
409
|
metrics_collection_callback: Optional[Callable[[], None]] = None,
|
|
412
410
|
apply_cache_factor_from_config: bool = True,
|
|
413
|
-
clock: Optional[Clock] = None,
|
|
414
411
|
prune_unread_entries: bool = True,
|
|
415
412
|
extra_index_cb: Optional[Callable[[KT, VT], KT]] = None,
|
|
416
413
|
): ...
|
|
@@ -420,13 +417,13 @@ class LruCache(Generic[KT, VT]):
|
|
|
420
417
|
self,
|
|
421
418
|
*,
|
|
422
419
|
max_size: int,
|
|
423
|
-
|
|
420
|
+
clock: Clock,
|
|
421
|
+
server_name: str,
|
|
424
422
|
cache_name: Literal[None] = None,
|
|
425
423
|
cache_type: Type[Union[dict, TreeCache]] = dict,
|
|
426
424
|
size_callback: Optional[Callable[[VT], int]] = None,
|
|
427
425
|
metrics_collection_callback: Optional[Callable[[], None]] = None,
|
|
428
426
|
apply_cache_factor_from_config: bool = True,
|
|
429
|
-
clock: Optional[Clock] = None,
|
|
430
427
|
prune_unread_entries: bool = True,
|
|
431
428
|
extra_index_cb: Optional[Callable[[KT, VT], KT]] = None,
|
|
432
429
|
): ...
|
|
@@ -435,13 +432,13 @@ class LruCache(Generic[KT, VT]):
|
|
|
435
432
|
self,
|
|
436
433
|
*,
|
|
437
434
|
max_size: int,
|
|
438
|
-
|
|
435
|
+
clock: Clock,
|
|
436
|
+
server_name: str,
|
|
439
437
|
cache_name: Optional[str] = None,
|
|
440
438
|
cache_type: Type[Union[dict, TreeCache]] = dict,
|
|
441
439
|
size_callback: Optional[Callable[[VT], int]] = None,
|
|
442
440
|
metrics_collection_callback: Optional[Callable[[], None]] = None,
|
|
443
441
|
apply_cache_factor_from_config: bool = True,
|
|
444
|
-
clock: Optional[Clock] = None,
|
|
445
442
|
prune_unread_entries: bool = True,
|
|
446
443
|
extra_index_cb: Optional[Callable[[KT, VT], KT]] = None,
|
|
447
444
|
):
|
|
@@ -450,12 +447,10 @@ class LruCache(Generic[KT, VT]):
|
|
|
450
447
|
max_size: The maximum amount of entries the cache can hold
|
|
451
448
|
|
|
452
449
|
server_name: The homeserver name that this cache is associated with
|
|
453
|
-
(used to label the metric) (`hs.hostname`).
|
|
454
|
-
set. If unset, no metrics will be reported on this cache.
|
|
450
|
+
(used to label the metric) (`hs.hostname`).
|
|
455
451
|
|
|
456
|
-
cache_name: The name of this cache, for the prometheus metrics.
|
|
457
|
-
|
|
458
|
-
cache.
|
|
452
|
+
cache_name: The name of this cache, for the prometheus metrics. If unset, no
|
|
453
|
+
metrics will be reported on this cache.
|
|
459
454
|
|
|
460
455
|
cache_type:
|
|
461
456
|
type of underlying cache to be used. Typically one of dict
|
|
@@ -494,13 +489,6 @@ class LruCache(Generic[KT, VT]):
|
|
|
494
489
|
|
|
495
490
|
Note: The new key does not have to be unique.
|
|
496
491
|
"""
|
|
497
|
-
# Default `clock` to something sensible. Note that we rename it to
|
|
498
|
-
# `real_clock` so that mypy doesn't think its still `Optional`.
|
|
499
|
-
if clock is None:
|
|
500
|
-
real_clock = Clock(cast(ISynapseThreadlessReactor, reactor))
|
|
501
|
-
else:
|
|
502
|
-
real_clock = clock
|
|
503
|
-
|
|
504
492
|
cache: Union[Dict[KT, _Node[KT, VT]], TreeCache] = cache_type()
|
|
505
493
|
self.cache = cache # Used for introspection.
|
|
506
494
|
self.apply_cache_factor_from_config = apply_cache_factor_from_config
|
|
@@ -592,7 +580,7 @@ class LruCache(Generic[KT, VT]):
|
|
|
592
580
|
key,
|
|
593
581
|
value,
|
|
594
582
|
weak_ref_to_self,
|
|
595
|
-
|
|
583
|
+
clock,
|
|
596
584
|
callbacks,
|
|
597
585
|
prune_unread_entries,
|
|
598
586
|
)
|
|
@@ -610,7 +598,7 @@ class LruCache(Generic[KT, VT]):
|
|
|
610
598
|
metrics.inc_memory_usage(node.memory)
|
|
611
599
|
|
|
612
600
|
def move_node_to_front(node: _Node[KT, VT]) -> None:
|
|
613
|
-
node.move_to_front(
|
|
601
|
+
node.move_to_front(clock, list_root)
|
|
614
602
|
|
|
615
603
|
def delete_node(node: _Node[KT, VT]) -> int:
|
|
616
604
|
node.drop_from_lists()
|
|
@@ -198,7 +198,17 @@ class ResponseCache(Generic[KV]):
|
|
|
198
198
|
# the should_cache bit, we leave it in the cache for now and schedule
|
|
199
199
|
# its removal later.
|
|
200
200
|
if self.timeout_sec and context.should_cache:
|
|
201
|
-
self.clock.call_later(
|
|
201
|
+
self.clock.call_later(
|
|
202
|
+
self.timeout_sec,
|
|
203
|
+
self._entry_timeout,
|
|
204
|
+
key,
|
|
205
|
+
# We don't need to track these calls since they don't hold any strong
|
|
206
|
+
# references which would keep the `HomeServer` in memory after shutdown.
|
|
207
|
+
# We don't want to track these because they can get cancelled really
|
|
208
|
+
# quickly and thrash the tracking mechanism, ie. during repeated calls
|
|
209
|
+
# to /sync.
|
|
210
|
+
call_later_cancel_on_shutdown=False,
|
|
211
|
+
)
|
|
202
212
|
else:
|
|
203
213
|
# otherwise, remove the result immediately.
|
|
204
214
|
self.unset(key)
|
synapse/util/clock.py
CHANGED
|
@@ -17,10 +17,12 @@
|
|
|
17
17
|
from typing import (
|
|
18
18
|
Any,
|
|
19
19
|
Callable,
|
|
20
|
+
Dict,
|
|
21
|
+
List,
|
|
20
22
|
)
|
|
21
23
|
|
|
22
|
-
import attr
|
|
23
24
|
from typing_extensions import ParamSpec
|
|
25
|
+
from zope.interface import implementer
|
|
24
26
|
|
|
25
27
|
from twisted.internet import defer, task
|
|
26
28
|
from twisted.internet.defer import Deferred
|
|
@@ -34,23 +36,54 @@ from synapse.util import log_failure
|
|
|
34
36
|
P = ParamSpec("P")
|
|
35
37
|
|
|
36
38
|
|
|
37
|
-
@attr.s(slots=True)
|
|
38
39
|
class Clock:
|
|
39
40
|
"""
|
|
40
41
|
A Clock wraps a Twisted reactor and provides utilities on top of it.
|
|
41
42
|
|
|
43
|
+
This clock should be used in place of calls to the base reactor wherever `LoopingCall`
|
|
44
|
+
or `DelayedCall` are made (such as when calling `reactor.callLater`. This is to
|
|
45
|
+
ensure the calls made by this `HomeServer` instance are tracked and can be cleaned
|
|
46
|
+
up during `HomeServer.shutdown()`.
|
|
47
|
+
|
|
48
|
+
We enforce usage of this clock instead of using the reactor directly via lints in
|
|
49
|
+
`scripts-dev/mypy_synapse_plugin.py`.
|
|
50
|
+
|
|
51
|
+
|
|
42
52
|
Args:
|
|
43
53
|
reactor: The Twisted reactor to use.
|
|
44
54
|
"""
|
|
45
55
|
|
|
46
|
-
_reactor: ISynapseThreadlessReactor
|
|
56
|
+
_reactor: ISynapseThreadlessReactor
|
|
57
|
+
|
|
58
|
+
def __init__(self, reactor: ISynapseThreadlessReactor, server_name: str) -> None:
|
|
59
|
+
self._reactor = reactor
|
|
60
|
+
self._server_name = server_name
|
|
61
|
+
|
|
62
|
+
self._delayed_call_id: int = 0
|
|
63
|
+
"""Unique ID used to track delayed calls"""
|
|
64
|
+
|
|
65
|
+
self._looping_calls: List[LoopingCall] = []
|
|
66
|
+
"""List of active looping calls"""
|
|
67
|
+
|
|
68
|
+
self._call_id_to_delayed_call: Dict[int, IDelayedCall] = {}
|
|
69
|
+
"""Mapping from unique call ID to delayed call"""
|
|
70
|
+
|
|
71
|
+
self._is_shutdown = False
|
|
72
|
+
"""Whether shutdown has been requested by the HomeServer"""
|
|
73
|
+
|
|
74
|
+
def shutdown(self) -> None:
|
|
75
|
+
self._is_shutdown = True
|
|
76
|
+
self.cancel_all_looping_calls()
|
|
77
|
+
self.cancel_all_delayed_calls()
|
|
47
78
|
|
|
48
79
|
async def sleep(self, seconds: float) -> None:
|
|
49
80
|
d: defer.Deferred[float] = defer.Deferred()
|
|
50
81
|
# Start task in the `sentinel` logcontext, to avoid leaking the current context
|
|
51
82
|
# into the reactor once it finishes.
|
|
52
83
|
with context.PreserveLoggingContext():
|
|
53
|
-
|
|
84
|
+
# We can ignore the lint here since this class is the one location callLater should
|
|
85
|
+
# be called.
|
|
86
|
+
self._reactor.callLater(seconds, d.callback, seconds) # type: ignore[call-later-not-tracked]
|
|
54
87
|
await d
|
|
55
88
|
|
|
56
89
|
def time(self) -> float:
|
|
@@ -123,6 +156,9 @@ class Clock:
|
|
|
123
156
|
) -> LoopingCall:
|
|
124
157
|
"""Common functionality for `looping_call` and `looping_call_now`"""
|
|
125
158
|
|
|
159
|
+
if self._is_shutdown:
|
|
160
|
+
raise Exception("Cannot start looping call. Clock has been shutdown")
|
|
161
|
+
|
|
126
162
|
def wrapped_f(*args: P.args, **kwargs: P.kwargs) -> Deferred:
|
|
127
163
|
assert context.current_context() is context.SENTINEL_CONTEXT, (
|
|
128
164
|
"Expected `looping_call` callback from the reactor to start with the sentinel logcontext "
|
|
@@ -144,13 +180,19 @@ class Clock:
|
|
|
144
180
|
# this function and yield control back to the reactor to avoid leaking the
|
|
145
181
|
# current logcontext to the reactor (which would then get picked up and
|
|
146
182
|
# associated with the next thing the reactor does)
|
|
147
|
-
with context.PreserveLoggingContext(
|
|
183
|
+
with context.PreserveLoggingContext(
|
|
184
|
+
context.LoggingContext(
|
|
185
|
+
name="looping_call", server_name=self._server_name
|
|
186
|
+
)
|
|
187
|
+
):
|
|
148
188
|
# We use `run_in_background` to reset the logcontext after `f` (or the
|
|
149
189
|
# awaitable returned by `f`) completes to avoid leaking the current
|
|
150
190
|
# logcontext to the reactor
|
|
151
191
|
return context.run_in_background(f, *args, **kwargs)
|
|
152
192
|
|
|
153
|
-
|
|
193
|
+
# We can ignore the lint here since this is the one location LoopingCall's
|
|
194
|
+
# should be created.
|
|
195
|
+
call = task.LoopingCall(wrapped_f, *args, **kwargs) # type: ignore[prefer-synapse-clock-looping-call]
|
|
154
196
|
call.clock = self._reactor
|
|
155
197
|
# If `now=true`, the function will be called here immediately so we need to be
|
|
156
198
|
# in the sentinel context now.
|
|
@@ -160,10 +202,32 @@ class Clock:
|
|
|
160
202
|
with context.PreserveLoggingContext():
|
|
161
203
|
d = call.start(msec / 1000.0, now=now)
|
|
162
204
|
d.addErrback(log_failure, "Looping call died", consumeErrors=False)
|
|
205
|
+
self._looping_calls.append(call)
|
|
163
206
|
return call
|
|
164
207
|
|
|
208
|
+
def cancel_all_looping_calls(self, consumeErrors: bool = True) -> None:
|
|
209
|
+
"""
|
|
210
|
+
Stop all running looping calls.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
consumeErrors: Whether to re-raise errors encountered when cancelling the
|
|
214
|
+
scheduled call.
|
|
215
|
+
"""
|
|
216
|
+
for call in self._looping_calls:
|
|
217
|
+
try:
|
|
218
|
+
call.stop()
|
|
219
|
+
except Exception:
|
|
220
|
+
if not consumeErrors:
|
|
221
|
+
raise
|
|
222
|
+
self._looping_calls.clear()
|
|
223
|
+
|
|
165
224
|
def call_later(
|
|
166
|
-
self,
|
|
225
|
+
self,
|
|
226
|
+
delay: float,
|
|
227
|
+
callback: Callable,
|
|
228
|
+
*args: Any,
|
|
229
|
+
call_later_cancel_on_shutdown: bool = True,
|
|
230
|
+
**kwargs: Any,
|
|
167
231
|
) -> IDelayedCall:
|
|
168
232
|
"""Call something later
|
|
169
233
|
|
|
@@ -175,37 +239,78 @@ class Clock:
|
|
|
175
239
|
delay: How long to wait in seconds.
|
|
176
240
|
callback: Function to call
|
|
177
241
|
*args: Postional arguments to pass to function.
|
|
242
|
+
call_later_cancel_on_shutdown: Whether this call should be tracked for cleanup during
|
|
243
|
+
shutdown. In general, all calls should be tracked. There may be a use case
|
|
244
|
+
not to track calls with a `timeout` of 0 (or similarly short) since tracking
|
|
245
|
+
them may result in rapid insertions and removals of tracked calls
|
|
246
|
+
unnecessarily. But unless a specific instance of tracking proves to be an
|
|
247
|
+
issue, we can just track all delayed calls.
|
|
178
248
|
**kwargs: Key arguments to pass to function.
|
|
179
249
|
"""
|
|
180
250
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
#
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
251
|
+
if self._is_shutdown:
|
|
252
|
+
raise Exception("Cannot start delayed call. Clock has been shutdown")
|
|
253
|
+
|
|
254
|
+
def create_wrapped_callback(
|
|
255
|
+
track_for_shutdown_cancellation: bool,
|
|
256
|
+
) -> Callable[P, None]:
|
|
257
|
+
def wrapped_callback(*args: Any, **kwargs: Any) -> None:
|
|
258
|
+
assert context.current_context() is context.SENTINEL_CONTEXT, (
|
|
259
|
+
"Expected `call_later` callback from the reactor to start with the sentinel logcontext "
|
|
260
|
+
f"but saw {context.current_context()}. In other words, another task shouldn't have "
|
|
261
|
+
"leaked their logcontext to us."
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Because this is a callback from the reactor, we will be using the
|
|
265
|
+
# `sentinel` log context at this point. We want the function to log with
|
|
266
|
+
# some logcontext as we want to know which server the logs came from.
|
|
267
|
+
#
|
|
268
|
+
# We use `PreserveLoggingContext` to prevent our new `call_later`
|
|
269
|
+
# logcontext from finishing as soon as we exit this function, in case `f`
|
|
270
|
+
# returns an awaitable/deferred which would continue running and may try to
|
|
271
|
+
# restore the `loop_call` context when it's done (because it's trying to
|
|
272
|
+
# adhere to the Synapse logcontext rules.)
|
|
273
|
+
#
|
|
274
|
+
# This also ensures that we return to the `sentinel` context when we exit
|
|
275
|
+
# this function and yield control back to the reactor to avoid leaking the
|
|
276
|
+
# current logcontext to the reactor (which would then get picked up and
|
|
277
|
+
# associated with the next thing the reactor does)
|
|
278
|
+
try:
|
|
279
|
+
with context.PreserveLoggingContext(
|
|
280
|
+
context.LoggingContext(
|
|
281
|
+
name="call_later", server_name=self._server_name
|
|
282
|
+
)
|
|
283
|
+
):
|
|
284
|
+
# We use `run_in_background` to reset the logcontext after `f` (or the
|
|
285
|
+
# awaitable returned by `f`) completes to avoid leaking the current
|
|
286
|
+
# logcontext to the reactor
|
|
287
|
+
context.run_in_background(callback, *args, **kwargs)
|
|
288
|
+
finally:
|
|
289
|
+
if track_for_shutdown_cancellation:
|
|
290
|
+
# We still want to remove the call from the tracking map. Even if
|
|
291
|
+
# the callback raises an exception.
|
|
292
|
+
self._call_id_to_delayed_call.pop(call_id)
|
|
293
|
+
|
|
294
|
+
return wrapped_callback
|
|
295
|
+
|
|
296
|
+
if call_later_cancel_on_shutdown:
|
|
297
|
+
call_id = self._delayed_call_id
|
|
298
|
+
self._delayed_call_id = self._delayed_call_id + 1
|
|
299
|
+
|
|
300
|
+
# We can ignore the lint here since this class is the one location callLater
|
|
301
|
+
# should be called.
|
|
302
|
+
call = self._reactor.callLater(
|
|
303
|
+
delay, create_wrapped_callback(True), *args, **kwargs
|
|
304
|
+
) # type: ignore[call-later-not-tracked]
|
|
305
|
+
call = DelayedCallWrapper(call, call_id, self)
|
|
306
|
+
self._call_id_to_delayed_call[call_id] = call
|
|
307
|
+
return call
|
|
308
|
+
else:
|
|
309
|
+
# We can ignore the lint here since this class is the one location callLater should
|
|
310
|
+
# be called.
|
|
311
|
+
return self._reactor.callLater(
|
|
312
|
+
delay, create_wrapped_callback(False), *args, **kwargs
|
|
313
|
+
) # type: ignore[call-later-not-tracked]
|
|
209
314
|
|
|
210
315
|
def cancel_call_later(self, timer: IDelayedCall, ignore_errs: bool = False) -> None:
|
|
211
316
|
try:
|
|
@@ -214,6 +319,24 @@ class Clock:
|
|
|
214
319
|
if not ignore_errs:
|
|
215
320
|
raise
|
|
216
321
|
|
|
322
|
+
def cancel_all_delayed_calls(self, ignore_errs: bool = True) -> None:
|
|
323
|
+
"""
|
|
324
|
+
Stop all scheduled calls that were marked with `cancel_on_shutdown` when they were created.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
ignore_errs: Whether to re-raise errors encountered when cancelling the
|
|
328
|
+
scheduled call.
|
|
329
|
+
"""
|
|
330
|
+
# We make a copy here since calling `cancel()` on a delayed_call
|
|
331
|
+
# will result in the call removing itself from the map mid-iteration.
|
|
332
|
+
for call in list(self._call_id_to_delayed_call.values()):
|
|
333
|
+
try:
|
|
334
|
+
call.cancel()
|
|
335
|
+
except Exception:
|
|
336
|
+
if not ignore_errs:
|
|
337
|
+
raise
|
|
338
|
+
self._call_id_to_delayed_call.clear()
|
|
339
|
+
|
|
217
340
|
def call_when_running(
|
|
218
341
|
self,
|
|
219
342
|
callback: Callable[P, object],
|
|
@@ -258,7 +381,9 @@ class Clock:
|
|
|
258
381
|
# current logcontext to the reactor (which would then get picked up and
|
|
259
382
|
# associated with the next thing the reactor does)
|
|
260
383
|
with context.PreserveLoggingContext(
|
|
261
|
-
context.LoggingContext(
|
|
384
|
+
context.LoggingContext(
|
|
385
|
+
name="call_when_running", server_name=self._server_name
|
|
386
|
+
)
|
|
262
387
|
):
|
|
263
388
|
# We use `run_in_background` to reset the logcontext after `f` (or the
|
|
264
389
|
# awaitable returned by `f`) completes to avoid leaking the current
|
|
@@ -276,7 +401,7 @@ class Clock:
|
|
|
276
401
|
callback: Callable[P, object],
|
|
277
402
|
*args: P.args,
|
|
278
403
|
**kwargs: P.kwargs,
|
|
279
|
-
) ->
|
|
404
|
+
) -> Any:
|
|
280
405
|
"""
|
|
281
406
|
Add a function to be called when a system event occurs.
|
|
282
407
|
|
|
@@ -290,6 +415,9 @@ class Clock:
|
|
|
290
415
|
callback: Function to call
|
|
291
416
|
*args: Postional arguments to pass to function.
|
|
292
417
|
**kwargs: Key arguments to pass to function.
|
|
418
|
+
|
|
419
|
+
Returns:
|
|
420
|
+
an ID that can be used to remove this call with `reactor.removeSystemEventTrigger`.
|
|
293
421
|
"""
|
|
294
422
|
|
|
295
423
|
def wrapped_callback(*args: Any, **kwargs: Any) -> None:
|
|
@@ -313,7 +441,11 @@ class Clock:
|
|
|
313
441
|
# this function and yield control back to the reactor to avoid leaking the
|
|
314
442
|
# current logcontext to the reactor (which would then get picked up and
|
|
315
443
|
# associated with the next thing the reactor does)
|
|
316
|
-
with context.PreserveLoggingContext(
|
|
444
|
+
with context.PreserveLoggingContext(
|
|
445
|
+
context.LoggingContext(
|
|
446
|
+
name="system_event", server_name=self._server_name
|
|
447
|
+
)
|
|
448
|
+
):
|
|
317
449
|
# We use `run_in_background` to reset the logcontext after `f` (or the
|
|
318
450
|
# awaitable returned by `f`) completes to avoid leaking the current
|
|
319
451
|
# logcontext to the reactor
|
|
@@ -321,6 +453,50 @@ class Clock:
|
|
|
321
453
|
|
|
322
454
|
# We can ignore the lint here since this class is the one location
|
|
323
455
|
# `addSystemEventTrigger` should be called.
|
|
324
|
-
self._reactor.addSystemEventTrigger(
|
|
456
|
+
return self._reactor.addSystemEventTrigger(
|
|
325
457
|
phase, event_type, wrapped_callback, *args, **kwargs
|
|
326
458
|
) # type: ignore[prefer-synapse-clock-add-system-event-trigger]
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
@implementer(IDelayedCall)
|
|
462
|
+
class DelayedCallWrapper:
|
|
463
|
+
"""Wraps an `IDelayedCall` so that we can intercept the call to `cancel()` and
|
|
464
|
+
properly cleanup the delayed call from the tracking map of the `Clock`.
|
|
465
|
+
|
|
466
|
+
args:
|
|
467
|
+
delayed_call: The actual `IDelayedCall`
|
|
468
|
+
call_id: Unique identifier for this delayed call
|
|
469
|
+
clock: The clock instance tracking this call
|
|
470
|
+
"""
|
|
471
|
+
|
|
472
|
+
def __init__(self, delayed_call: IDelayedCall, call_id: int, clock: Clock):
|
|
473
|
+
self.delayed_call = delayed_call
|
|
474
|
+
self.call_id = call_id
|
|
475
|
+
self.clock = clock
|
|
476
|
+
|
|
477
|
+
def cancel(self) -> None:
|
|
478
|
+
"""Remove the call from the tracking map and propagate the call to the
|
|
479
|
+
underlying delayed_call.
|
|
480
|
+
"""
|
|
481
|
+
self.delayed_call.cancel()
|
|
482
|
+
try:
|
|
483
|
+
self.clock._call_id_to_delayed_call.pop(self.call_id)
|
|
484
|
+
except KeyError:
|
|
485
|
+
# If the delayed call isn't being tracked anymore we can just move on.
|
|
486
|
+
pass
|
|
487
|
+
|
|
488
|
+
def getTime(self) -> float:
|
|
489
|
+
"""Propagate the call to the underlying delayed_call."""
|
|
490
|
+
return self.delayed_call.getTime()
|
|
491
|
+
|
|
492
|
+
def delay(self, secondsLater: float) -> None:
|
|
493
|
+
"""Propagate the call to the underlying delayed_call."""
|
|
494
|
+
self.delayed_call.delay(secondsLater)
|
|
495
|
+
|
|
496
|
+
def reset(self, secondsFromNow: float) -> None:
|
|
497
|
+
"""Propagate the call to the underlying delayed_call."""
|
|
498
|
+
self.delayed_call.reset(secondsFromNow)
|
|
499
|
+
|
|
500
|
+
def active(self) -> bool:
|
|
501
|
+
"""Propagate the call to the underlying delayed_call."""
|
|
502
|
+
return self.delayed_call.active()
|
synapse/util/constants.py
CHANGED
synapse/util/daemonize.py
CHANGED
|
@@ -32,6 +32,7 @@ from typing import NoReturn, Optional, Type
|
|
|
32
32
|
from synapse.logging.context import (
|
|
33
33
|
LoggingContext,
|
|
34
34
|
PreserveLoggingContext,
|
|
35
|
+
current_context,
|
|
35
36
|
)
|
|
36
37
|
|
|
37
38
|
|
|
@@ -149,9 +150,12 @@ def daemonize_process(pid_file: str, logger: logging.Logger, chdir: str = "/") -
|
|
|
149
150
|
|
|
150
151
|
signal.signal(signal.SIGTERM, sigterm)
|
|
151
152
|
|
|
153
|
+
# Copy the `server_name` from the current logcontext
|
|
154
|
+
server_name = current_context().server_name
|
|
155
|
+
|
|
152
156
|
# Cleanup pid file at exit.
|
|
153
157
|
def exit() -> None:
|
|
154
|
-
with LoggingContext("atexit"):
|
|
158
|
+
with LoggingContext(name="atexit", server_name=server_name):
|
|
155
159
|
logger.warning("Stopping daemon.")
|
|
156
160
|
os.remove(pid_file)
|
|
157
161
|
sys.exit(0)
|
synapse/util/distributor.py
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
#
|
|
21
21
|
import logging
|
|
22
22
|
from typing import (
|
|
23
|
+
TYPE_CHECKING,
|
|
23
24
|
Any,
|
|
24
25
|
Awaitable,
|
|
25
26
|
Callable,
|
|
@@ -36,10 +37,13 @@ from typing_extensions import ParamSpec
|
|
|
36
37
|
from twisted.internet import defer
|
|
37
38
|
|
|
38
39
|
from synapse.logging.context import make_deferred_yieldable, run_in_background
|
|
39
|
-
from synapse.metrics.background_process_metrics import run_as_background_process
|
|
40
40
|
from synapse.types import UserID
|
|
41
41
|
from synapse.util.async_helpers import maybe_awaitable
|
|
42
42
|
|
|
43
|
+
if TYPE_CHECKING:
|
|
44
|
+
from synapse.server import HomeServer
|
|
45
|
+
|
|
46
|
+
|
|
43
47
|
logger = logging.getLogger(__name__)
|
|
44
48
|
|
|
45
49
|
|
|
@@ -58,13 +62,13 @@ class Distributor:
|
|
|
58
62
|
model will do for today.
|
|
59
63
|
"""
|
|
60
64
|
|
|
61
|
-
def __init__(self,
|
|
65
|
+
def __init__(self, hs: "HomeServer") -> None:
|
|
62
66
|
"""
|
|
63
67
|
Args:
|
|
64
68
|
server_name: The homeserver name of the server (used to label metrics)
|
|
65
69
|
(this should be `hs.hostname`).
|
|
66
70
|
"""
|
|
67
|
-
self.
|
|
71
|
+
self.hs = hs
|
|
68
72
|
self.signals: Dict[str, Signal] = {}
|
|
69
73
|
self.pre_registration: Dict[str, List[Callable]] = {}
|
|
70
74
|
|
|
@@ -97,8 +101,8 @@ class Distributor:
|
|
|
97
101
|
if name not in self.signals:
|
|
98
102
|
raise KeyError("%r does not have a signal named %s" % (self, name))
|
|
99
103
|
|
|
100
|
-
run_as_background_process(
|
|
101
|
-
name, self.
|
|
104
|
+
self.hs.run_as_background_process(
|
|
105
|
+
name, self.signals[name].fire, *args, **kwargs
|
|
102
106
|
)
|
|
103
107
|
|
|
104
108
|
|
synapse/util/metrics.py
CHANGED
|
@@ -217,7 +217,11 @@ class Measure:
|
|
|
217
217
|
else:
|
|
218
218
|
assert isinstance(curr_context, LoggingContext)
|
|
219
219
|
parent_context = curr_context
|
|
220
|
-
self._logging_context = LoggingContext(
|
|
220
|
+
self._logging_context = LoggingContext(
|
|
221
|
+
name=str(curr_context),
|
|
222
|
+
server_name=self.server_name,
|
|
223
|
+
parent_context=parent_context,
|
|
224
|
+
)
|
|
221
225
|
self.start: Optional[float] = None
|
|
222
226
|
|
|
223
227
|
def __enter__(self) -> "Measure":
|
|
@@ -289,21 +293,46 @@ class DynamicCollectorRegistry(CollectorRegistry):
|
|
|
289
293
|
|
|
290
294
|
def __init__(self) -> None:
|
|
291
295
|
super().__init__()
|
|
292
|
-
self.
|
|
296
|
+
self._server_name_to_pre_update_hooks: Dict[
|
|
297
|
+
str, Dict[str, Callable[[], None]]
|
|
298
|
+
] = {}
|
|
299
|
+
"""
|
|
300
|
+
Mapping of server name to a mapping of metric name to metric pre-update
|
|
301
|
+
hook
|
|
302
|
+
"""
|
|
293
303
|
|
|
294
304
|
def collect(self) -> Generator[Metric, None, None]:
|
|
295
305
|
"""
|
|
296
306
|
Collects metrics, calling pre-update hooks first.
|
|
297
307
|
"""
|
|
298
308
|
|
|
299
|
-
for
|
|
300
|
-
pre_update_hook()
|
|
309
|
+
for pre_update_hooks in self._server_name_to_pre_update_hooks.values():
|
|
310
|
+
for pre_update_hook in pre_update_hooks.values():
|
|
311
|
+
pre_update_hook()
|
|
301
312
|
|
|
302
313
|
yield from super().collect()
|
|
303
314
|
|
|
304
|
-
def register_hook(
|
|
315
|
+
def register_hook(
|
|
316
|
+
self, server_name: str, metric_name: str, hook: Callable[[], None]
|
|
317
|
+
) -> None:
|
|
305
318
|
"""
|
|
306
319
|
Registers a hook that is called before metric collection.
|
|
307
320
|
"""
|
|
308
321
|
|
|
309
|
-
self.
|
|
322
|
+
server_hooks = self._server_name_to_pre_update_hooks.setdefault(server_name, {})
|
|
323
|
+
if server_hooks.get(metric_name) is not None:
|
|
324
|
+
# TODO: This should be an `assert` since registering the same metric name
|
|
325
|
+
# multiple times will clobber the old metric.
|
|
326
|
+
# We currently rely on this behaviour as we instantiate multiple
|
|
327
|
+
# `SyncRestServlet`, one per listener, and in the `__init__` we setup a new
|
|
328
|
+
# LruCache.
|
|
329
|
+
# Once the above behaviour is changed, this should be changed to an `assert`.
|
|
330
|
+
logger.error(
|
|
331
|
+
"Metric named %s already registered for server %s",
|
|
332
|
+
metric_name,
|
|
333
|
+
server_name,
|
|
334
|
+
)
|
|
335
|
+
server_hooks[metric_name] = hook
|
|
336
|
+
|
|
337
|
+
def unregister_hooks_for_homeserver(self, server_name: str) -> None:
|
|
338
|
+
self._server_name_to_pre_update_hooks.pop(server_name, None)
|