Fix ccache usage in GitHub Actions - #10966
Merged
Merged
Conversation
jschmidt-icinga
force-pushed
the
fix-ccache-in-ghas
branch
6 times, most recently
from
July 29, 2026 08:54
fae46be to
2edc1e3
Compare
ccache usage in GitHub Actionsccache usage in GitHub Actions
jschmidt-icinga
marked this pull request as ready for review
July 29, 2026 11:14
jschmidt-icinga
requested review from
Al2Klimov
and removed request for
julianbrost
September 8, 2026 13:50
Previously the cache was never updated, because the key based on matrix.distro always hits and thus the action never updates the cache. This means we were effectively always running with the same stale old cache entry that was generated a long time ago, when the action was introduced, because the only other reason for GitHub to invalidate the cache is if it remains unused for >7 days, effectively meaning never, because there is always *some* activity in this repo. This changes it so each run on the master saves a new cache entry, overwriting the previous one that GitHub will then prune once the combined sizes of the entries are above 10GB. The new entry will however still contain a reasonable amount of old cache entries.
jschmidt-icinga
force-pushed
the
fix-ccache-in-ghas
branch
from
September 10, 2026 09:31
2edc1e3 to
11dbd2b
Compare
Al2Klimov
approved these changes
Sep 16, 2026
Member
Would you mind sharing a bit more on what you tested there? To me, it's not really obvious what to look for there. |
Member
|
Well, that PR-run behaves exactly as promised: a ccache gets restored. While on it, its build takes a few minutes less than the master one. |
|
Backport failed for Please cherry-pick the changes locally and resolve any conflicts. git fetch origin support/2.15
git worktree add -d .worktree/backport-10966-to-support/2.15 origin/support/2.15
cd .worktree/backport-10966-to-support/2.15
git switch --create backport-10966-to-support/2.15
git cherry-pick -x 53dcabd2c853bb29e8243a0748f178dfa18b1a44 e64ef211cfb118378f4d1e9669a1d6a5eec07851 11dbd2bfedfd74340cabf1300964386f69550362 |
1 similar comment
|
Backport failed for Please cherry-pick the changes locally and resolve any conflicts. git fetch origin support/2.15
git worktree add -d .worktree/backport-10966-to-support/2.15 origin/support/2.15
cd .worktree/backport-10966-to-support/2.15
git switch --create backport-10966-to-support/2.15
git cherry-pick -x 53dcabd2c853bb29e8243a0748f178dfa18b1a44 e64ef211cfb118378f4d1e9669a1d6a5eec07851 11dbd2bfedfd74340cabf1300964386f69550362 |
|
Successfully created backport PR for |
Member
|
v2.15 misses c34e030, hence the conflict. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This fixes the
actions/cacheGitHub action we use to save and restore ccache for faster builds in PRs that change little or no code.As a rough estimate, the Linux workflow on this PR, when rebuilt with a warm cache went down from 2h 17m to 1h 25m.
What was broken?
The default cache action only saves to a key when it didn't find an exact match. On current master the save and restore keys are identical and depend on just the
matrix.distrostring. This effectively means one cache entry per distro got written (probably a long time ago), that old entry always gets restored again and again, only ever leads to cache misses and no new entry gets saved because the keys matched exactly. This is the reason we weren't seeing any benefits, even when force-pushing to a PR with zero code changes.To solve this we now save to a key in the format
ccache-<distro>-<platform>-<runid>with the runid being the critical part, and we use therestore-keysattribute of the action to do a prefix-matched restore from any key that matches just the distro and platform. This means that a new cache entry gets stored for each run.Why save only on pushes to master
GitHub
actions/cachehas a total limit of 10 GiB across all cache entries per repo. The eviction for old entries past this limit is LRU. Meanwhileccache's limits are controlled via theCCACHE_MAXSIZEenvironment variable or otherwise defaults to 5 GiB. Lets assume for a moment we left it at the default and each distro accumulated more and more cached objects. At some point, the total objects cached betweenalpine:bashanddebian:12 (linux/386)(the first and last jobs at the time of writing) will weigh enough that they will go over the GHA's maximum of 10 GiB andalpine:bash's cache of the same run will already be evicted by GitHub causing full rebuilds on their next run. Thenamazonlinux:2023gets evicted and so on.So we set
CCACHE_MAXSIZEto a reasonable limit for all distros. I've chosen400 MiB, which fits around four full rebuilds for the average distro (each weighing around 100 MiB), which currently gives us 8 GiB if all distros fully use it, so a bit of headroom remains for other things or additions to the matrix. This avoids any individual run evicting caches of earlier jobs. But there is still a problem when CI jobs for multiple PRs run concurrently, which is very common in this repo. There is no sane way to cap this either, without restricting the max size further into uselessness.So the idea is to always restore the cache from the last master build and save it only on a push to master after a PR has been merged. Since those don't run concurrently, we ensure that there is always a cache that can be read for PR jobs. Obviously how useful that cache is will depend on how much that PR changes (more on that further down).
Invoking
ccachethrough CMakePreviously we were using (or trying to anyway)
ccache's compiler wrapper scripts by prioritizing them in$PATH. This has some issues with compatibility that we can afford to avoid because CMake has native support for launchers likeccachesince version 3.17. By settingCMAKE_<lang>_COMPILER_LAUNCHER=ccacheit just works. The build scripts will now just preface every compiler invocation with theccachecommand.Other changes
ccachein its regular repos, but it does have it in its SPAL repos, which can be installed through thespal-releasepackage, so we do that now.-g0to the very end of the compiler flags. This about halves the cache space required by each run. I don't think debug symbols add anything in the GitHub CI, so that's a good trade-off in my eyes.Future improvements
My idea for the future, once #10936 gets merged is to look into enabling precompiled headers in a separate PR. That would allow us reasonably fast non-unity builds, which would synergize much better with the cache action, because it would be more efficient with the cache size and rebuilds would only ever be what the PR directly touches, making it less of a hassle that we only ever restore the cache from master.