Fix redundant Go caching - #23281
Conversation
|
✅ No conflicts with other open PRs targeting |
There was a problem hiding this comment.
Pull request overview
Risk Rating: MEDIUM — This PR changes CI cache keying and write/restore policy across multiple workflows and the shared setup-go composite action; mistakes here can silently degrade CI performance or break caching behavior on critical branches.
This PR refactors Go-related caching in GitHub Actions to reduce redundant cache writes and contention by scoping cache keys more precisely (OS/arch/version) and enforcing “single writer” policies for build caches, while gating module-cache warming on actual go.mod/go.sum changes.
Changes:
- Introduces explicit build-cache write policies (
always,default-branch-only,never) and per-job build cache versioning via./.github/actions/setup-go. - Gates the
go-mod-cacheworkflow on detected Go module changes for PRs, while still running on non-PR events. - Updates multiple workflow cache keys to include
${{ runner.os }}and${{ runner.arch }}(and bumpsgotestsuminstall version in the mod-cache workflow).
Scrupulous human review recommended (high-impact areas):
.github/actions/setup-go/action.ymlbuild-cache key generation + conditional restore/save logic (default branch behavior and restore-keys handling).- Workflow callers that set
build-cache-write/build-cache-version(ensuring no unintended concurrent writers share a key). go-mod-cache.ymlPR gating behavior (ensuring “skipped” behavior is acceptable for required checks and operational expectations).
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| .github/workflows/integration-tests.yml | Removes redundant env var related to mod-cache versioning. |
| .github/workflows/go-mod-cache.yml | Adds paths-based filtering to avoid running mod-cache updates on PRs without Go module changes; updates gotestsum install version. |
| .github/workflows/delete-caches.yml | Updates workflow header comments to clarify GitHub vs S3 cache lifecycle. |
| .github/workflows/cre-system-tests.yaml | Scopes gotestsum cache key by OS/arch to avoid cross-runner collisions. |
| .github/workflows/cre-regression-system-tests.yaml | Scopes gotestsum cache key by OS/arch to avoid cross-runner collisions. |
| .github/workflows/ccip-system-tests.yaml | Scopes gotestsum cache key by OS/arch to avoid cross-runner collisions. |
| .github/workflows/codeql.yml | Switches to new build-cache write policy inputs for Go setup. |
| .github/workflows/ci-deployments.yml | Consolidates deployment build cache version and enforces single-writer behavior (shard 1 only, default-branch-only). |
| .github/workflows/ci-core.yml | Adds build-cache versioning/policies for lint, core tests, scripts tests, and misc jobs; adjusts WASM cache restore/save behavior. |
| .github/actions/setup-solana/action.yml | Updates solana CLI cache key to include arch and fixes hashFiles path formatting. |
| .github/actions/setup-go/action.yml | Adds build-cache-write policy, adds arch to cache keys, and reworks build-cache restore/save conditions. |
| .github/actions/golangci-lint/action.yml | Plumbs build-cache versioning/policies through to setup-go for lint matrix jobs. |
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
.github/actions/setup-go/action.yml:203
- The build-cache-keys step exits early on the default branch before emitting
secondary-key/develop-key. Those outputs are then interpolated intorestore-keysfor both restore and save steps, which can produce empty restore-key lines and potentially restore an unrelated cache (or error) on default-branch runs.
if [ "$CURRENT_BRANCH" = "$DEFAULT_BRANCH" ]; then
exit 0
fi
SECONDARY_KEY="${KEY_PREFIX}-${CURRENT_BRANCH}-"
DEVELOP_KEY="${RUNNER_OS}-${RUNNER_ARCH}-gobuild-${CACHE_VERSION}-${DEFAULT_BRANCH}-"
echo "secondary-key=${SECONDARY_KEY}" >> "${GITHUB_OUTPUT}"
echo "develop-key=${DEVELOP_KEY}" >> "${GITHUB_OUTPUT}"
.github/workflows/sonar.yml:28
- On
workflow_runevents,actions/checkoutwill not automatically check out the completed CI Core run’s commit. Without explicitly settingreftogithub.event.workflow_run.head_sha, the Sonar scan can run against the default branch instead of the PR/push commit that produced the artifacts.
- name: Checkout the repo
uses: actions/checkout@v7
with:
persist-credentials: false
fetch-depth: 0 # fetches all history for all tags and branches to provide more metadata for sonar reports
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
.github/workflows/sonar.yml:30
actions/checkoutis always givenref: ${{ github.event.workflow_run.head_sha }}, which is empty onworkflow_dispatch. That can result in checking out the wrong revision (or failing), and makes manual dispatch runs unreliable.
- name: Checkout the repo
uses: actions/checkout@v7
with:
persist-credentials: false
fetch-depth: 0 # fetches all history for all tags and branches to provide more metadata for sonar reports
ref: ${{ github.event.workflow_run.head_sha }}
.github/workflows/sonar.yml:44
- These env vars read from
github.event.workflow_run.*, but the workflow also supportsworkflow_dispatch. On manual dispatch,PR_NUMBER/HEAD_BRANCH/BASE_BRANCHend up empty, and the script will always take the "no PR context" path and skip analysis.
env:
PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }}
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
BASE_BRANCH: ${{ github.event.workflow_run.pull_requests[0].base.ref || 'develop' }}
.github/workflows/sonar.yml:13
workflow_dispatchis enabled, but the workflow is primarily driven byworkflow_runartifacts and context. With the currentdownload-artifactstep guarded toworkflow_runand the hard dependency on reports, a manual dispatch run will typically no-op (skip analysis). Consider either removingworkflow_dispatchor adding inputs (e.g., run-id/head-sha) and using them to download artifacts + set PR/branch context.
This issue also appears in the following locations of the same file:
- line 24
- line 41
on:
workflow_run:
workflows: ["CI Core"]
types:
- completed
workflow_dispatch:
.github/workflows/codeql.yml:52
- This note says CodeQL only writes the build cache on push, but the workflow also runs on a schedule and the
default-branch-onlypolicy insetup-goenables cache writes on scheduled runs of the default branch as well.
# Note: CodeQL writes the build cache on push to default branch (develop).
# On release/* branches and PRs, build-cache-write resolves to never (restore-only).




