perf(spot): carry the rendered users next to the applications

Ansible forks a fresh worker per task, so every lookup('users') in a forked worker re-rendered the whole user catalogue (298 users, 0.3 to 0.5 s locally and about 1 s on the runner, 1 s locally once a users password is vaulted): _MERGED_USERS_CACHE lives in the process and had no carrier fact.

applications_carrier now also parks lookup('users', carrier=True) in _INFINITO_USERS_RENDERED at the same park points, and clear drops both facts. get_merged_users serves the fact to templated reads when its key matches: the applications carrier key (roles dir, applications, users, DOMAIN_PRIMARY, SYSTEM_EMAIL_DOMAIN, group_names, token store digest) plus inventory_hostname, so a carrier a run_once park broadcasts never answers another host with its become password. A park whose users carrier still matches leaves it in place instead of returning 119 KB again, so parks cost what they cost before.

Real ansible-playbook harness on 2.20.6 and 2.21.5 with the real roles tree: users renders per users-reading task after a park 1 -> 0, task 0.56 -> 0.08 s (vaulted 1.21 -> 0.17 s); a rotated token, a rotated inventory password, a cleared carrier and another host's carrier all re-render; 32 of 32 probe digests equal HEAD's. Removing the carrier check fails three unit tests. The stalwart swarm roundtrip pass 1 and its DR drill ran green with it on all nodes, with no 401 from JMAP, msmtp or LDAP.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5.5 committed 2026-10-29 15:53:34 +01:00
1 parent 5689812fbe
commit 91f29d332e
10 files changed
+460 -69

No files matched your search

