Memo in GitHub Actions Skipped a Rust Release Build and Caught Runners With Different Compilers
Most CI jobs build the same thing again and again. A docs fix or a typo in a README triggers the whole pipeline, and the release build runs as if the code had changed. This case study puts BXM Memo in front of one such step, a release build of the bxm-canon binary, and keeps Memo’s cache between jobs with actions/cache.
The workflow is in the repository as .github/workflows/case-study-memo.yml. Five jobs run one after another, each on a fresh GitHub-hosted runner. plain builds without Memo for the baseline. cold builds through Memo with an empty cache, warm changes nothing, docs edits the README, and source edits a Rust source file. The inputs are canon/src/**, Cargo.toml and Cargo.lock, and the salt is rustc --version, so a different compiler means a different result. We ran the whole workflow twice.

| Job | Run 1 | Run 2 |
|---|---|---|
plain: cargo build --release, no Memo |
16.9 s | 23.0 s |
| cold: through Memo, empty cache | miss, 13.5 s | miss, 7.9 s |
| warm: nothing changed | hit, 0.17 s | miss: the salt changed, 10.9 s |
| docs: README edited | hit, 0.16 s | hit, 0.17 s |
source: main.rs edited |
miss: the salt changed, input canon/src/main.rs changed, 8.6 s | miss: the salt changed, input canon/src/main.rs changed, 18.6 s |
A hit took under two tenths of a second for the whole step. Memo put the binary back in about a millisecond (670 µs, 798 µs and 1.0 ms in the three hits), and actions/cache took 0.32 to 0.35 s to restore the cache folder before that. The folder held 448 KB. The hit jobs finished in 5 and 6 seconds from start to end, against 22 and 26 for the plain build. The README edit was still a hit, since the README isn’t an input. The source edit missed and said which file.
Builds on these runners vary a lot. The cold build through Memo ran faster than the plain one in both runs, which is down to the runners, not to Memo. The fair comparison is a hit against any of the build times.
The surprise: two Rust versions
Run 2’s warm job should have been a hit, and Memo said “the salt changed”. The workflow prints the toolchain, so we could check. The cold job’s runner had rustc 1.99.1 on image 20261018. The warm job’s runner had rustc 1.99.0 on image 20261012. GitHub rolls out new runner images gradually, so for a while jobs in one workflow can land on different images. Run 1’s source job missed on the salt too. That run didn’t print the toolchain yet, but the cause is almost certainly the same.
With the compiler in the salt, Memo rebuilt rather than hand back a binary from a different compiler. Leave the salt out and those jobs would have been hits with the other compiler’s output. Whether that matters depends on the step, but for a release binary we’d rather rebuild. It cost one build each time.
How to set it up
Download bxm-memo from the latest release, point BXM_MEMO_CACHE at a folder in the workspace, and wrap the step:
bxm-memo run -n canon-release \
-i 'canon/src/**' -i canon/Cargo.toml -i canon/Cargo.lock \
-o canon/target/release/bxm-canon --salt "$(rustc --version)" \
-- cargo build --release --manifest-path canon/Cargo.toml --bin bxm-canon
Save the folder with actions/cache after the step and restore it before. Memo’s cache is small, because it holds the step’s outputs and not the whole target folder.
The recorded results of both runs are in test/results/case-studies/04-github-actions.txt, with links to the runs. More examples are on the use cases page.