Small Changes
Big Changes
sonarcheck into independent workflow run instead of bundled as part of ci-coreGo Caching
Go caching had a lot of redundant writes and poorly performing reads.
Before Flow
flowchart LR subgraph W["One PR commit — concurrent workflows"] LINT["golangci<br/>N jobs, one per module<br/>arm64 / S3"] MISC["misc<br/>x64 / GitHub"] SCR["core-scripts-tests<br/>x64 / GitHub"] UT["go_core_tests"] IT["go_core_tests_integration"] FZ["go_core_fuzz"] RC["go_core_race_tests"] DEP["deployment-tests<br/>shards 1..7"] GMC["go-mod-cache<br/>4 jobs, every PR"] end K1{{"Linux-gobuild-1-BRANCH-SHA<br/>N+2 concurrent writers"}} KW{{"Linux-wasmcache-HASH<br/>4 concurrent writers"}} K2(["Linux-gobuild-go_core_tests-*"]) K3(["Linux-gobuild-go_core_tests_integration-*"]) KD(["Linux-gobuild-go_deployment_tests-1..7<br/>7 near-identical caches"]) KM(["Linux-gomod-1-HASH"]) LINT -->|write| K1 MISC -->|write| K1 SCR -->|write| K1 UT -->|write| K2 IT -->|write| K3 FZ -->|read| K2 RC -->|read| K2 UT -->|write| KW IT -->|write| KW FZ -->|write| KW RC -->|write| KW DEP -->|write x7| KD GMC -->|write| KMPer-commit
GOCACHEuploads ≈ 17New Flow
flowchart LR subgraph PR["One PR commit"] LINT2["golangci × N"] MISC2["misc"] SCR2["core-scripts-tests"] UT2["go_core_tests"] IT2["go_core_tests_integration"] FZ2["go_core_fuzz"] RC2["go_core_race_tests"] DEP2["deployment shards 1..7"] GMC2["go-mod-cache<br/>gated on go.mod / go.sum diff"] end subgraph DEV["push to develop — checkpoint writers"] DLINT["golangci × changed modules"] DMISC["misc"] DSCR["core-scripts-tests"] DDEP["deployment shard 1 only"] DGMC["go-mod-cache"] end KL(["Linux-X64|ARM64-gobuild-lint-MODULE-*"]) KM2(["Linux-ARCH-gobuild-misc-*"]) KS2(["Linux-ARCH-gobuild-go_core_scripts_tests-*"]) KU(["Linux-ARCH-gobuild-go_core_tests-*"]) KI(["Linux-ARCH-gobuild-go_core_tests_integration-*"]) KD2(["Linux-ARCH-gobuild-go_deployment_tests-*<br/>one key, 7 readers"]) KW2(["Linux-ARCH-wasmcache-HASH<br/>1 writer"]) KMOD(["Linux-ARCH-gomod-1-HASH"]) LINT2 -.->|read| KL MISC2 -.->|read| KM2 SCR2 -.->|read| KS2 UT2 -->|write| KU IT2 -->|write| KI FZ2 -.->|read| KU RC2 -.->|read| KU UT2 -->|write| KW2 IT2 -.->|read| KW2 FZ2 -.->|read| KW2 RC2 -.->|read| KW2 DEP2 -.->|read| KD2 GMC2 -->|write on go.sum change| KMOD DLINT --> KL DMISC --> KM2 DSCR --> KS2 DDEP --> KD2 DGMC --> KMODEvery key has exactly one writer. Per-commit
GOCACHEuploads on a PR = 2.