From b15c68418bc398cc296d29ab4cbb074970e41ca7 Mon Sep 17 00:00:00 2001 From: Martin Krizek Date: Mon, 10 Jun 2024 18:38:22 +0200 Subject: [PATCH] Validate and process Handler.listen only once (#83400) (#83405) Fixes #83392 (cherry picked from commit cbbf06893e963252039487e8d4e7a39fc7607e0d) --- .../83392-fix-memory-issues-handlers.yml | 2 ++ lib/ansible/playbook/handler.py | 10 +++++++ lib/ansible/plugins/strategy/__init__.py | 26 +++++-------------- 3 files changed, 18 insertions(+), 20 deletions(-) create mode 100644 changelogs/fragments/83392-fix-memory-issues-handlers.yml diff --git a/changelogs/fragments/83392-fix-memory-issues-handlers.yml b/changelogs/fragments/83392-fix-memory-issues-handlers.yml new file mode 100644 index 00000000000..7e4c2e5599f --- /dev/null +++ b/changelogs/fragments/83392-fix-memory-issues-handlers.yml @@ -0,0 +1,2 @@ +bugfixes: + - Fix rapid memory usage growth when notifying handlers using the ``listen`` keyword (https://github.com/ansible/ansible/issues/83392) diff --git a/lib/ansible/playbook/handler.py b/lib/ansible/playbook/handler.py index 09f122ee089..a7b4a5388b0 100644 --- a/lib/ansible/playbook/handler.py +++ b/lib/ansible/playbook/handler.py @@ -37,6 +37,16 @@ class Handler(Task): ''' returns a human-readable representation of the handler ''' return "HANDLER: %s" % self.get_name() + def _validate_listen(self, attr, name, value): + new_value = self.get_validated_value(name, attr, value, None) + if self._role is not None: + for listener in new_value.copy(): + new_value.extend([ + f"{self._role.get_name(include_role_fqcn=True)} : {listener}", + f"{self._role.get_name(include_role_fqcn=False)} : {listener}", + ]) + setattr(self, name, new_value) + @staticmethod def load(data, block=None, role=None, task_include=None, variable_manager=None, loader=None): t = Handler(block=block, role=role, task_include=task_include) diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index efd69efe9b4..a5822c3b018 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -549,27 +549,13 @@ class StrategyBase: yield handler break - templar.available_variables = {} - seen = [] + seen = set() for handler in handlers: - if listeners := handler.listen: - listeners = handler.get_validated_value( - 'listen', - handler.fattributes.get('listen'), - listeners, - templar, - ) - if handler._role is not None: - for listener in listeners.copy(): - listeners.extend([ - handler._role.get_name(include_role_fqcn=True) + ' : ' + listener, - handler._role.get_name(include_role_fqcn=False) + ' : ' + listener - ]) - if notification in listeners: - if handler.name and handler.name in seen: - continue - seen.append(handler.name) - yield handler + if notification in handler.listen: + if handler.name and handler.name in seen: + continue + seen.add(handler.name) + yield handler @debug_closure def _process_pending_results(self, iterator, one_pass=False, max_passes=None):