Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions .github/actions/java-test/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@ runs:
shell: bash
run: echo "hash=${{ hashFiles('**/pom.xml') }}" >> "$GITHUB_OUTPUT"

- name: Cache Maven dependencies
- name: Restore Maven dependencies
# TODO: remove next line after working again
# temporarily work around https://github.com/actions/runner-images/issues/13341
# by disabling caching for macOS
if: ${{ runner.os != 'macOS' }}
uses: actions/cache@v5
id: maven-cache
uses: actions/cache/restore@v5
with:
path: |
~/.m2/repository
Expand Down Expand Up @@ -176,3 +177,16 @@ runs:
if-no-files-found: ignore
retention-days: 7 # 1 week for test reports
overwrite: true

# Saved only on main, and only on a green run: an entry written from a pull request or a
# `gh-readonly-queue/*` branch cannot be restored by any later run, and it
# evicts main's from the shared budget. See "Large caches are written on
# main only" in ../../workflows/README.md.
- name: Save Maven dependencies
if: ${{ runner.os != 'macOS' && github.ref == 'refs/heads/main' && steps.maven-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v5
with:
path: |
~/.m2/repository
/root/.m2/repository
key: ${{ runner.os }}-java-maven-${{ steps.maven-cache-key.outputs.hash }}
18 changes: 16 additions & 2 deletions .github/actions/rust-test/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ runs:
cd native
cargo install cargo-machete --version 0.7.0 && cargo machete

- name: Cache Maven dependencies
uses: actions/cache@v4
- name: Restore Maven dependencies
id: maven-cache
uses: actions/cache/restore@v4
with:
path: |
~/.m2/repository
Expand All @@ -57,6 +58,19 @@ runs:
cd common
../mvnw -B clean compile -DskipTests

# Saved only on main: an entry written from a pull request or a
# `gh-readonly-queue/*` branch cannot be restored by any later run, and it
# evicts main's from the shared budget. See "Large caches are written on
# main only" in ../../workflows/README.md.
- name: Save Maven dependencies
if: ${{ github.ref == 'refs/heads/main' && steps.maven-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
with:
path: |
~/.m2/repository
/root/.m2/repository
key: ${{ runner.os }}-rust-maven-${{ hashFiles('**/pom.xml') }}

- name: Install nextest
shell: bash
run: |
Expand Down
25 changes: 23 additions & 2 deletions .github/actions/setup-spark-builder/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,21 @@ runs:
cd apache-spark
git apply ../dev/diffs/${{inputs.spark-version}}.diff

- name: Cache Maven dependencies
uses: actions/cache@v4
- name: Restore Maven dependencies
id: maven-cache
uses: actions/cache/restore@v4
with:
path: |
~/.m2/repository
/root/.m2/repository
key: ${{ runner.os }}-spark-sql-${{ hashFiles('common/**/pom.xml', 'spark/**/pom.xml') }}
# No Spark SQL workflow has a push-to-main tier, so nothing writes
# `spark-sql-` any more (see the save step below). `java-maven-` is the
# entry the TPC-H/TPC-DS jobs write on push to main; a Maven repository
# is always safe to start from a superset or a subset of itself.
restore-keys: |
${{ runner.os }}-spark-sql-
${{ runner.os }}-java-maven-

- name: Build Comet (with native)
if: ${{ inputs.skip-native-build != 'true' }}
Expand Down Expand Up @@ -93,3 +99,18 @@ runs:
"$(dirname "$pom")/_remote.repositories"
done
done

# No Spark SQL workflow has a push-to-main tier today, so in practice this
# never fires. The guard is here so the step starts writing if one is ever
# added, and never writes from a pull request or a `gh-readonly-queue/*`
# branch, where the entry cannot be restored by any later run and only
# evicts main's from the shared budget. See "Large caches are written on
# main only" in ../../workflows/README.md.
- name: Save Maven dependencies
if: ${{ github.ref == 'refs/heads/main' && steps.maven-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
with:
path: |
~/.m2/repository
/root/.m2/repository
key: ${{ runner.os }}-spark-sql-${{ hashFiles('common/**/pom.xml', 'spark/**/pom.xml') }}
67 changes: 67 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,73 @@ than once carries its version inputs, e.g.
and also that every `download-artifact` name is produced by an upload in the
same workflow.

## Large caches are written on main only

An `actions/cache` entry is scoped to the ref that wrote it. A run can restore
entries from its own ref and from the default branch, and nothing else. So a
cache written from `refs/pull/*/merge` is visible only to another run of that
same pull request, and one written from the merge queue's
`gh-readonly-queue/*` branch is visible to nobody at all, because the queue
deletes that branch when it is done with it.

Both still count against the repository's shared cache budget, which is
evicted least-recently-used. Writing them therefore has no upside and one
large downside: it pushes main's entries out, and main's entries are the only
ones a future pull request can use.

That is what happened. On 2026-09-15 the repository held 12.27 GiB across 14
entries: 9.22 GiB on a single `gh-readonly-queue/*` branch, 3.01 GiB on
`refs/pull/*/merge` refs, and nothing whatsoever on main. Every one of the
seven near-identical `Linux-java-maven-*` Maven repositories was a
write-only copy. With main's `cargo-ci` entry evicted, all eight native
builds in a merge-queue run missed their cache and paid a cold ~26 minute
compile; the same build with a hit takes 2m21s.

The rule, then: any cache holding a Maven repository (`~/.m2/repository`) or a
cargo tree (`~/.cargo/registry`, `~/.cargo/git`, `native/target`) is
**restored everywhere and saved only on push to main**:

```yaml
- name: Restore Maven dependencies
id: maven-cache
uses: actions/cache/restore@v6
with:
path: |
~/.m2/repository
/root/.m2/repository
key: ${{ runner.os }}-java-maven-${{ hashFiles('**/pom.xml') }}-lint
restore-keys: |
${{ runner.os }}-java-maven-

# ... the steps that populate it ...

- name: Save Maven dependencies
if: ${{ github.ref == 'refs/heads/main' && steps.maven-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v6
with:
path: |
~/.m2/repository
/root/.m2/repository
key: ${{ runner.os }}-java-maven-${{ hashFiles('**/pom.xml') }}-lint
```

The bare `actions/cache@vN` form cannot express this: it saves in an implicit
post step that no `if:` can reach. `dev/ci/check-ci-config.py` rejects it for
any of the paths above, and rejects a `save` that is missing the `github.ref`
guard. `publish_snapshot.yml` is exempt in `CACHE_SAVE_SCOPE_EXEMPT`, because
it runs from main on a schedule already.

The TPC-H and TPC-DS dataset caches keep the read-write form and are out of
scope entirely: `./tpch` and `./tpcds-sf-1` are a few hundred MB, they are not
dependency trees, and they are keyed on this workflow file, so a pull request
that edits it would regenerate the data on every run rather than once.

A job that only ever runs on a pull request or in the queue keeps the guard
anyway, and so never writes. That is deliberate — it restores from main's
entry through `restore-keys` and downloads whatever else it needs, which is
what a cold pull request already did. See the push-tier discussion above for
which jobs do run on main and therefore do write.

## Retrying flaky network operations

**Maven.** `.mvn/maven.config` tunes the Maven Resolver HTTP transport: six
Expand Down
23 changes: 21 additions & 2 deletions .github/workflows/pr_benchmark_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,21 @@ jobs:
cd native
cargo check --benches

- name: Cache Maven dependencies
uses: actions/cache@v6
- name: Restore Maven dependencies
id: maven-cache
uses: actions/cache/restore@v6
with:
path: |
~/.m2/repository
/root/.m2/repository
key: ${{ runner.os }}-benchmark-maven-${{ hashFiles('**/pom.xml') }}
# This job is queue-only, so nothing writes `benchmark-maven-` any
# more (see the save step below). `java-maven-` is the entry the
# TPC-H/TPC-DS jobs write on push to main; a Maven repository is
# always safe to start from a superset or a subset of itself.
restore-keys: |
${{ runner.os }}-benchmark-maven-
${{ runner.os }}-java-maven-

- name: Check Scala compilation and linting
# Pin to spark-4.0 (Scala 2.13.16) because the default profile is now
Expand All @@ -76,3 +82,16 @@ jobs:
# the same exclusion in the main lint matrix.
run: |
./mvnw -B compile test-compile scalafix:scalafix -Dscalafix.mode=CHECK -Psemanticdb -Pspark-4.0 -DskipTests

# Saved only on main: an entry written from a pull request or a
# `gh-readonly-queue/*` branch cannot be restored by any later run, and
# it evicts main's from the shared budget. See "Large caches are written
# on main only" in README.md.
- name: Save Maven dependencies
if: ${{ github.ref == 'refs/heads/main' && steps.maven-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v6
with:
path: |
~/.m2/repository
/root/.m2/repository
key: ${{ runner.os }}-benchmark-maven-${{ hashFiles('**/pom.xml') }}
Loading
Loading