Skip to content

Commit 31a38f5

Browse files
Resolve breaking change to run_as_background_process in module API (#18737)
Fix #18735 In #18670, we updated `run_as_background_process` to add a `server_name` argument. Because this function is directly exported from the Synapse module API, this is a breaking change to any downstream Synapse modules that use `run_as_background_process`. This PR shims and deprecates the existing `run_as_background_process(...)` for modules by providing a stub `server_name` value and introduces a new `ModuleApi.run_as_background_process(...)` that covers the `server_name` logic automagically.
1 parent 5b8b45a commit 31a38f5

File tree

4 files changed

+137
-6
lines changed

4 files changed

+137
-6
lines changed

changelog.d/18737.removal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Deprecate `run_as_background_process` exported as part of the module API interface in favor of `ModuleApi.run_as_background_process`. See the relevant section in the upgrade notes for more information.

docs/upgrade.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,35 @@ stacking them up. You can monitor the currently running background updates with
119119
120120
# Upgrading to v1.136.0
121121
122+
## Deprecate `run_as_background_process` exported as part of the module API interface in favor of `ModuleApi.run_as_background_process`
123+
124+
The `run_as_background_process` function is now a method of the `ModuleApi` class. If
125+
you were using the function directly from the module API, it will continue to work fine
126+
but the background process metrics will not include an accurate `server_name` label.
127+
This kind of metric labeling isn't relevant for many use cases and is used to
128+
differentiate Synapse instances running in the same Python process (relevant to Synapse
129+
Pro: Small Hosts). We recommend updating your usage to use the new
130+
`ModuleApi.run_as_background_process` method to stay on top of future changes.
131+
132+
<details>
133+
<summary>Example <code>run_as_background_process</code> upgrade</summary>
134+
135+
Before:
136+
```python
137+
class MyModule:
138+
def __init__(self, module_api: ModuleApi) -> None:
139+
run_as_background_process(__name__ + ":setup_database", self.setup_database)
140+
```
141+
142+
After:
143+
```python
144+
class MyModule:
145+
def __init__(self, module_api: ModuleApi) -> None:
146+
module_api.run_as_background_process(__name__ + ":setup_database", self.setup_database)
147+
```
148+
149+
</details>
150+
122151
## Metric labels have changed on `synapse_federation_last_received_pdu_time` and `synapse_federation_last_sent_pdu_time`
123152

124153
Previously, the `synapse_federation_last_received_pdu_time` and
@@ -136,6 +165,7 @@ this change but you will need to manually update your own existing Grafana dashb
136165
using these metrics.
137166
138167
168+
139169
# Upgrading to v1.135.0
140170
141171
## `on_user_registration` module API callback may now run on any worker

synapse/events/auto_accept_invites.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ async def on_new_event(self, event: EventBase, *args: Any) -> None:
114114
# that occurs when responding to invites over federation (see https://github.com/matrix-org/synapse-auto-accept-invite/issues/12)
115115
run_as_background_process(
116116
"retry_make_join",
117-
self.server_name,
118117
self._retry_make_join,
119118
event.state_key,
120119
event.state_key,

synapse/module_api/__init__.py

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from typing import (
2424
TYPE_CHECKING,
2525
Any,
26+
Awaitable,
2627
Callable,
2728
Collection,
2829
Dict,
@@ -80,7 +81,9 @@
8081
make_deferred_yieldable,
8182
run_in_background,
8283
)
83-
from synapse.metrics.background_process_metrics import run_as_background_process
84+
from synapse.metrics.background_process_metrics import (
85+
run_as_background_process as _run_as_background_process,
86+
)
8487
from synapse.module_api.callbacks.account_validity_callbacks import (
8588
IS_USER_EXPIRED_CALLBACK,
8689
ON_LEGACY_ADMIN_REQUEST,
@@ -158,6 +161,9 @@
158161
from synapse.util.frozenutils import freeze
159162

160163
if TYPE_CHECKING:
164+
# Old versions don't have `LiteralString`
165+
from typing_extensions import LiteralString
166+
161167
from synapse.app.generic_worker import GenericWorkerStore
162168
from synapse.server import HomeServer
163169

@@ -216,6 +222,65 @@ class UserIpAndAgent:
216222
last_seen: int
217223

218224

225+
def run_as_background_process(
226+
desc: "LiteralString",
227+
func: Callable[..., Awaitable[Optional[T]]],
228+
*args: Any,
229+
bg_start_span: bool = True,
230+
**kwargs: Any,
231+
) -> "defer.Deferred[Optional[T]]":
232+
"""
233+
XXX: Deprecated: use `ModuleApi.run_as_background_process` instead.
234+
235+
Run the given function in its own logcontext, with resource metrics
236+
237+
This should be used to wrap processes which are fired off to run in the
238+
background, instead of being associated with a particular request.
239+
240+
It returns a Deferred which completes when the function completes, but it doesn't
241+
follow the synapse logcontext rules, which makes it appropriate for passing to
242+
clock.looping_call and friends (or for firing-and-forgetting in the middle of a
243+
normal synapse async function).
244+
245+
Args:
246+
desc: a description for this background process type
247+
server_name: The homeserver name that this background process is being run for
248+
(this should be `hs.hostname`).
249+
func: a function, which may return a Deferred or a coroutine
250+
bg_start_span: Whether to start an opentracing span. Defaults to True.
251+
Should only be disabled for processes that will not log to or tag
252+
a span.
253+
args: positional args for func
254+
kwargs: keyword args for func
255+
256+
Returns:
257+
Deferred which returns the result of func, or `None` if func raises.
258+
Note that the returned Deferred does not follow the synapse logcontext
259+
rules.
260+
"""
261+
262+
logger.warning(
263+
"Using deprecated `run_as_background_process` that's exported from the Module API. "
264+
"Prefer `ModuleApi.run_as_background_process` instead.",
265+
)
266+
267+
# Historically, since this function is exported from the module API, we can't just
268+
# change the signature to require a `server_name` argument. Since
269+
# `run_as_background_process` internally in Synapse requires `server_name` now, we
270+
# just have to stub this out with a placeholder value and tell people to use the new
271+
# function instead.
272+
stub_server_name = "synapse_module_running_from_unknown_server"
273+
274+
return _run_as_background_process(
275+
desc,
276+
stub_server_name,
277+
func,
278+
*args,
279+
bg_start_span=bg_start_span,
280+
**kwargs,
281+
)
282+
283+
219284
def cached(
220285
*,
221286
max_entries: int = 1000,
@@ -1323,10 +1388,9 @@ def looping_background_call(
13231388

13241389
if self._hs.config.worker.run_background_tasks or run_on_all_instances:
13251390
self._clock.looping_call(
1326-
run_as_background_process,
1391+
self.run_as_background_process,
13271392
msec,
13281393
desc,
1329-
self.server_name,
13301394
lambda: maybe_awaitable(f(*args, **kwargs)),
13311395
)
13321396
else:
@@ -1382,9 +1446,8 @@ def delayed_background_call(
13821446
return self._clock.call_later(
13831447
# convert ms to seconds as needed by call_later.
13841448
msec * 0.001,
1385-
run_as_background_process,
1449+
self.run_as_background_process,
13861450
desc,
1387-
self.server_name,
13881451
lambda: maybe_awaitable(f(*args, **kwargs)),
13891452
)
13901453

@@ -1590,6 +1653,44 @@ async def get_room_state(
15901653

15911654
return {key: state_events[event_id] for key, event_id in state_ids.items()}
15921655

1656+
def run_as_background_process(
1657+
self,
1658+
desc: "LiteralString",
1659+
func: Callable[..., Awaitable[Optional[T]]],
1660+
*args: Any,
1661+
bg_start_span: bool = True,
1662+
**kwargs: Any,
1663+
) -> "defer.Deferred[Optional[T]]":
1664+
"""Run the given function in its own logcontext, with resource metrics
1665+
1666+
This should be used to wrap processes which are fired off to run in the
1667+
background, instead of being associated with a particular request.
1668+
1669+
It returns a Deferred which completes when the function completes, but it doesn't
1670+
follow the synapse logcontext rules, which makes it appropriate for passing to
1671+
clock.looping_call and friends (or for firing-and-forgetting in the middle of a
1672+
normal synapse async function).
1673+
1674+
Args:
1675+
desc: a description for this background process type
1676+
server_name: The homeserver name that this background process is being run for
1677+
(this should be `hs.hostname`).
1678+
func: a function, which may return a Deferred or a coroutine
1679+
bg_start_span: Whether to start an opentracing span. Defaults to True.
1680+
Should only be disabled for processes that will not log to or tag
1681+
a span.
1682+
args: positional args for func
1683+
kwargs: keyword args for func
1684+
1685+
Returns:
1686+
Deferred which returns the result of func, or `None` if func raises.
1687+
Note that the returned Deferred does not follow the synapse logcontext
1688+
rules.
1689+
"""
1690+
return _run_as_background_process(
1691+
desc, self.server_name, func, *args, bg_start_span=bg_start_span, **kwargs
1692+
)
1693+
15931694
async def defer_to_thread(
15941695
self,
15951696
f: Callable[P, T],

0 commit comments

Comments
 (0)