How do I know my build cache hit rate?
You want to know how much of your build comes from the cache. Your build log gives one number for one build, and your FosterStack Cache server's counters give another across all builds. They differ, and this page shows why they differ and how to read each.
Read one build: count it in the build log
This is the number to use for a single build. For Gradle, run with --info on a clean checkout, as CI does, and count two kinds of line:
gradle assemble --info --console=plain > build.log
grep -c 'Build cache key for task' build.log # tasks that can be cached
grep -c '^> Task .* FROM-CACHE$' build.log # of those, taken from the cache
Divide the second number by the first. In our runs a cold build gave 0 of 4, a repeat build gave 4 of 4, and a build after one change gave 3 of 4.
Gradle also prints a summary line, such as 8 actionable tasks: 4 executed, 4 from cache. Do not use it as the hit rate. It also counts tasks that were not cached, such as jar. For our repeat build it said 4 of 8, while all 4 cacheable tasks came from the cache.
For Maven, count the modules it restored and compare with the number of modules that build code, not the parent pom:
grep -c 'Found cached build, restoring' build.log
In our runs, with 3 modules, that printed 0 for a cold build, 3 for a repeat build, and 0 after a change in the bottom module.
Read the server: hits and misses are requests
The server counts every lookup it answers. Read the counters, or the same numbers on the status page:
curl -s localhost:8080/metrics | grep -E 'fscache_cache_(hits|misses)_total'
curl -s localhost:8080/statusz # add -u user:password if login is on
The hit rate is hits divided by hits plus misses. These counters run from the start of the server, so for a recent window use the query on Monitor a Gradle build cache with Prometheus and Grafana.
The server counts requests, not tasks or modules, so it will not match the build log:
- Gradle: our 4 cacheable tasks made 13 requests in a build. Gradle also caches its compiled Kotlin build scripts (
.gradle.kts), and those lookups count too. - Maven: restoring 3 modules made 6 hits. We saw 2 hits for each restored module.
So use the build log to judge one build, and the server to watch the trend over days. Do not compare the two directly.
What is a good number?
There is no single good number. It depends on how often your code changes. A first build against an empty server is 0%.
Measure a few ordinary CI builds with the recipes above, then watch the server's rate over weeks. If it stays low, see why the Gradle hit rate is low or why the Maven cache is not restoring.
How we checked
| Build | Gradle: build log | Gradle: server | Maven: build log | Maven: server |
|---|---|---|---|---|
| 1 cold | 0 of 4 | 0 hits, 13 misses | 0 of 3 | 0 hits, 3 misses |
| 2 repeat | 4 of 4 | 13 hits, 0 misses | 3 of 3 | 6 hits, 0 misses |
| 3 one change | 3 of 4 | 12 hits, 1 miss | 0 of 3 | 0 hits, 3 misses |
Over the three builds, Gradle's log said 7 of 12 tasks, about 58%, and its server said 25 hits in 39 requests, about 64%. For Maven, the log said 3 of 9 modules, about 33%, and the server said 6 hits in 12 requests, 50%. Our repeat builds gave 100% in the build log.
Each tool used one new empty FosterStack Cache 0.2.1 server and three builds. Build 3 changed one method body in the bottom module. The server figures are the change in the counters since the previous build, read from /metrics.
Gradle 9.8.0 built a four-module project of 12 small classes per module with assemble. Maven 3.9.9 with Build Cache Extension 1.2.3 ran mvn verify on a three-module chain with all eight plugin versions pinned. Every build used a fresh copy of the project. Gradle's local build cache was off and its home folder was new; Maven's local cache was empty. Temurin 21.0.12.1, one Mac (arm64).
Not tested: other task types, tests, a server shared by many builds, and the Prometheus window queries.
The first run above was on a Mac with 0.2.1. We ran the recipes again on a GitHub-hosted Linux machine with FosterStack Cache 0.2.2, Gradle 9.8.0, Maven 3.9.9 and Java 21.0.12.1, on a test project with the same layout (four Gradle modules, three Maven modules) that a public script writes. The build-log counts matched: 0, 4 and 3 of 4 tasks for Gradle, and 0, 3 and 0 modules for Maven. Maven's server counts matched too: 0 hits and 3 misses, then 6 hits and 0 misses, then 0 hits and 3 misses. On the Gradle side the server saw 11 requests per build here, where our Mac run saw 13. The requests beyond the four tasks depend on the project, so your number may differ. We ran the two server commands shown above, and they printed the hit and miss counters. The run and its log are public.