From 7b412ffa7622cf7673895cc2b0c2197fea6d64f3 Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Thu, 5 Nov 2026 11:51:23 +0000 Subject: [PATCH] perf(web-app-matrix): write the Synapse vhost once instead of twice per deploy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the ansible flavor the Synapse vhost had two writers: the shared front-proxy include rendered proxy.conf.j2 for the Synapse domain, and a second task rendered flavor/ansible/synapse.conf.j2 to the same file right after it. The two contents differed, so the file changed on every deploy and notified restart-openresty, which in swarm is a service update --force of OpenResty in every pass (WordPress#0, fork run 41926377524). proxy.conf.j2 now includes the ansible-flavor Synapse template for the Synapse domain itself, and the second task is gone. The rendered vhost is identical to the former final content across TLS, Element Call, clearnet and onion, so the 8448 listener and the Matrix RTC locations stay. A real change now goes through the front-proxy's reload, which reaches OpenResty in swarm since 7e5da9422c. 🎯 Fix: 90 % 💥 Break: 4 % 🌱 Efficiency: – ⚡ Speed: – 🛡️ Security: ~100 % 🏆 Score: 86 🚦 Gate: ✅ 90/5, efficiency and speed <= 100, security 100 % Co-Authored-By: Claude Opus 5.5 --- .../tasks/flavor/ansible/05_proxy_wiring.yml | 10 -- roles/web-app-matrix/templates/proxy.conf.j2 | 4 +- .../test_synapse_vhost_single_writer.py | 91 +++++++++++++++++++ 3 files changed, 94 insertions(+), 11 deletions(-) create mode 100644 tests/unit/python/roles/web-app-matrix/test_synapse_vhost_single_writer.py diff --git a/roles/web-app-matrix/tasks/flavor/ansible/05_proxy_wiring.yml b/roles/web-app-matrix/tasks/flavor/ansible/05_proxy_wiring.yml index 48390e8be2..5645763917 100644 --- a/roles/web-app-matrix/tasks/flavor/ansible/05_proxy_wiring.yml +++ b/roles/web-app-matrix/tasks/flavor/ansible/05_proxy_wiring.yml @@ -183,16 +183,6 @@ http_port: "{{ MATRIX_SYNAPSE_PORT }}" proxy_internal_port: "{{ MATRIX_SYNAPSE_CONTAINER_PORT }}" -- name: "🔀 front-proxy: Synapse federation listener and Matrix RTC paths" - stack_host_template: - src: "flavor/ansible/synapse.conf.j2" - dest: "{{ lookup('nginx', 'files.domain', MATRIX_SYNAPSE_DOMAIN) }}" - vars: - domain: "{{ MATRIX_SYNAPSE_DOMAIN }}" - http_port: "{{ MATRIX_SYNAPSE_PORT }}" - proxy_internal_port: "{{ MATRIX_SYNAPSE_CONTAINER_PORT }}" - notify: restart-openresty - - name: "🔀 front-proxy: federation delegation" ansible.builtin.include_tasks: "{{ role_path }}/tasks/utils/well_known.yml" diff --git a/roles/web-app-matrix/templates/proxy.conf.j2 b/roles/web-app-matrix/templates/proxy.conf.j2 index 36a1c0abd3..1f10f608a2 100644 --- a/roles/web-app-matrix/templates/proxy.conf.j2 +++ b/roles/web-app-matrix/templates/proxy.conf.j2 @@ -1,4 +1,6 @@ -{% if domain == MATRIX_SYNAPSE_DOMAIN %} +{% if domain == MATRIX_SYNAPSE_DOMAIN and matrix_flavor == 'ansible' %} +{% include 'roles/web-app-matrix/templates/flavor/ansible/synapse.conf.j2' %} +{% elif domain == MATRIX_SYNAPSE_DOMAIN %} {% include 'roles/web-app-matrix/templates/synapse.conf.j2' %} {% else %} {% include 'roles/sys-svc-proxy/templates/vhost/basic.conf.j2' %} diff --git a/tests/unit/python/roles/web-app-matrix/test_synapse_vhost_single_writer.py b/tests/unit/python/roles/web-app-matrix/test_synapse_vhost_single_writer.py new file mode 100644 index 0000000000..d4062e84c3 --- /dev/null +++ b/tests/unit/python/roles/web-app-matrix/test_synapse_vhost_single_writer.py @@ -0,0 +1,91 @@ +from __future__ import annotations + +import unittest + +from jinja2 import ( + ChoiceLoader, + DictLoader, + Environment, + FileSystemLoader, + StrictUndefined, +) + +from utils.cache.files import PROJECT_ROOT, read_text + +PROXY_TEMPLATE = "roles/web-app-matrix/templates/proxy.conf.j2" +TASKS_DIR = PROJECT_ROOT / "roles/web-app-matrix/tasks" +SYNAPSE_VHOST_DEST = "lookup('nginx', 'files.domain', MATRIX_SYNAPSE_DOMAIN)" + +STUBS = { + "roles/web-app-matrix/templates/flavor/ansible/synapse.conf.j2": "ANSIBLE_SYNAPSE", + "roles/web-app-matrix/templates/synapse.conf.j2": "COMPOSE_SYNAPSE", + "roles/sys-svc-proxy/templates/vhost/basic.conf.j2": "BASIC", +} + +SYNAPSE_DOMAIN = "matrix.example.test" + + +def _render(*, domain: str, flavor: str) -> str: + """Render the front-proxy template of web-app-matrix with stubbed includes. + + Args: + domain: vhost domain the front proxy renders. + flavor: value of the matrix_flavor fact. + + Returns: + The rendered marker of the included vhost template. + """ + env = Environment( + autoescape=True, + undefined=StrictUndefined, + loader=ChoiceLoader([DictLoader(STUBS), FileSystemLoader(str(PROJECT_ROOT))]), + ) + return ( + env.get_template(PROXY_TEMPLATE) + .render( + domain=domain, + matrix_flavor=flavor, + MATRIX_SYNAPSE_DOMAIN=SYNAPSE_DOMAIN, + ) + .strip() + ) + + +class TestSynapseVhostSingleWriter(unittest.TestCase): + def test_ansible_flavor_front_proxy_renders_the_ansible_synapse_vhost( + self, + ) -> None: + self.assertEqual( + _render(domain=SYNAPSE_DOMAIN, flavor="ansible"), "ANSIBLE_SYNAPSE" + ) + + def test_compose_flavor_front_proxy_renders_the_compose_synapse_vhost( + self, + ) -> None: + self.assertEqual( + _render(domain=SYNAPSE_DOMAIN, flavor="compose"), "COMPOSE_SYNAPSE" + ) + + def test_other_domains_render_the_basic_vhost(self) -> None: + for flavor in ("ansible", "compose"): + with self.subTest(flavor=flavor): + self.assertEqual( + _render(domain="element.example.test", flavor=flavor), "BASIC" + ) + + def test_no_matrix_task_rewrites_the_front_proxy_synapse_vhost(self) -> None: + writers = [ + str(path.relative_to(PROJECT_ROOT)) + for path in sorted(TASKS_DIR.rglob("*.yml")) + if SYNAPSE_VHOST_DEST in read_text(str(path)) + ] + self.assertEqual( + writers, + [], + "sys-stk-front-proxy owns the Synapse vhost; a second writer flips " + "its content every deploy and forces an OpenResty redeploy", + ) + + +if __name__ == "__main__": + unittest.main()