gradle / gradle/actions

setup-gradle: GUH cache key collision when used in a reusable workflow

Open
#316 23 comments 3 reactions 0 assignees View on GitHub
bug in:setup-gradle
Dominant language
TypeScript
Stars
340
Forks
117
Avg merge
1d 9h
Merged PRs (30d)
11

Description

Repro: https://github.com/cloudshiftinc/gradle-setup-action-cache-repro/actions/runs/10067337462

That repository consists of a small "Hello World" Gradle / Kotlin project, with two build files:
1) Build with Kotlin 1.8.22;
2) Build with Kotlin 1.9.25;

There's a GHA workflow that runs two steps, sequentially, using each of the above build files.
Each step checks out the code, calls `setup-gradle` (v4 beta 1), and executes the respective Gradle build file (build.gradle.kts or build-second.gradle.kts).

The second build "cleans up" artifacts it didn't use from the first build:

```
Build cache (/home/runner/.gradle/caches/build-cache-1) removing files not accessed on or after Tue Jul 23 22:45:31 UTC 2024.
Build cache (/home/runner/.gradle/caches/build-cache-1) cleanup deleted 0 files/directories.
Build cache (/home/runner/.gradle/caches/build-cache-1) cleaned up in 0.001 secs.
groovy-dsl (/home/runner/.gradle/caches/8.9/groovy-dsl) removing files not accessed on or after Tue Jul 23 22:45:31 UTC 2024.
groovy-dsl (/home/runner/.gradle/caches/8.9/groovy-dsl) cleanup deleted 2 files/directories.
groovy-dsl (/home/runner/.gradle/caches/8.9/groovy-dsl) cleaned up in 0.01 secs.
Artifact transforms cache (/home/runner/.gradle/caches/8.9/transforms) removing files not accessed on or after Tue Jul 23 22:45:31 UTC 2024.
Artifact transforms cache (/home/runner/.gradle/caches/8.9/transforms) cleanup deleted 0 files/directories.
Artifact transforms cache (/home/runner/.gradle/caches/8.9/transforms) cleaned up in 0.027 secs.
jars (/home/runner/.gradle/caches/jars-9) cleanup deleted 0 files/directories.
jars (/home/runner/.gradle/caches/jars-9) removing files not accessed on or after Tue Jul 23 22:45:31 UTC 2024.
jars (/home/runner/.gradle/caches/jars-9) cleanup deleted 1 files/directories.
jars (/home/runner/.gradle/caches/jars-9) cleaned up in 0.003 secs.
artifact cache (/home/runner/.gradle/caches/modules-2) cleanup deleted 0 files/directories.
artifact cache (/home/runner/.gradle/caches/modules-2) [subdir: /home/runner/.gradle/caches/modules-2/resources-2.1] cleanup deleted 0 files/directories.
artifact cache (/home/runner/.gradle/caches/modules-2) [subdir: /home/runner/.gradle/caches/modules-2/resources-2.1] removing files not accessed on or after Tue Jul 23 22:45:31 UTC 2024.
artifact cache (/home/runner/.gradle/caches/modules-2) [subdir: /home/runner/.gradle/caches/modules-2/resources-2.1] cleanup deleted 0 files/directories.
artifact cache (/home/runner/.gradle/caches/modules-2) [subdir: /home/runner/.gradle/caches/modules-2/files-2.1] cleanup deleted 0 files/directories.
artifact cache (/home/runner/.gradle/caches/modules-2) [subdir: /home/runner/.gradle/caches/modules-2/files-2.1] removing files not accessed on or after Tue Jul 23 22:45:31 UTC 2024.
artifact cache (/home/runner/.gradle/caches/modules-2) [subdir: /home/runner/.gradle/caches/modules-2/files-2.1] cleanup deleted 42 files/directories.
artifact cache (/home/runner/.gradle/caches/modules-2) [subdir: /home/runner/.gradle/caches/modules-2/metadata-2.106] cleanup deleted 0 files/directories.
artifact cache (/home/runner/.gradle/caches/modules-2) cleanup deleted 0 files/directories.
artifact cache (/home/runner/.gradle/caches/modules-2) cleaned up in 0.041 secs.
```

This isn't desirable in a multi-step setup - we'd want to retain _all_ the cached entries and only cleanup at the end.

For reasons I don't currently understand the cache isn't updated after the cleanup in the second build, even though the cleanup removed entries:

```
Entry: /home/runner/.gradle/caches/modules-*/files-*/*/*/*/*
Requested Key : gradle-dependencies-v1-40c681dd67f1cd4e62478d3e197f1e6d
Restored Key : gradle-dependencies-v1-40c681dd67f1cd4e62478d3e197f1e6d
Size: 88 MB (92509727 B)
(Entry restored: exact match found)
Saved Key :
Size:
(Entry not saved: referencing 'Gradle User Home' cache entry not saved)
```

The issue starts [here](https://github.com/gradle/actions/blob/fd87365911aa12c016c307ea21313f351dc53551/sources/src/caching/cache-cleaner.ts#L17) where `setup-action` stores the current timestamp, and then in the post-build cleanup removes everything older than that. This assumes a closed-loop where it's a self-contained build, which doesn't work for shared caches (dependencies, wrappers, etc) that other builds will use/update/cleanup.

Not sure what the best solution may be - perhaps loosen the cleanup windows to "older than a day"; not thrilled about making it configurable, as one wouldn't know what to reasonably put (is 10m too long? what if the execution time varies? and so on).

If there's a way to get the "start of the workflow" timestamp that may be more deterministic, though that still leaves holes where there are different workflows running that share those caches - one may wish to save the shared caches for longer periods (e.g. workflows that use those caches that run weekly, for example).

Contributor guide

Open the contributing guide

Research direction

Start with sources/src/caching/cache-cleaner.ts at the timestamp and cleanup logic linked in the report, then reproduce the behavior using the linked Gradle/Kotlin workflow. Compare the cache entries before and after both sequential builds; done means the shared cache is retained correctly and the reported cache-key collision no longer occurs.

Written by the indexing model from the issue text.

Assessment

Tech stack
github-actions, kotlin, typescript
Domain
build-system, ci-cd
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.