Skip to content

Commit 3dd9756

Browse files
authored
fix(ci): improve caching (#11128)
2 parents 8e9ff3b + 9fe499b commit 3dd9756

4 files changed

Lines changed: 78 additions & 6 deletions

File tree

.github/workflows/README.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,40 @@ _The diagram above illustrates the parallel execution patterns in our CI/CD syst
142142

143143
**Note**: Self-hosted Runners are just used to keep the logs running in the GitHub Actions UI for over 6 hours, the Integration Tests are not run in the Self-hosted Runner itself, but in the deployed VMs in GCP through GitHub Actions.
144144

145-
### 5. Queue Management
145+
### 5. Rust build caching
146+
147+
Rust jobs cache `~/.cargo` and dependency artifacts in `target/` through
148+
[`actions-rust-lang/setup-rust-toolchain`](https://github.com/actions-rust-lang/setup-rust-toolchain),
149+
which wraps [`Swatinem/rust-cache`](https://github.com/Swatinem/rust-cache). Caching is on by
150+
default, so a job opts _out_ with `cache: false` rather than opting in.
151+
152+
GitHub gives each repository 10 GB of cache storage by default; administrators can configure a
153+
higher paid limit. Least-recently-used entries are evicted when the configured limit is exceeded.
154+
Caches are also branch-scoped: a branch can read its own caches and the default branch's,
155+
caches written by PRs can never be read by anyone else, but they still evict main's caches, which
156+
are the only ones every PR does restore from.
157+
158+
Three rules keep the quota usable:
159+
160+
1. **Only main writes.** The main building jobs set
161+
`cache-save-if: ${{ github.ref == 'refs/heads/main' }}`. PRs restore from main and write nothing. (There are some minor exceptions to this rule.)
162+
2. **Wide matrices share one key.** The `test-crates.yml` matrices use
163+
`cache-shared-key` and seed the shared cache from a single build (`zebrad`
164+
which has the widest dependency closure; `zebra-rpc` for the MSRV build since
165+
it does not build `zebrad` and `zebra-rpc` is second widest option).
166+
3. **Jobs that don't build don't cache.** `fmt`, `no-test-deps`, `deny` (12 jobs wide), and the
167+
crate-matrix generator in `test-crates.yml` set `cache: false`.
168+
169+
To inspect the current state:
170+
171+
```bash
172+
gh api repos/ZcashFoundation/zebra/actions/cache/usage
173+
gh api 'repos/ZcashFoundation/zebra/actions/caches?per_page=100' \
174+
--jq '[.actions_caches[] | {ref, mb: (.size_in_bytes/1048576|round)}]
175+
| group_by(.ref)[] | "\(.[0].ref) n=\(length) \(map(.mb)|add)MB"'
176+
```
177+
178+
### 6. Queue Management
146179

147180
[Mergify](https://mergify.com)
148181

.github/workflows/lint.yml

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,16 @@ jobs:
7070
with:
7171
components: clippy
7272
toolchain: 1.97.0
73+
# Both matrix legs build the same workspace, so they share one cache.
74+
cache-shared-key: clippy
7375
cache-on-failure: true
76+
# Restore on every ref, but only write from main. Actions caches are
77+
# branch-scoped, so a cache written by a PR can never be read by any
78+
# other branch, yet it still consumes the repo-wide cache allowance
79+
# and can evict main's caches - the only ones PRs can restore from.
80+
# The `tests` leg seeds it, because its feature set is a superset.
81+
# See "Rust build caching" in .github/workflows/README.md.
82+
cache-save-if: ${{ github.ref == 'refs/heads/main' && matrix.type == 'tests' }}
7483
- uses: ./.github/actions/setup-zebra-build
7584
- name: Run clippy
7685
run: cargo clippy ${{ matrix.args }} --features "${{ matrix.features }}"
@@ -90,6 +99,8 @@ jobs:
9099
- uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 #v1.16.1
91100
with:
92101
cache-on-failure: true
102+
# Only main writes caches, see the clippy job above.
103+
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
93104
- uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 #v2.79.3
94105
with:
95106
tool: cargo-hack
@@ -118,6 +129,8 @@ jobs:
118129
with:
119130
toolchain: 1.91.0 # MSRV
120131
cache-on-failure: true
132+
# Only main writes caches, see the clippy job above.
133+
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
121134
- uses: ./.github/actions/setup-zebra-build
122135
- run: cargo build --bin "${{ matrix.binary }}" --workspace
123136

@@ -138,6 +151,8 @@ jobs:
138151
with:
139152
toolchain: 1.91.0
140153
components: rustfmt
154+
# `cargo fmt` builds nothing, so a cache here only consumes quota.
155+
cache: false
141156
- name: Run fmt
142157
run: cargo fmt --all -- --check
143158

@@ -154,7 +169,8 @@ jobs:
154169
- uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 #v1.16.1
155170
with:
156171
toolchain: 1.97.0
157-
cache-on-failure: true
172+
# `cargo tree` builds nothing, so a cache here only consumes quota.
173+
cache: false
158174
- name: Ensure no arbitrary or proptest dependency on default build
159175
run: cargo tree --package zebrad -e=features,no-dev | grep -Eq "arbitrary|proptest" && exit 1 || exit 0
160176

@@ -175,6 +191,8 @@ jobs:
175191
with:
176192
toolchain: 1.97.0
177193
cache-on-failure: true
194+
# Only main writes caches, see the clippy job above.
195+
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
178196
- uses: ./.github/actions/setup-zebra-build
179197
- run: cargo check --locked --all-features --all-targets
180198

@@ -211,7 +229,9 @@ jobs:
211229
persist-credentials: false
212230
- uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 #v1.16.1
213231
with:
214-
cache-on-failure: true
232+
# cargo-deny reads the dependency graph without building it, and this
233+
# matrix is 12 jobs wide, so caching here is pure quota consumption.
234+
cache: false
215235
- name: Check ${{ matrix.checks }} with features ${{ matrix.features }}
216236
uses: EmbarkStudios/cargo-deny-action@6c8f9facfa5047ec02d8485b6bf52b587b7777d1 #v2.0.18
217237
with:

.github/workflows/test-crates.yml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ jobs:
6363
with:
6464
toolchain: 1.97.0
6565
components: clippy
66-
cache-on-failure: true
66+
# This job only runs `cargo tree` to build the matrix, so a cache here
67+
# only consumes quota.
68+
cache: false
6769
- uses: ./.github/actions/setup-zebra-build
6870

6971
# This step dynamically creates a JSON containing the values of each crate
@@ -117,8 +119,16 @@ jobs:
117119
with:
118120
toolchain: 1.97.0
119121
components: clippy
120-
cache-key: crate-build-${{ matrix.crate }}
122+
# One cache for the whole matrix instead of one per crate. These jobs
123+
# compile overlapping subsets of the same dependency graph, so 12
124+
# per-crate caches stored ~12 GB of near-duplicate artifacts and blew
125+
# through the repo's default 10 GB quota on their own.
126+
cache-shared-key: crate-build
121127
cache-on-failure: true
128+
# Only main writes the shared cache, and only from the crate with the
129+
# widest dependency closure, so the other legs restore a superset
130+
# rather than whichever leg happened to finish first.
131+
cache-save-if: ${{ github.ref == 'refs/heads/main' && matrix.crate == 'zebrad' }}
122132

123133
- uses: ./.github/actions/setup-zebra-build
124134

@@ -179,8 +189,13 @@ jobs:
179189
- uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 #v1.16.1
180190
with:
181191
toolchain: ${{ steps.msrv.outputs.version }}
182-
cache-key: crate-build-msrv-${{ matrix.crate }}
192+
# One cache for the whole matrix, see the `build` job above.
193+
cache-shared-key: crate-build-msrv
183194
cache-on-failure: true
195+
# `zebrad` can't seed this matrix: the build step below skips it, so
196+
# that leg compiles nothing. `zebra-rpc` has the widest closure of
197+
# the crates that do build here.
198+
cache-save-if: ${{ github.ref == 'refs/heads/main' && matrix.crate == 'zebra-rpc' }}
184199

185200
- uses: ./.github/actions/setup-zebra-build
186201

.github/workflows/tests-unit.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ jobs:
6969
toolchain: 1.97.0
7070
cache-key: unit-tests-1.97.0-default-release-binaries
7171
cache-on-failure: true
72+
# Restore on every ref, but only write from main. This is the longest
73+
# job in CI, so it is the one that suffers most when PR-scoped caches
74+
# evict main's copy. See "Rust build caching" in README.md.
75+
cache-save-if: ${{ github.ref == 'refs/heads/main' }}
7276
- uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 #v2.79.3
7377
with:
7478
tool: cargo-nextest

0 commit comments

Comments
 (0)