Infrastructure worth keeping — and kept patched.

Can a Failed or Cancelled Build Poison the Cache?

In our runs, a failed or stopped build left no half-built result in FosterStack Cache. Only tasks that finished were stored, and a job killed during an upload left nothing behind.

The short answer

What to do

Our advice, not a test result:

How we checked

FosterStack Cache 0.2.1 on a Mac (arm64), no password, a fresh data folder for each case. Gradle 9.8.0 with Java 21, one fresh copy of the project for every build, no reused daemon, the local build cache off. All builds shared one Gradle home, filled first by a build with the cache off. We counted entries with the server's /statusz page and listed its data folder. Each row is one run; we did not repeat them.

What went wrongWhat the build didEntries storedThe next builds
Syntax error in module b. Three modules, compileJava --continuea and c compiled. b failed. Exit code 12 (a and c)Same build again: a and c from the cache, b failed again, still 2 entries. Code fixed: b ran, a and c from the cache, 3 entries. Fixed again in a new copy: all three from the cache
A JUnit test failscompileJava and compileTestJava ran. The test failed. Exit code 12 (the two compile tasks)Same build again: both compile tasks from the cache, the test ran and failed again, still 2 entries. Test fixed: compileTestJava and the test ran, 4 entries. Again in a new copy: the test from the cache
Ctrl-C (SIGINT) to the Gradle process, 3 seconds after the second of four 8-second tasks had finishedTasks 1 and 2 had finished. Gradle exited with code 130. No Gradle process was left 6 seconds later2 (tasks 1 and 2). No temporary fileNext build: tasks 1 and 2 from the cache, tasks 3 and 4 ran, all four outputs correct. The build after: all four from the cache
SIGKILL to the whole build, at the same momentTasks 1 and 2 had finished. The build was killed, so it has no exit code. No Gradle process was left 6 seconds later2. No temporary fileSame as Ctrl-C
SIGKILL to the whole build while it was uploading a 300 MiB resultThe server held a 2.8 MiB temporary file when we killed it0. The temporary file was goneNext build: the task ran and stored the result. The build after: from the cache, and the file was identical to the one the build before it made
curl killed while uploading 40 MiB at 4 MiB a secondThe server held a temporary file of about 16 MiB, then logged blobstore: write: unexpected EOF0. The temporary file was goneReading that key gave 404. The same upload left alone gave 201 and then 200
A task writes the time into its output, and the time is not an inputFirst build: the task ran and wrote a timestampNot recordedA new copy built a few seconds later took it from the cache with the same timestamp

The three slow-task rows used four tasks that each sleep for 8 seconds, built one at a time. We sent the signal to the process, or process group, we had started, and nothing else. For the 300 MiB row we watched the server's data folder and sent the kill when a temporary file above 1 MiB appeared. A first try caught a small upload instead and is not counted.

Not tested: Maven, Kotlin or Android builds, a CI system's own cancel button, Windows, a second Ctrl-C, more than one build at a time, and a kill at any other moment than the ones in the table.