ci: share the dependency caches between pull requests - #25479
Conversation
|
Findings, most significant first:
|
|
| # that ought to fail, for a module the branch renamed or removed. | ||
| path: | | ||
| ~/.m2/repository | ||
| !~/.m2/repository/com/vaadin |
There was a problem hiding this comment.
!~/.m2/repository/com/vaadin is a no-op, so the stale-jar hazard remains
Head lines 79-81, 127-129, 243-245, 409-411, 514-516 (and pre-existing at 675-677).
actions/cache resolves path through resolvePaths, which calls glob.create(..., {implicitDescendants: false}) — confirmed in the pinned bundle 27d5ce7f at dist/restore/index.js:1548. Under that option a non-wildcard directory pattern yields only the directory itself; the traversal drops children because partialMatch ignores deeper paths, and negation only clears a match on an identical path (result &= ~pattern.match(itemPath)). Same semantics in the pinned bundle at :6223 and :6236.
Run against a fixture .m2 containing both com/vaadin/flow-server/999.99-SNAPSHOT/flow-server.jar and org/apache/commons/commons.jar:
--- PR pattern (dir + negated subdir) ---
[ '.../fakehome/.m2/repository' ] ← exclusion had no effect
--- wildcard form ---
[ '.../repository', '.../repository/com',
'.../repository/org', '.../repository/org/apache',
'.../repository/org/apache/commons',
'.../repository/org/apache/commons/commons.jar' ] ← com/vaadin excluded
Only ~/.m2/repository reaches the tar manifest, and tar recurses, so com/vaadin/** is still saved. The ~ is expanded fine in the negated pattern (negation is stripped before fixupPattern) — the pattern isn't malformed, just inert.
Two ways out: the wildcard form ~/.m2/repository/** + !~/.m2/repository/com/vaadin/**, which works but enumerates every file in a ~100k-entry local repository; or keep path: ~/.m2/repository and delete the directory in a final regular step (rm -rf ~/.m2/repository/com/vaadin), since cache's post step runs after all regular steps. The second is cheaper and needs no glob subtleties.
Seems this is broken also on the API diff
|
I'll reconsider the separated cache workflow. Validation is getting too complicated. |
bd78830 to
fafa644
Compare
|
Restructured the PR and updated the description |
Caching it by hand meant every pull request saved its own copy of it. Those copies held 7 of the 10 GB the repository is allowed, so entries evicted each other within hours and even a second run of the same pull request started cold. Leaving the writing to pushes is what fixes that; the maintenance branches keep filling their own scope. The action's default cache provider is a proprietary component that its MIT licence does not cover and is free only as a preview, so this asks for the MIT one. That provider saves one entry over ~/.gradle/caches and ~/.gradle/wrapper, the same tree the hand written step cached, and deliberately passes no restore keys: a change to any build script or wrapper file now starts from an empty Gradle home rather than a near match, where the replaced step fell back on a prefix.
fafa644 to
efe3512
Compare
A run can only read caches from its own ref and from the default branch. Since #24103 dropped main from the validation push trigger, nothing writes to refs/heads/main any more, and a merge queue branch is single use, so its caches are never read either. Every pull request starts with an empty local repository, downloads the whole dependency tree, and saves a copy only it can read. One branch went from 4 minutes to 13.6 minutes in a day without changing. Putting main back in the validation trigger would fix that, but it also brings back the second full run per merge that #24103 removed: the build, four unit jobs, a fifteen way integration matrix and Sonar, for a tree the merge queue has just validated, with flaky tests now able to redden main. A single job gets the same cache: the build job is what fills an empty local repository, and every other job waits on it, so the entry it saves is the one they all read anyway. The Flow artifacts are removed before the cache is saved, since an entry every branch restores should not carry one branch's 999.99-SNAPSHOT jars, which could satisfy a resolution that ought to fail.
efe3512 to
08a6998
Compare
|
On the cancelled run: cancelling does not remove the entry saved before it. The Queueing instead would build one superseded commit after another while the run And there is usually time to finish. Over the last 18 days the median gap |
|



A run can only read caches from its own ref and from the default branch. Since
#24103 dropped main from the validation push trigger, nothing writes to
refs/heads/mainany more, and a merge queue branch is single use, so itscaches are never read either.
So every pull request starts with an empty local repository, downloads the whole
dependency tree, and then saves a copy only it can read. Those copies do not
fit: 10.7 GB against a 10 GB limit, of which Gradle was 7.1 GB. Entries evict
each other within hours, and even a second run of the same pull request starts
cold. One branch went from 4 minutes to 13.6 minutes in a day without changing.
validation.ymlletsgradle/actions/setup-gradlecache the Gradle home,instead of caching the whole
~/.gradle/cachestree by hand. It keepsdeduplicated entries and, as configured here, only writes on a push, so pull
requests read one instead of each saving 1.4 GB. That alone takes 7.1 GB out of
the limit. The action's default cache provider is a proprietary component that
its MIT licence does not cover and is free only as a preview, so this asks for
the MIT one.
warm-caches.ymlis a new workflow that builds main once per merge, so thedefault branch holds an entry every pull request can read. It is a single job,
not the validation matrix, so it does not bring back the duplicate validation
that #24103 removed.
The Flow artifacts are removed before the cache is saved. An entry every branch
restores should not carry one branch's 999.99-SNAPSHOT jars, which could satisfy
a resolution that ought to fail.