+147
View File
@@ -14,12 +14,14 @@ from __future__ import annotations
import tempfile
import textwrap
import unittest
import unittest.mock as mock
from collections import OrderedDict
from pathlib import Path
from utils.cache import _reset_cache_for_tests
from utils.cache import base as cache_base
from utils.cache import users as cache_users
from utils.cache.carrier import USERS_RENDERED_FACT, merged_users_cache_key
from utils.cache.users import store as cache_users_store
from utils.cache.yaml import dump_yaml_str
from utils.roles.mapping import ROLE_FILE_META_SERVICES, ROLE_FILE_META_USERS
@@ -415,6 +417,151 @@ class TestGetMergedUsersTokenSource(unittest.TestCase):
self.assertEqual(after["foo"]["tokens"]["web-app-foo"], "minted")
class _Templar:
def __init__(self) -> None:
self.available_variables: dict = {}
def template(self, value, fail_on_undefined=False):
return value
class TestGetMergedUsersCarrierFact(unittest.TestCase):
def setUp(self) -> None:
_reset_cache_for_tests()
@staticmethod
def _variables(host: str = "h1") -> dict:
return {
"inventory_hostname": host,
"users": {"foo": {"email": "foo@example.org"}},
cache_users_store.STORE_FACT: {
"foo": {"tokens": {"web-app-foo": "minted"}}
},
}
@staticmethod
def _park(roles: Path, variables: dict, payload: dict) -> dict:
key = merged_users_cache_key(variables, roles_dir=roles)
variables[USERS_RENDERED_FACT] = {
"key": [key[0], list(key[1])],
"users": payload,
}
return payload
@staticmethod
def _merged(roles: Path, variables: dict, templar=None) -> tuple:
with mock.patch.object(
cache_users,
"_render_with_templar",
side_effect=lambda value, **_: {"rendered": value},
) as render:
merged = cache_users.get_merged_users(
variables=variables,
roles_dir=roles,
templar=_Templar() if templar is None else templar,
)
return merged, render.call_count
def test_matching_key_serves_the_carried_payload_without_rendering(self):
with tempfile.TemporaryDirectory() as tmp:
roles = _seed_minimal_user_role(Path(tmp))
variables = self._variables()
carried = self._park(roles, variables, {"foo": {"carried": True}})
merged, renders = self._merged(roles, variables)
self.assertIs(merged, carried)
self.assertEqual(renders, 0)
def test_the_carried_payload_answers_the_rest_of_the_process(self):
with tempfile.TemporaryDirectory() as tmp:
roles = _seed_minimal_user_role(Path(tmp))
variables = self._variables()
carried = self._park(roles, variables, {"foo": {"carried": True}})
self._merged(roles, variables)
del variables[USERS_RENDERED_FACT]
merged, renders = self._merged(roles, variables)
self.assertIs(merged, carried)
self.assertEqual(renders, 0)
def test_stale_key_falls_back_to_a_fresh_render(self):
with tempfile.TemporaryDirectory() as tmp:
roles = _seed_minimal_user_role(Path(tmp))
variables = self._variables()
key = merged_users_cache_key(variables, roles_dir=roles)
variables[USERS_RENDERED_FACT] = {
"key": [key[0], ["stale"] * len(key[1])],
"users": {"foo": {"carried": True}},
}
merged, renders = self._merged(roles, variables)
self.assertEqual(renders, 1)
self.assertEqual(merged["rendered"]["foo"]["email"], "foo@example.org")
def test_another_hosts_carrier_is_rejected(self):
with tempfile.TemporaryDirectory() as tmp:
roles = _seed_minimal_user_role(Path(tmp))
variables = self._variables("h1")
self._park(roles, variables, {"foo": {"carried": True}})
variables["inventory_hostname"] = "h2"
merged, renders = self._merged(roles, variables)
self.assertEqual(renders, 1)
self.assertNotIn("carried", merged["rendered"]["foo"])
def test_a_rotated_store_token_rejects_the_carrier(self):
with tempfile.TemporaryDirectory() as tmp:
roles = _seed_minimal_user_role(Path(tmp))
variables = self._variables()
self._park(roles, variables, {"foo": {"carried": True}})
variables[cache_users_store.STORE_FACT] = {
"foo": {"tokens": {"web-app-foo": "rotated"}}
}
merged, renders = self._merged(roles, variables)
self.assertEqual(renders, 1)
self.assertEqual(
merged["rendered"]["foo"]["tokens"]["web-app-foo"], "rotated"
)
def test_a_rotated_inventory_password_rejects_the_carrier(self):
with tempfile.TemporaryDirectory() as tmp:
roles = _seed_minimal_user_role(Path(tmp))
variables = self._variables()
variables["users"]["foo"]["password"] = "before"
self._park(roles, variables, {"foo": {"carried": True}})
variables["users"] = {"foo": {"password": "after"}}
merged, renders = self._merged(roles, variables)
self.assertEqual(renders, 1)
self.assertEqual(merged["rendered"]["foo"]["password"], "after")
def test_without_a_templar_the_carrier_is_ignored(self):
with tempfile.TemporaryDirectory() as tmp:
roles = _seed_minimal_user_role(Path(tmp))
variables = self._variables()
self._park(roles, variables, {"foo": {"carried": True}})
merged = cache_users.get_merged_users(
variables=variables, roles_dir=roles, templar=None
)
self.assertNotIn("carried", merged["foo"])
self.assertEqual(merged["foo"]["email"], "foo@example.org")
def test_malformed_carrier_is_ignored(self):
with tempfile.TemporaryDirectory() as tmp:
roles = _seed_minimal_user_role(Path(tmp))
key = merged_users_cache_key(self._variables(), roles_dir=roles)
for carrier in (
"not-a-mapping",
{"key": "x", "users": {}},
{"key": [key[0], list(key[1])], "users": "not-a-mapping"},
{},
):
with self.subTest(carrier=carrier):
_reset_cache_for_tests()
variables = self._variables()
variables[USERS_RENDERED_FACT] = carrier
merged, renders = self._merged(roles, variables)
self.assertEqual(renders, 1)
self.assertEqual(
merged["rendered"]["foo"]["email"], "foo@example.org"
)
class TestStoreSignature(unittest.TestCase):
def setUp(self) -> None:
_reset_cache_for_tests()