Skip to content

roles: added dep and var cache to improve performance - #85249

Open
monsdar wants to merge 55 commits into
ansible:develfrom
monsdar:85206-faster-dependency-resolution
Open

roles: added dep and var cache to improve performance#85249
monsdar wants to merge 55 commits into
ansible:develfrom
monsdar:85206-faster-dependency-resolution

Conversation

@monsdar

@monsdar monsdar commented Jun 3, 2025

Copy link
Copy Markdown
Contributor
SUMMARY

These changes introduce caching the role dependencies, default vars and role vars to avoid those being recalculated during each Task. This PR also contains a fix to not add duplicate dependencies to the roles dep-list.

These fixes improve overhead in deployment where a lot of role dependencies are involved, see related issue.

Fixes #85206

ISSUE TYPE
  • Feature Pull Request

@ansibot ansibot added feature This issue/PR relates to a feature request. needs_triage Needs a first human triage before being processed. has_issue labels Jun 3, 2025
@ansibot ansibot added the needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR. label Jun 3, 2025
@ansibot

This comment was marked as outdated.

@monsdar

monsdar commented Jun 3, 2025

Copy link
Copy Markdown
Contributor Author

I need to put down work on this for the time being due to something else coming up. I've planned to continue working on this by the end of June latest.

Comment thread lib/ansible/playbook/role/__init__.py Outdated
@bcoca bcoca removed the needs_triage Needs a first human triage before being processed. label Jun 3, 2025
@monsdar

monsdar commented Jun 3, 2025

Copy link
Copy Markdown
Contributor Author

With the current implementation I reduced the runtime of one of our deployments from 68min to 16min. This deployment in particular uses one of our high-level roles but limits what is actually been executed with tags.

@ansibot

This comment was marked as outdated.

Comment thread lib/ansible/playbook/role/__init__.py Outdated
Comment thread lib/ansible/playbook/role/__init__.py Outdated
Comment thread lib/ansible/playbook/role/__init__.py Outdated
@webknjaz webknjaz added the ci_verified Changes made in this PR are causing tests to fail. label Jun 4, 2025
@ansibot ansibot removed the ci_verified Changes made in this PR are causing tests to fail. label Jun 5, 2025
@webknjaz

webknjaz commented Jun 9, 2025

Copy link
Copy Markdown
Member

NOTE: This PR is still work in progress.

@monsdar if it's WIP, you could mark it as draft. Also, some people put WIP or DNM into the PR title to indicate that it's not ready to be reviewed or merged.

Comment thread changelogs/fragments/85206-faster-dependency-resolution.yml
Comment thread lib/ansible/playbook/role/__init__.py Outdated
Comment thread lib/ansible/playbook/role/__init__.py Outdated
@monsdar

monsdar commented Jun 25, 2025

Copy link
Copy Markdown
Contributor Author

role cache should be at play level

I see that there's already a role_cache at play level. Sorry, I didn't notice when I made myself familiar with the codebase first. I think we should utilize this instead of adding a global cache.

At first glance it looks like the role in the cache isn't updated after initializing it, so any queried dependencies, default and role vars aren't added to the cached role (and therefore are read over and over again).

I'll check how this could be integrated. Switching the PR back to draft status again.

@monsdar
monsdar marked this pull request as draft June 25, 2025 09:24
@ansibot ansibot removed the needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR. label Jun 25, 2025
@monsdar

monsdar commented Jun 26, 2025

Copy link
Copy Markdown
Contributor Author

Seems it's enough to store the deps and vars as cached_properties, rest is done by the already existing cache. The fix is turning out to be pretty simple the more I look into it...

@monsdar
monsdar marked this pull request as ready for review June 27, 2025 08:54
@ansible ansible deleted a comment from ansibot Jul 3, 2025
@ansible ansible deleted a comment from ansibot Jul 3, 2025
@ansibot ansibot added the stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested. label Jul 10, 2025
@ansibot ansibot removed the stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested. label Aug 11, 2025
@monsdar
monsdar requested a review from sivel August 11, 2025 10:34
@monsdar

monsdar commented Aug 11, 2025

Copy link
Copy Markdown
Contributor Author

@sivel with 2.19 out the door would you have time to look into this PR again? I think the implementation is as simple as it gets now, while providing a significant performance boost.

If there are worries about potential breaking changes it'd be possible to move the caching behind a feature flag, e.g. using an env var ANSIBLE_ENABLE_ROLE_CACHE=1 and ANSIBLE_ENABLE_DEP_CACHE=1. This would allow us to test feasibility in the real world before rolling this out as a default.

@ansibot ansibot added the stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested. label Aug 25, 2025
@monsdar

monsdar commented Sep 8, 2025

Copy link
Copy Markdown
Contributor Author

@sivel @bcoca @webknjaz how can we proceed here? I'd like to integrate any needed changes. Waiting for feedback.

@ansibot ansibot added needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR. and removed stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested. labels Sep 8, 2025
@ansibot ansibot removed the needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR. label Sep 8, 2025
@ansibot ansibot added the stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested. label Sep 22, 2025

@mkrizek mkrizek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this breaks backward compatibility, see my comments.

I have a draft pull request that is less invasive and deals just with not re-loading variables needlessly: #85418. The performance improvement is 4x in my testing just with that change. But I have not done extensive testing.

I wonder if we should just leave duplicates in the dependency tree and let them be skipped based on allow_duplicates as we do with "normal" roles.

for dep in self.get_direct_dependencies():
for child_dep in dep._all_dependencies:
if child_dep in self._all_dependencies:
self._all_dependencies.remove(child_dep)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not take allow_duplicates into account. This looks like a breaking change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this is just to get the dependencies of a role, not about getting roles that should be run before this role. Is there a case where a role has a dependency onto another role multiple times? Perhaps when defining a dependency with different variables?

The check in L577 (if child_dep in self._all_dependencies) could account for that by extending the comparison operator.

self._all_dependencies.remove(child_dep)
self._all_dependencies.append(child_dep)
if dep in self._all_dependencies:
self._all_dependencies.remove(child_dep)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this meant to say:

Suggested change
self._all_dependencies.remove(child_dep)
self._all_dependencies.remove(dep)

Otherwise I get:

    self._all_dependencies.remove(child_dep)
                                  ^^^^^^^^^
UnboundLocalError: cannot access local variable 'child_dep' where it is not associated with a value

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I wonder why I didn't catch that before. The code is running for me for several deployments I'm doing 🤔

for role in play.roles:
if role.public:
all_vars = _combine_and_track(all_vars, role.get_default_vars(), "role '%s' defaults" % role.name)
all_vars = _combine_and_track(all_vars, role.get_default_vars(), f"role '{role!r}' defaults")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are any changes in lib/ansible/vars/manager.py necessary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, these are all cosmetic changes allowing for easier debugging. During the lifetime of this MR all other changes have been removed from manager.py

# available by using forward references this seems not to work well with commonly used IDEs.
# Therefore the TYPE_CHECKING hack seems to be a more universal approach, even if not being very elegant.
# References:
# Refs:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am curious as to what is incorrect about "References"?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I wrote the NOTE... comment while working on this and it got merged through another MR. I think this change is the result of me working between those MRs.

@ansibot ansibot added needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR. and removed stale_review Updates were made after the last review and the last review is more than 7 days old. labels Sep 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature This issue/PR relates to a feature request. has_issue needs_revision This PR fails CI tests or a maintainer has requested a review/revision of the PR. stale_ci This PR has been tested by CI more than one week ago. Close and re-open this PR to get it retested.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ansible execution slows down with a large role dependency tree

7 participants