Skip to content

fix(component-test): let deploy test jobs reuse the Gradle build cache [ENERGY-2742] - #340

Draft
ckattmann wants to merge 1 commit into
mainfrom
fix/ENERGY-deploy-gradle-cache
Draft

fix(component-test): let deploy test jobs reuse the Gradle build cache [ENERGY-2742]#340
ckattmann wants to merge 1 commit into
mainfrom
fix/ENERGY-deploy-gradle-cache

Conversation

@ckattmann

Copy link
Copy Markdown
Contributor

Problem

The test job in component-test-kotlin.yml (used by deploy-kotlin.yml and deploy-kotlin-v2.yml) caches Gradle only via actions/setup-java's cache: 'gradle'. That key is a hash of the dependency files, and actions/cache skips saving when the primary key hits — so the ~/.gradle/caches/build-cache-1 inside the restored archive is a frozen snapshot that never accumulates task outputs. Every deploy therefore re-runs KSP and both Kotlin compiles from scratch.

Evidence from service-energy's production deploy on 2026-09-06 (run 34027976026, main @ bfb13787):

step Test project 6m33s
Gradle summary BUILD SUCCESSFUL in 6m 31s, 11 actionable tasks: 11 executed — nothing FROM-CACHE
:kspKotlin ~63 s
:compileKotlin ~45 s
:kspTestKotlin ~30 s
:compileTestKotlin ~30 s
:test (first event 10:43:14 → last 10:45:29) ~136 s
cache log Cache hit for: setup-java-Linux-arm64-gradle-43568f91… (598 MB) → Cache hit occurred on the primary key …, not saving cache

So ~4 of the 6.5 minutes is compilation that the pull-request workflow gets from cache: pull-request-kotlin.yml restores/saves ~/.gradle with actions/cache keyed on ${{ runner.os }}-gradle-${{ github.head_ref }}-${{ github.sha }} and saves after a non-exact restore, which is why its runs report :compileKotlin FROM-CACHE.

For scale, service-energy's Test project step across recent deploys: 9m18s (09-02), 5m10s / 4m59s (09-03), 4m50s (09-05), 6m33s (09-06). The compile cost is systematic, not a one-off.

Change

One file, component-test-kotlin.yml, mirroring what pull-request-kotlin.yml already does:

  • drop cache: 'gradle' from actions/setup-java
  • add Restore Gradle cache (id: gradle-cache, actions/cache/restore@v5) over ~/.gradle/caches + ~/.gradle/wrapper, key ${{ runner.os }}-gradle-deploy-${{ github.sha }}, restore-keys ${{ runner.os }}-gradle-deploy- then ${{ runner.os }}-gradle-
  • add Save Gradle cache (actions/cache/save@v5) guarded by always() && steps.gradle-cache.outputs.cache-hit != 'true'

The deploy- key segment keeps these archives distinct from the PR workflow's and from sonar-cloud.yml's -gradle-main- keys; the two fallbacks mean a deploy warms up from the previous deploy, and can also pick up any main-scoped Gradle cache when there isn't one.

Expected effect

~3–4 minutes off the deploy test job for every Kotlin service, growing with how little changed between deploys. The first deploy after this merges is still a miss (nothing under -gradle-deploy- yet); from the second one onwards the previous archive is restored.

Considerations for reviewers

  • Cache budget: each deploy now saves a ~600 MB–1 GB archive under a per-commit key, same as the PR workflow already does. GitHub evicts LRU per repo at 10 GB, so repos that deploy very frequently will churn caches a bit more. If SRE prefers, the key could be ${{ runner.os }}-gradle-deploy-${{ github.ref_name }} (one archive per tag/branch, overwritten) at the cost of losing the exact-hit guard's meaning.
  • Scope: I deliberately did not touch code-coverage-kotlin.yml or component-service-profile-kotlin.yml, which use the same cache: 'gradle' pattern and presumably have the same symptom. Happy to follow up once this shape is agreed.
  • Not changed: runner selection, Gradle args, timeout-minutes, artifact upload, and the test task itself.

Validation

  • YAML parsed and step graph checked (jobs.test.steps = Checkout → Set up JDK → Restore Gradle cache (id: gradle-cache) → Test project → Upload test results (if: always()) → Save Gradle cache (if: always() && steps.gradle-cache.outputs.cache-hit != 'true')).
  • actionlint is not installed on my machine, so it has not been run locally; relying on repo CI for that.
  • No service-side change is needed: service-energy already passes --parallel --build-cache, which is what makes the restored build-cache-1 useful.

Ticket: https://montaapp.atlassian.net/browse/ENERGY-2742 (draft — CODEOWNERS is @monta-app/sre, so this needs your review before it affects every Kotlin deploy)

…e [ENERGY-2742]

The test job cached Gradle only through actions/setup-java's cache: 'gradle',
which keys on a hash of the dependency files and, on an exact primary-key hit,
does not save. The build-cache-1 inside that archive is therefore a frozen
snapshot that never accumulates task outputs, so every deploy re-runs KSP and
both Kotlin compiles from scratch.

service-energy production deploy 2026-09-06 (run 34027976026): 'Test project'
6m33s, 'BUILD SUCCESSFUL in 6m 31s', '11 actionable tasks: 11 executed',
nothing FROM-CACHE; :kspKotlin ~63s, :compileKotlin ~45s, :kspTestKotlin ~30s,
:compileTestKotlin ~30s. The pull-request workflow restores the same tasks
FROM-CACHE because it keys actions/cache on the commit and saves after a
non-exact restore.

Mirror that here: drop setup-java's cache input, restore with a per-commit key
plus '-gradle-deploy-' and '-gradle-' fallbacks, and save only when the restore
was not an exact hit.
@ckattmann

Copy link
Copy Markdown
Contributor Author

Update on the validation note: the repo's Lint GitHub Workflows check (actionlint) passed on this branch — https://github.com/monta-app/github-workflows/actions/runs/34052330363 — so the expression syntax and step references are lint-clean, covering the gap from not having actionlint installed locally. Still draft pending @monta-app/sre review.

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.

1 participant