Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Add the notify_appservices_from_worker configuration option (superseding notify_appservices) to allow a generic worker to be designated as the worker to send traffic to Application Services. #12452

Merged
merged 10 commits into from
May 6, 2022
1 change: 1 addition & 0 deletions changelog.d/12452.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the `notify_appservices_from_worker` configuration option (superseding `notify_appservices`) to allow a generic worker to be designated as the worker to send traffic to Application Services.
4 changes: 2 additions & 2 deletions docker/configure_workers_and_start.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@
"worker_extra_conf": "enable_media_repo: true",
},
"appservice": {
"app": "synapse.app.appservice",
"app": "synapse.app.generic_worker",
"listener_resources": [],
"endpoint_patterns": [],
"shared_extra_conf": {"notify_appservices": False},
"shared_extra_conf": {"notify_appservices_from_worker": "appservice"},
"worker_extra_conf": "",
},
"federation_sender": {
Expand Down
27 changes: 27 additions & 0 deletions docs/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,40 @@ To re-enable this functionality, set the
[`allow_device_name_lookup_over_federation`](https://matrix-org.github.io/synapse/v1.59/usage/configuration/config_documentation.html#federation)
homeserver config option to `true`.


## Deprecation of the `synapse.app.appservice` worker application type

The `synapse.app.appservice` worker application type allowed you to configure a
single worker to use to notify application services of new events, as long
as this functionality was disabled on the main process with `notify_appservices: False`.

To unify Synapse's worker types, the `synapse.app.appservice` worker application
type and the `notify_appservices` configuration option have been deprecated.

To get the same functionality, it's now recommended that the `synapse.app.generic_worker`
worker application type is used and that the `notify_appservices_from_worker` option
is set to the name of a worker.

For the time being, `notify_appservices_from_worker` can be used alongside
`synapse.app.appservice` and `notify_appservices` to make it easier to transition
between the two configurations, however please note that:

- the options must not contradict each other (otherwise Synapse won't start); and
- the `notify_appservices` option will be removed in a future release of Synapse.

Please see [the relevant section of the worker documentation][v1_59_notify_ases_from] for more information.

[v1_59_notify_ases_from]: workers.md#notifying-application-services


# Upgrading to v1.58.0

## Groups/communities feature has been disabled by default

The non-standard groups/communities feature in Synapse has been disabled by default
and will be removed in Synapse v1.61.0.


# Upgrading to v1.57.0

## Changes to database schema for application services
Expand Down
20 changes: 20 additions & 0 deletions docs/workers.md
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,23 @@ An example for a dedicated background worker instance:
{{#include systemd-with-workers/workers/background_worker.yaml}}
```

#### Notifying Application Services

You can designate one worker to send output traffic to Application Services.
clokep marked this conversation as resolved.
Show resolved Hide resolved

Specify its name in the shared configuration as follows:

```yaml
notify_appservices_from_worker: worker_name
```

This work cannot be load-balanced; please ensure the main process is restarted
after setting this option in the shared configuration!

This style of configuration supersedes the legacy `synapse.app.appservice`
worker application type.


### `synapse.app.pusher`

Handles sending push notifications to sygnal and email. Doesn't handle any
Expand All @@ -453,6 +470,9 @@ pusher_instances:

### `synapse.app.appservice`

**Deprecated as of Synapse v1.58.** [Use `synapse.app.generic_worker` with the
`notify_appservices_from_worker` option instead.](#notifying-application-services)

Handles sending output traffic to Application Services. Doesn't handle any
REST endpoints itself, but you should set `notify_appservices: False` in the
shared configuration file to stop the main synapse sending appservice notifications.
Expand Down
16 changes: 0 additions & 16 deletions synapse/app/generic_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,22 +441,6 @@ def start(config_options: List[str]) -> None:
"synapse.app.user_dir",
)

if config.worker.worker_app == "synapse.app.appservice":
if config.appservice.notify_appservices:
sys.stderr.write(
"\nThe appservices must be disabled in the main synapse process"
"\nbefore they can be run in a separate worker."
"\nPlease add ``notify_appservices: false`` to the main config"
"\n"
)
sys.exit(1)

# Force the appservice to start since they will be disabled in the main config
config.appservice.notify_appservices = True
else:
# For other worker types we force this to off.
config.appservice.notify_appservices = False

if config.worker.worker_app == "synapse.app.user_dir":
if config.server.update_user_directory:
sys.stderr.write(
Expand Down
1 change: 0 additions & 1 deletion synapse/config/appservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ class AppServiceConfig(Config):

def read_config(self, config: JsonDict, **kwargs: Any) -> None:
self.app_service_config_files = config.get("app_service_config_files", [])
self.notify_appservices = config.get("notify_appservices", True)
self.track_appservice_user_ips = config.get("track_appservice_user_ips", False)

def generate_config_section(cls, **kwargs: Any) -> str:
Expand Down
109 changes: 108 additions & 1 deletion synapse/config/workers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
# limitations under the License.

import argparse
from typing import Any, List, Union
import logging
from typing import Any, Dict, List, Union

import attr

Expand Down Expand Up @@ -42,6 +43,13 @@
Please add ``start_pushers: false`` to the main config
"""

_DEPRECATED_WORKER_DUTY_OPTION_USED = """
The '%s' configuration option is deprecated and will be removed in a future
Synapse version. Please use ``%s: name_of_worker`` instead.
"""

logger = logging.getLogger(__name__)


def _instance_to_list_converter(obj: Union[str, List[str]]) -> List[str]:
"""Helper for allowing parsing a string or list of strings to a config
Expand Down Expand Up @@ -296,6 +304,105 @@ def read_config(self, config: JsonDict, **kwargs: Any) -> None:
self.worker_name is None and background_tasks_instance == "master"
) or self.worker_name == background_tasks_instance

self.should_notify_appservices = self._should_this_worker_perform_duty(
config,
legacy_master_option_name="notify_appservices",
legacy_worker_app_name="synapse.app.appservice",
new_option_name="notify_appservices_from_worker",
)

def _should_this_worker_perform_duty(
self,
config: Dict[str, Any],
legacy_master_option_name: str,
legacy_worker_app_name: str,
new_option_name: str,
) -> bool:
"""
Figures out whether this worker should perform a certain duty.

This function is temporary and is only to deal with the complexity
of allowing old, transitional and new configurations all at once.

Contradictions between the legacy and new part of a transitional configuration
will lead to a ConfigError.

Parameters:
config: The config dictionary
legacy_master_option_name: The name of a legacy option, whose value is boolean,
specifying whether it's the master that should handle a certain duty.
e.g. "notify_appservices"
legacy_worker_app_name: The name of a legacy Synapse worker application
that would traditionally perform this duty.
e.g. "synapse.app.appservice"
new_option_name: The name of the new option, whose value is the name of a
designated worker to perform the duty.
e.g. "notify_appservices_from_worker"
"""

# None means 'unspecified'; True means 'run here' and False means
# 'don't run here'.
new_option_should_run_here = None
if new_option_name in config:
designated_worker = config[new_option_name] or "master"
new_option_should_run_here = (
designated_worker == "master" and self.worker_name is None
) or designated_worker == self.worker_name

legacy_option_should_run_here = None
if legacy_master_option_name in config:
run_on_master = bool(config[legacy_master_option_name])

legacy_option_should_run_here = (
self.worker_name is None and run_on_master
) or (self.worker_app == legacy_worker_app_name and not run_on_master)

# Suggest using the new option instead.
logger.warning(
_DEPRECATED_WORKER_DUTY_OPTION_USED,
legacy_master_option_name,
new_option_name,
)

if self.worker_app == legacy_worker_app_name and config.get(
legacy_master_option_name, True
):
# As an extra bit of complication, we need to check that the
# specialised worker is only used if the legacy config says the
# master isn't performing the duties.
raise ConfigError(
f"Cannot use deprecated worker app type '{legacy_worker_app_name}' whilst deprecated option '{legacy_master_option_name}' is not set to false.\n"
f"Consider setting `worker_app: synapse.app.generic_worker` and using the '{new_option_name}' option instead.\n"
f"The '{new_option_name}' option replaces '{legacy_master_option_name}'."
)

if new_option_should_run_here is None and legacy_option_should_run_here is None:
# Neither option specified; the fallback behaviour is to run on the main process
return self.worker_name is None

if (
new_option_should_run_here is not None
and legacy_option_should_run_here is not None
):
# Both options specified; ensure they match!
if new_option_should_run_here != legacy_option_should_run_here:
update_worker_type = (
" and set worker_app: synapse.app.generic_worker"
if self.worker_app == legacy_worker_app_name
else ""
)
# If the values conflict, we suggest the admin removes the legacy option
# for simplicity.
raise ConfigError(
f"Conflicting configuration options: {legacy_master_option_name} (legacy), {new_option_name} (new).\n"
f"Suggestion: remove {legacy_master_option_name}{update_worker_type}.\n"
)

# We've already validated that these aren't conflicting; now just see if
# either is True.
# (By this point, these are either the same value or only one is not None.)
return bool(new_option_should_run_here or legacy_option_should_run_here)

def generate_config_section(self, **kwargs: Any) -> str:
return """\
## Workers ##
Expand Down
2 changes: 1 addition & 1 deletion synapse/handlers/appservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def __init__(self, hs: "HomeServer"):
self.scheduler = hs.get_application_service_scheduler()
self.started_scheduler = False
self.clock = hs.get_clock()
self.notify_appservices = hs.config.appservice.notify_appservices
self.notify_appservices = hs.config.worker.should_notify_appservices
self.event_sources = hs.get_event_sources()
self._msc2409_to_device_messages_enabled = (
hs.config.experimental.msc2409_to_device_messages_enabled
Expand Down
Loading