chore: run only the cache-writing jobs on push to main - #5930
Conversation
`pr_build_linux` runs on push to main solely to keep main's actions/cache entries warm, because a pull request can only restore caches saved on its own branch or on main and the merge queue's `gh-readonly-queue/*` branch takes its caches with it. It has been doing that by running the whole pipeline: 587 runner-minutes a push, of which 514 is lints, the 5x4 linux-test matrix and the TPC-H/TPC-DS query passes that the queue already ran against the exact tree that landed. Add a `cache-refresh-only` input to pr_build_linux.yml, set by ci.yml on push, that reduces the workflow to the four jobs owning a cache entry plus the 40 second `lint` they depend on, and skips the TPC query passes while keeping dataset generation. Routing is the existing spark_4_1 / spark_4_1_hive shape: a second POLICY output, `build_linux_full`, with tiers ["pr", "queue"]. check-ci-config.py gains a sixth invariant so a job added to that workflow without the guard, or a caller that drops the input, fails preflight instead of silently putting the full pipeline back on the push tier. Fixes apache#5929
mbutrovich
left a comment
There was a problem hiding this comment.
Description made sense and changes look reasonable. Thanks @andygrove!
comphead
left a comment
There was a problem hiding this comment.
Makes sense thanks @andygrove
…fresh Conflict in dev/ci/check-ci-config.py: apache#5885 removed spark_3_4 from SPARK_OPT_IN and added SPARK_DEPRECATED in the same list-shaped hunk this branch touches to add build_linux_full to PR_TIER. Took both. apache#5885 also documents the push tier in docs/source/contributor-guide/ci.md, which this branch had not updated, so the cache-refresh-only mode and the new check-ci-config invariant are described there too.
|
Merged The more useful thing the merge turned up is that #5885 documents the push tier in I re-ran the four mutation cases after the merge rather than assuming they survived it, plus a fifth for #5885's own invariant (putting |
Which issue does this PR close?
Closes #5929.
Rationale for this change
ci.ymlrunspr_build_linuxon push to main, and the reason isactions/cachescoping rather than coverage: a pull request can only restorecaches saved on its own branch or on
main, and the merge queue builds on athrowaway
gh-readonly-queue/*branch whose caches are deleted with it. Withouta push run, main's caches go stale and every later pull request pays the delta.
It has been doing that by running the entire pipeline. Averaged over four recent
push-to-main runs,
pr_build_linuxcosts 587 runner-minutes a push, and allbut ~73 of that is lints, the 5x4
linux-testmatrix and the TPC-H/TPC-DS querypasses — none of which test anything new, because the queue already ran the same
jobs against the exact tree that landed.
linux-test(5 profiles x 4 suites)Build Native Librarycargo-ciVerify TPC-DS Resultsjava-mavenubuntu-latest/rust-testcargo-debugLint Java(matrix)java-mavenCeleborn(2 versions)java-mavenVerify TPC-H Resultsjava-mavenBuild Spark 4.1, JDK 17java-mavenLintneeds:itLint Scala (syntactic)At the 8-10 pushes a day this repository sees, that is roughly 4,100-5,200
runner-minutes a day. For scale, all of
ci.ymlcame to 1,257 runner-hours on2026-09-12 (44,440 min on pull requests, 26,395 in the queue, 4,588 on push).
What changes are included in this PR?
pr_build_linux.ymlgains acache-refresh-onlyboolean input. When set,only the jobs that own an
actions/cacheentry run:build-native(
cargo-ci),linux-test-rust(cargo-debug), the two TPC-H/TPC-DS jobs(the SF=1 datasets and the shared
java-mavenentry), andlint, which is 40seconds and which both native jobs
needs:. Everything else carriesif: ${{ !inputs.cache-refresh-only }}. Inside the two TPC jobs only thequery passes are skipped; data generation still runs, or the dataset caches
would never be written.
compute-changes.pygainsbuild_linux_full, tiers["pr", "queue"],sharing
build_linux'sFILTERSby assignment. This is the existingspark_4_1/spark_4_1_hiveshape: two POLICY outputs feeding one call.ci.ymlfolds it in ascache-refresh-only: ${{ needs.changes.outputs.build_linux_full != 'true' }}.check-ci-config.pygains a sixth invariant,check_cache_refresh_scope:every job in
pr_build_linux.ymlmust be either listed inCACHE_REFRESH_JOBS(with the cache entry it writes) or carry the guard, andci.ymlmust actually pass the input. Both failure modes are silent —the runner bill goes back up and nothing turns red — which is why they need a
check rather than a comment.
.github/workflows/README.mdupdated: the push-tier paragraph, thediagram, the "What runs when" row, and a note in "Changing what runs when"
that an output need not map one-to-one onto a job.
Net effect: the push tier goes from 587 runner-minutes to about 73. Nothing
changes for pull requests, the merge queue, or
workflow_dispatch, all of whichset
build_linux_full=trueand so getcache-refresh-only: false.Why an input rather than a separate cache-warming workflow: the cache keys are
the entire point of the push tier, and a second workflow would have to repeat
every one of them. Keeping both modes in one file means they cannot drift.
Two deliberate consequences, both stated in the issue:
-lint,-spark-4.1-build,-celeborn-*) stopbeing refreshed on main and fall back to the shared
<os>-java-maven-restore-keys prefix, which the TPC jobs still write. Cost is a small delta
download per lint job on a pull request.
<os>-cargo-registry-*stops being written on main, becauselinux-testownsit. Those jobs pass
skip-native-build: trueand run no cargo command, so theentry is close to unused.
How are these changes tested?
dev/ci/check-ci-config.pyis the test, and it runs inpreflighton every PR,merge group and push. The new check was mutation-tested locally — each of these
fails it, and the unmodified tree passes:
linux-testcheck_cache_refresh_scope(job unguarded)with:block fromci.ymlcheck_cache_refresh_scope(input never passed)build-native, a cache writercheck_cache_refresh_scope(cache goes stale)build_linux_fullback on thepushtiercheck_event_policy(POLICY_CASES)Routing was verified by running
compute-changes.pydirectly for each eventagainst a
spark/src/main/**change:build_linuxbuild_linux_fullcache-refresh-onlypushmerge_grouppull_requestworkflow_dispatchA docs-only push still routes to neither, so the workflow does not run at all.
actionlintandprettier --checkpass. The behaviour onpushitself canonly be confirmed after merge, on the first push to main — the check to make is
that
PR Build (Linux)reports five jobs rather than 32.