From 7b44e132764a510fbdfe0e03082cc9a501522749 Mon Sep 17 00:00:00 2001 From: QEDeD Date: Sun, 18 Oct 2026 11:27:42 +0200 Subject: [PATCH] Restart Synapse only when its files or image changed With conditional restart (install-* tags), the homeserver entry for Synapse fell through to `restart_necessary: true`, and the worker entries carried no flag at all, which systemd_service_manager also treats as "restart". Every install-all and install-service synapse therefore restarted Synapse and all of its workers, even when nothing had changed. Without workers, systemd also restarted most bridges and bots, whose units require matrix-synapse.service. Compute matrix_synapse_restart_necessary from the tasks that install what Synapse runs: homeserver.yaml, the initial config and signing key, the log config, labels and systemd service, the container image (pulled, self-built or customized), each worker's config, labels and systemd service, and the downloaded or cloned extension modules. Workers follow the same decision, as they read the same files. Synapse also mounts the appservice registration files written by other roles. Add matrix_homeserver_app_service_registration_changed in group_vars/matrix_servers, which checks the registered result of each task installing one of the files listed in matrix_homeserver_container_extra_arguments_auto, so that a changed registration still restarts Synapse and its workers. The double-puppet role did not register its result; it does now. The Synapse image pull and three extension downloads all registered their result as `result`. The downloads run first, so the pull's result overwrote theirs before the restart decision could read them; they now use role-prefixed names. --- group_vars/matrix_servers | 75 ++++++++++++++++++- .../tasks/install.yml | 1 + roles/custom/matrix-synapse/defaults/main.yml | 12 +++ .../ext/encryption-disabler/setup_install.yml | 4 +- .../ext/mjolnir-antispam/setup_install.yml | 1 + .../tasks/ext/rest-auth/setup_install.yml | 4 +- .../ext/shared-secret-auth/setup_install.yml | 4 +- .../synapse-http-antispam/setup_install.yml | 1 + .../synapse-simple-antispam/setup_install.yml | 1 + .../tasks/synapse/setup_install.yml | 27 ++++++- .../workers/util/setup_files_for_worker.yml | 14 ++++ 11 files changed, 135 insertions(+), 9 deletions(-) diff --git a/group_vars/matrix_servers b/group_vars/matrix_servers index a237e0b0d..5ece91a09 100755 --- a/group_vars/matrix_servers +++ b/group_vars/matrix_servers @@ -247,6 +247,78 @@ matrix_homeserver_app_service_config_files_auto: | (['/matrix-steam-bridge-registration.yaml'] if matrix_bridge_steam_enabled else []) }} +# Tells whether any appservice registration file mounted into the homeserver (see `matrix_homeserver_container_extra_arguments_auto`) +# changed during this run, so that the homeserver is restarted to pick it up. +# Each entry is the registered result of the task which installs that file. +# Synapse workers read the same configuration and registration files as the main Synapse process, +# so their entries in `devture_systemd_service_manager_services_list_auto` follow the same restart decision. +matrix_homeserver_app_service_registration_changed: | + {{ + ( + ([matrix_bridge_appservice_discord_registration_result | default({})] if matrix_bridge_appservice_discord_enabled else []) + + + ([matrix_appservice_draupnir_for_all_registration_config_result | default({})] if matrix_appservice_draupnir_for_all_enabled else []) + + + ([matrix_bot_meowlnir_registration_result | default({})] if matrix_bot_meowlnir_enabled else []) + + + ([matrix_appservice_double_puppet_registration_result | default({})] if matrix_appservice_double_puppet_enabled else []) + + + ([matrix_bridge_appservice_irc_registration_result | default({})] if matrix_bridge_appservice_irc_enabled else []) + + + ([matrix_bridge_beeper_linkedin_registration_result | default({})] if matrix_bridge_beeper_linkedin_enabled else []) + + + ([matrix_bridge_beeper_line_registration_result | default({})] if matrix_bridge_beeper_line_enabled else []) + + + ([matrix_bridge_wechat_registration_result | default({})] if matrix_bridge_wechat_enabled else []) + + + ([matrix_bridge_heisenbridge_registration_result | default({})] if matrix_bridge_heisenbridge_enabled else []) + + + ([matrix_bridge_hookshot_registration_result | default({})] if matrix_bridge_hookshot_enabled else []) + + + ([matrix_bridge_mautrix_bluesky_registration_result | default({})] if matrix_bridge_mautrix_bluesky_enabled else []) + + + ([matrix_bridge_rustpush_registration_result | default({})] if matrix_bridge_rustpush_enabled else []) + + + ([matrix_bridge_mautrix_discord_registration_result | default({})] if matrix_bridge_mautrix_discord_enabled else []) + + + ([matrix_bridge_mautrix_slack_registration_result | default({})] if matrix_bridge_mautrix_slack_enabled else []) + + + ([matrix_bridge_mautrix_googlechat_registration_result | default({})] if matrix_bridge_mautrix_googlechat_enabled else []) + + + ([matrix_bridge_mautrix_signal_registration_result | default({})] if matrix_bridge_mautrix_signal_enabled else []) + + + ([matrix_bridge_mautrix_meta_messenger_registration_result | default({})] if matrix_bridge_mautrix_meta_messenger_enabled else []) + + + ([matrix_bridge_mautrix_meta_instagram_registration_result | default({})] if matrix_bridge_mautrix_meta_instagram_enabled else []) + + + ([matrix_bridge_mautrix_telegram_registration_result | default({})] if matrix_bridge_mautrix_telegram_enabled else []) + + + ([matrix_bridge_mautrix_twitter_registration_result | default({})] if matrix_bridge_mautrix_twitter_enabled else []) + + + ([matrix_bridge_mautrix_gmessages_registration_result | default({})] if matrix_bridge_mautrix_gmessages_enabled else []) + + + ([matrix_bridge_mautrix_gvoice_registration_result | default({})] if matrix_bridge_mautrix_gvoice_enabled else []) + + + ([matrix_bridge_mautrix_linkedin_registration_result | default({})] if matrix_bridge_mautrix_linkedin_enabled else []) + + + ([matrix_bridge_mautrix_whatsapp_registration_result | default({})] if matrix_bridge_mautrix_whatsapp_enabled else []) + + + ([matrix_bridge_mautrix_wsproxy_registration_androidsms_result | default({})] if matrix_bridge_mautrix_wsproxy_enabled else []) + + + ([matrix_bridge_mautrix_wsproxy_registration_imessage_result | default({})] if matrix_bridge_mautrix_wsproxy_enabled else []) + + + ([matrix_bridge_mx_puppet_groupme_registration_result | default({})] if matrix_bridge_mx_puppet_groupme_enabled else []) + + + ([matrix_bridge_mx_puppet_steam_registration_result | default({})] if matrix_bridge_mx_puppet_steam_enabled else []) + + + ([matrix_bridge_sms_registration_result | default({})] if matrix_bridge_sms_enabled else []) + + + ([matrix_bridge_steam_registration_result | default({})] if matrix_bridge_steam_enabled else []) + + + ([matrix_cactus_comments_config_appservice_result | default({})] if matrix_cactus_comments_enabled else []) + ) | selectattr('changed', 'defined') | selectattr('changed') | list | length > 0 + }} + matrix_addons_homeserver_container_network: "{{ matrix_playbook_reverse_proxy_container_network if matrix_playbook_internal_matrix_client_api_traefik_entrypoint_enabled else matrix_homeserver_container_network }}" matrix_addons_homeserver_client_api_url: "{{ ('http://' + matrix_playbook_reverse_proxy_hostname + ':' + matrix_playbook_internal_matrix_client_api_traefik_entrypoint_port | string) if matrix_playbook_internal_matrix_client_api_traefik_entrypoint_enabled else matrix_homeserver_container_url }}" matrix_addons_homeserver_systemd_services_list: | @@ -681,6 +753,7 @@ devture_systemd_service_manager_services_list_auto: | else (matrix_continuwuity_restart_necessary | bool) if matrix_homeserver_implementation == 'continuwuity' else (matrix_tuwunel_restart_necessary | bool) if matrix_homeserver_implementation == 'tuwunel' else (matrix_dendrite_restart_necessary | bool) if matrix_homeserver_implementation == 'dendrite' + else ((matrix_synapse_restart_necessary | bool) or (matrix_homeserver_app_service_registration_changed | bool)) if matrix_homeserver_implementation == 'synapse' else true ), 'groups': ['matrix', 'homeservers', matrix_homeserver_implementation], @@ -959,7 +1032,7 @@ devture_systemd_service_manager_services_list_auto: | 'groups': ['matrix', 'traefik-certs-dumper'], }] if traefik_certs_dumper_enabled else []) + - (matrix_synapse_worker_systemd_services_list if matrix_synapse_enabled else []) + (matrix_synapse_worker_systemd_services_list | map('combine', {'restart_necessary': ((matrix_synapse_restart_necessary | bool) or (matrix_homeserver_app_service_registration_changed | bool))}) | list if matrix_synapse_enabled else []) }} ######################################################################## diff --git a/roles/custom/matrix-appservice-double-puppet/tasks/install.yml b/roles/custom/matrix-appservice-double-puppet/tasks/install.yml index 385805d19..220723cce 100644 --- a/roles/custom/matrix-appservice-double-puppet/tasks/install.yml +++ b/roles/custom/matrix-appservice-double-puppet/tasks/install.yml @@ -25,3 +25,4 @@ mode: '0644' owner: "{{ matrix_user_name }}" group: "{{ matrix_group_name }}" + register: matrix_appservice_double_puppet_registration_result diff --git a/roles/custom/matrix-synapse/defaults/main.yml b/roles/custom/matrix-synapse/defaults/main.yml index dbe53c0c1..0e84099a7 100644 --- a/roles/custom/matrix-synapse/defaults/main.yml +++ b/roles/custom/matrix-synapse/defaults/main.yml @@ -135,6 +135,18 @@ matrix_synapse_ext_s3_storage_provider_container_arguments: [] # installation tasks haven't run (e.g., due to --tags skipping them). matrix_synapse_s3_storage_provider_restart_necessary: false +# matrix_synapse_restart_necessary controls whether Synapse and its workers +# will be restarted (when true) or merely started (when false) by the +# systemd service manager role (when conditional restart is enabled). +# +# This value is automatically computed during installation based on whether +# the Synapse or worker configuration, support files, systemd service files, +# container image or extension modules changed. +# The playbook also restarts Synapse when an appservice registration file it mounts changes. +# The default of `false` means "no restart needed" — appropriate when the role's +# installation tasks haven't run (e.g., due to --tags skipping them). +matrix_synapse_restart_necessary: false + matrix_synapse_container_client_api_port: 8008 # Controls the `x_forwarded` setting for the "Insecure HTTP listener (Client API)". diff --git a/roles/custom/matrix-synapse/tasks/ext/encryption-disabler/setup_install.yml b/roles/custom/matrix-synapse/tasks/ext/encryption-disabler/setup_install.yml index b3d79f283..7fcb5f6fe 100644 --- a/roles/custom/matrix-synapse/tasks/ext/encryption-disabler/setup_install.yml +++ b/roles/custom/matrix-synapse/tasks/ext/encryption-disabler/setup_install.yml @@ -13,10 +13,10 @@ mode: '0440' owner: "{{ matrix_synapse_uid }}" group: "{{ matrix_synapse_gid }}" - register: result + register: matrix_synapse_ext_encryption_disabler_download_result retries: "{{ devture_playbook_help_geturl_retries_count }}" delay: "{{ devture_playbook_help_geturl_retries_delay }}" - until: result is not failed + until: matrix_synapse_ext_encryption_disabler_download_result is not failed - ansible.builtin.set_fact: matrix_synapse_modules: | diff --git a/roles/custom/matrix-synapse/tasks/ext/mjolnir-antispam/setup_install.yml b/roles/custom/matrix-synapse/tasks/ext/mjolnir-antispam/setup_install.yml index 5e4c1b224..41e6277ca 100644 --- a/roles/custom/matrix-synapse/tasks/ext/mjolnir-antispam/setup_install.yml +++ b/roles/custom/matrix-synapse/tasks/ext/mjolnir-antispam/setup_install.yml @@ -34,6 +34,7 @@ GIT_CONFIG_COUNT: "1" GIT_CONFIG_KEY_0: safe.directory GIT_CONFIG_VALUE_0: "{{ matrix_synapse_ext_path }}/mjolnir" + register: matrix_synapse_ext_spam_checker_mjolnir_antispam_git_result - ansible.builtin.set_fact: matrix_synapse_modules: > diff --git a/roles/custom/matrix-synapse/tasks/ext/rest-auth/setup_install.yml b/roles/custom/matrix-synapse/tasks/ext/rest-auth/setup_install.yml index 4df5d68ba..a13b2389a 100644 --- a/roles/custom/matrix-synapse/tasks/ext/rest-auth/setup_install.yml +++ b/roles/custom/matrix-synapse/tasks/ext/rest-auth/setup_install.yml @@ -20,10 +20,10 @@ mode: '0440' owner: "{{ matrix_synapse_uid }}" group: "{{ matrix_synapse_gid }}" - register: result + register: matrix_synapse_ext_password_provider_rest_auth_download_result retries: "{{ devture_playbook_help_geturl_retries_count }}" delay: "{{ devture_playbook_help_geturl_retries_delay }}" - until: result is not failed + until: matrix_synapse_ext_password_provider_rest_auth_download_result is not failed - ansible.builtin.set_fact: matrix_synapse_password_providers_enabled: true diff --git a/roles/custom/matrix-synapse/tasks/ext/shared-secret-auth/setup_install.yml b/roles/custom/matrix-synapse/tasks/ext/shared-secret-auth/setup_install.yml index 71198c84c..5d121b55b 100644 --- a/roles/custom/matrix-synapse/tasks/ext/shared-secret-auth/setup_install.yml +++ b/roles/custom/matrix-synapse/tasks/ext/shared-secret-auth/setup_install.yml @@ -25,10 +25,10 @@ mode: '0440' owner: "{{ matrix_synapse_uid }}" group: "{{ matrix_synapse_gid }}" - register: result + register: matrix_synapse_ext_password_provider_shared_secret_auth_download_result retries: "{{ devture_playbook_help_geturl_retries_count }}" delay: "{{ devture_playbook_help_geturl_retries_delay }}" - until: result is not failed + until: matrix_synapse_ext_password_provider_shared_secret_auth_download_result is not failed - ansible.builtin.set_fact: matrix_synapse_modules: | diff --git a/roles/custom/matrix-synapse/tasks/ext/synapse-http-antispam/setup_install.yml b/roles/custom/matrix-synapse/tasks/ext/synapse-http-antispam/setup_install.yml index 88e44b480..6e6bf9265 100644 --- a/roles/custom/matrix-synapse/tasks/ext/synapse-http-antispam/setup_install.yml +++ b/roles/custom/matrix-synapse/tasks/ext/synapse-http-antispam/setup_install.yml @@ -32,6 +32,7 @@ GIT_CONFIG_COUNT: "1" GIT_CONFIG_KEY_0: safe.directory GIT_CONFIG_VALUE_0: "{{ matrix_synapse_ext_path }}/synapse-http-antispam" + register: matrix_synapse_ext_synapse_http_antispam_git_result - ansible.builtin.set_fact: matrix_synapse_modules: > diff --git a/roles/custom/matrix-synapse/tasks/ext/synapse-simple-antispam/setup_install.yml b/roles/custom/matrix-synapse/tasks/ext/synapse-simple-antispam/setup_install.yml index f45ec1917..4d405301e 100644 --- a/roles/custom/matrix-synapse/tasks/ext/synapse-simple-antispam/setup_install.yml +++ b/roles/custom/matrix-synapse/tasks/ext/synapse-simple-antispam/setup_install.yml @@ -41,6 +41,7 @@ GIT_CONFIG_COUNT: "1" GIT_CONFIG_KEY_0: safe.directory GIT_CONFIG_VALUE_0: "{{ matrix_synapse_ext_path }}/synapse-simple-antispam" + register: matrix_synapse_ext_spam_checker_synapse_simple_antispam_git_result - ansible.builtin.set_fact: matrix_synapse_modules: > diff --git a/roles/custom/matrix-synapse/tasks/synapse/setup_install.yml b/roles/custom/matrix-synapse/tasks/synapse/setup_install.yml index 4f0926ead..4527a6f48 100644 --- a/roles/custom/matrix-synapse/tasks/synapse/setup_install.yml +++ b/roles/custom/matrix-synapse/tasks/synapse/setup_install.yml @@ -79,16 +79,17 @@ DOCKER_BUILDKIT: 1 changed_when: true when: "matrix_synapse_git_pull_results.changed | bool or matrix_synapse_container_image_check_result.stdout == ''" + register: matrix_synapse_container_image_build_result - name: Ensure Synapse Docker image is pulled community.docker.docker_image_pull: name: "{{ matrix_synapse_container_image }}" pull: always when: "not matrix_synapse_container_image_self_build" - register: result + register: matrix_synapse_container_image_pull_result retries: "{{ devture_playbook_help_container_retries_count }}" delay: "{{ devture_playbook_help_container_retries_delay }}" - until: result is not failed + until: matrix_synapse_container_image_pull_result is not failed - when: "matrix_synapse_container_image_customizations_enabled | bool" block: @@ -133,6 +134,7 @@ {{ matrix_synapse_container_image }} generate creates: "{{ matrix_synapse_config_dir_path }}/{{ matrix_server_fqn_matrix }}.signing.key" + register: matrix_synapse_initial_config_result - name: Ensure Synapse homeserver config installed ansible.builtin.copy: @@ -141,6 +143,7 @@ mode: '0644' owner: "{{ matrix_synapse_uid }}" group: "{{ matrix_synapse_gid }}" + register: matrix_synapse_config_result - name: Ensure Synapse container network is created when: matrix_synapse_container_network != 'host' @@ -168,6 +171,7 @@ - src: "{{ role_path }}/templates/synapse/systemd/matrix-synapse.service.j2" dest: "{{ devture_systemd_docker_base_systemd_path }}/matrix-synapse.service" mode: '0644' + register: matrix_synapse_support_files_result - name: Generate sample prometheus.yml for external scraping ansible.builtin.template: @@ -177,3 +181,22 @@ group: "{{ matrix_synapse_gid }}" mode: '0644' when: matrix_synapse_metrics_proxying_enabled | bool + +- name: Determine whether Synapse needs a restart + ansible.builtin.set_fact: + matrix_synapse_restart_necessary: >- + {{ + matrix_synapse_config_result.changed | default(false) + or matrix_synapse_initial_config_result.changed | default(false) + or matrix_synapse_support_files_result.changed | default(false) + or matrix_synapse_container_image_pull_result.changed | default(false) + or matrix_synapse_container_image_build_result.changed | default(false) + or matrix_synapse_container_image_customized_build_result.changed | default(false) + or matrix_synapse_workers_files_changed | default(false) | bool + or matrix_synapse_ext_encryption_disabler_download_result.changed | default(false) + or matrix_synapse_ext_password_provider_rest_auth_download_result.changed | default(false) + or matrix_synapse_ext_password_provider_shared_secret_auth_download_result.changed | default(false) + or matrix_synapse_ext_spam_checker_mjolnir_antispam_git_result.changed | default(false) + or matrix_synapse_ext_spam_checker_synapse_simple_antispam_git_result.changed | default(false) + or matrix_synapse_ext_synapse_http_antispam_git_result.changed | default(false) + }} diff --git a/roles/custom/matrix-synapse/tasks/synapse/workers/util/setup_files_for_worker.yml b/roles/custom/matrix-synapse/tasks/synapse/workers/util/setup_files_for_worker.yml index d6c55fb25..e731da24f 100644 --- a/roles/custom/matrix-synapse/tasks/synapse/workers/util/setup_files_for_worker.yml +++ b/roles/custom/matrix-synapse/tasks/synapse/workers/util/setup_files_for_worker.yml @@ -20,6 +20,7 @@ mode: '0644' owner: "{{ matrix_synapse_uid }}" group: "{{ matrix_synapse_gid }}" + register: matrix_synapse_worker_config_result - name: Ensure labels exists for {{ matrix_synapse_worker_systemd_service_name }} ansible.builtin.template: @@ -28,9 +29,22 @@ mode: '0644' owner: "{{ matrix_synapse_uid }}" group: "{{ matrix_synapse_gid }}" + register: matrix_synapse_worker_labels_result - name: Ensure systemd service exists for {{ matrix_synapse_worker_systemd_service_name }} ansible.builtin.template: src: "{{ role_path }}/templates/synapse/systemd/matrix-synapse-worker.service.j2" dest: "{{ devture_systemd_docker_base_systemd_path }}/{{ matrix_synapse_worker_systemd_service_name }}.service" mode: '0644' + register: matrix_synapse_worker_systemd_service_result + +# Workers share Synapse's restart decision (see `matrix_synapse_restart_necessary`), +# so a change to any one worker's files marks it as needed. +- ansible.builtin.set_fact: + matrix_synapse_workers_files_changed: >- + {{ + matrix_synapse_workers_files_changed | default(false) | bool + or matrix_synapse_worker_config_result.changed | default(false) + or matrix_synapse_worker_labels_result.changed | default(false) + or matrix_synapse_worker_systemd_service_result.changed | default(false) + }}