Skip to content

Add source-keyed target cache support#349

Open
garysassano wants to merge 1 commit into
Swatinem:masterfrom
garysassano:target-key-source-cache
Open

Add source-keyed target cache support#349
garysassano wants to merge 1 commit into
Swatinem:masterfrom
garysassano:target-key-source-cache

Conversation

@garysassano

@garysassano garysassano commented Jun 13, 2026

Copy link
Copy Markdown

Closes #348

Summary

This adds an opt-in target-key input for source-keyed workspace target caching.

When target-key is set together with cache-targets: true and cache-workspace-crates: true, rust-cache now caches workspace target directories separately from Cargo home/dependency state:

  • Cargo home keeps the existing dependency-oriented key strategy.
  • Target directories get a separate key that includes the user-provided source key.
  • Target cache restore happens after the Cargo cache restore.
  • Cargo and target caches are saved independently based on their own exact-hit state.

Existing behavior is unchanged unless target-key is provided.

Why

cache-workspace-crates: true is useful for workspaces that intentionally cache local crate artifacts. However, the current target cache key intentionally focuses on Rust/Cargo environment, manifests, lockfiles, and config rather than workspace source contents.

That is the right default for dependency-oriented caching, but it creates a corner case for users who opt into workspace crate artifact caching:

  1. An exact cache hit can restore stale workspace target artifacts.
  2. Cargo rebuilds those stale workspace crates correctly.
  3. The post step sees the cache as up-to-date because the restored key was exact.
  4. The rebuilt target state is not saved under a new key.
  5. The next run restores the same stale target state again.

This PR preserves the current default while providing an escape hatch for the users who deliberately want workspace target artifacts to be keyed by source state.

API

- uses: Swatinem/rust-cache@v2
  with:
    workspaces: ./app -> ../../target-for-one-binary
    cache-targets: true
    cache-all-crates: true
    cache-workspace-crates: true
    shared-key: one-binary-target
    target-key: ${{ steps.source-key.outputs.hash }}

The action does not try to compute a source key itself. The user can choose the fingerprint that matches their repository, for example:

hash="$({ git rev-parse HEAD:app; git ls-files -s app; } | sha256sum | cut -d ' ' -f1)"
echo "hash=$hash" >> "$GITHUB_OUTPUT"

If target-key is set without cache-targets: true or cache-workspace-crates: true, the action warns and falls back to existing behavior.

Implementation Notes

  • CacheConfig now records independent cargo and target cache state when the target cache mode is enabled.
  • Restore flow restores Cargo home first, then target directories.
  • Save flow cleans and saves the cargo and target groups independently.
  • cache-hit is true only when all active cache groups are exact hits.
  • The existing single-cache flow remains the default when target-key is empty.
  • The target key layout keeps the dependency/environment portion separate from the source portion and provides restore prefixes for both same-dependency and broader target reuse.

Example generated keys:

Cargo key:
v0-rust-target-key-smoke-v1-Linux-x64-<env-hash>-<lock-hash>

Target key:
v0-rust-target-key-smoke-v1-Linux-x64-<env-hash>-target-<lock-hash>-<source-key>

Target restore keys:
v0-rust-target-key-smoke-v1-Linux-x64-<env-hash>-target-<lock-hash>-
v0-rust-target-key-smoke-v1-Linux-x64-<env-hash>-target-

Verification

Local checks:

npm ci
npm run prepare

I also tested this against a private production-sized Rust workspace with 30 Cargo packages and 11 independent binary artifact builds. The relevant results were:

  • First build-only run seeded the new target caches successfully.
  • Second build-only run restored both the cargo cache and target cache with full match: true in every binary job.
  • Cargo skipped compilation in every build; the repeated run was dominated by runner/tool setup, cache extraction, and artifact upload.

Representative log lines from the second run:

Restored from cache key "v0-rust-binary-target-v1-Linux-x64-<env-hash>-<lock-hash>" full match: true.
Restored from target cache key "v0-rust-binary-target-v1-Linux-x64-<env-hash>-target-<lock-hash>-<source-key>" full match: true.

Comparison from that private workflow:

Scenario Cache behavior Binary matrix wall time Binary Cargo build phase Cargo behavior
First run with this PR Seeds new target caches 3m02s Expected full build Build work is expected because the source-keyed target caches do not exist yet.
Repeated run with upstream rust-cache@v2 and no target-key Exact cache hit for the existing combined cache 1m14s 18s-42s Some workspace crates still rebuild despite exact cache hits.
Repeated run with this PR and target-key Exact cargo cache hit and exact target cache hit 37s 0.3s No workspace compilation on repeated runs.
Per-binary timings

GitHub reports step durations rounded to whole seconds. A 0s Cargo build phase means the measured step was below one second.

Binary First run with this PR (Total) First run with this PR (Cargo) Upstream repeated run (Total) Upstream repeated run (Cargo) This PR repeated run (Total) This PR repeated run (Cargo)
function_a 2m47s 2m09s 1m07s 34s 35s 0s
function_b 2m33s 1m55s 1m11s 38s 36s 0s
function_c 3m02s 1m56s 1m11s 39s 36s 1s
function_d 2m56s 2m17s 1m14s 42s 37s 0s
function_e 2m37s 2m01s 1m11s 42s 35s 0s
function_f 2m27s 1m50s 51s 18s 35s 0s
function_g 2m35s 1m58s 1m10s 38s 35s 1s
function_h 2m32s 1m55s 59s 28s 36s 1s
function_i 2m33s 1m55s 1m09s 38s 35s 1s
function_j 2m38s 2m01s 1m13s 42s 36s 1s
function_k 2m33s 1m55s 59s 22s 34s 1s

Representative upstream baseline log shape:

Restored from cache key "v0-rust-<job-key>-Linux-x64-<env-hash>-<lock-hash>" full match: true.
Updating crates.io index
Compiling <workspace-crate> ...
Finished `release` profile [optimized] target(s) in 18s-42s

With this PR and a source-keyed target cache, the repeated run restored exact target-cache hits and reduced the Cargo build phases to roughly 0.3s, with no Compiling lines.

Compatibility

  • No behavior change unless target-key is set.
  • Existing cache keys and cleanup behavior remain the default path.
  • This does increase cache count/size for users who opt in, because target
    directories are cached separately and source changes create new target keys.
    That tradeoff is intentional for workloads that need true repeated-run no-op
    behavior for workspace artifacts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: allow source-keyed target cache when caching workspace crates

1 participant