2.4 Caching & Incrementality
Bazel can spend time loading and analyzing after bazel shutdown without
rerunning the compiler. In another invocation, one action may execute again
even though no source file obviously changed. These observations involve
different kinds of reuse.
Two questions organize the section. Which reusable state was available to this invocation? Did the action's inputs, command, configuration, environment, tools, and platform still identify the same computation?
Separate Stored State From Action Identity
2.4.1 Skyframe & Incrementality explains warm-server reuse. The in-memory evaluation graph behind 1.1.3 Server/Client Architecture remembers loading, analysis, file state, artifacts, and action execution state so Bazel can invalidate affected nodes after a change. Losing that graph makes loading and analysis cold, but it does not automatically delete reusable action outputs.
2.4.2 Where Bazel Caches Things separates that memory from the output base, repository
cache, optional --disk_cache, and remote cache. Their scopes and lifetimes
explain why bazel shutdown and bazel clean affect different state, and why
some reuse crosses checkouts or machines.
2.4.3 What Makes a Cache Hit moves from location to identity. A target label or existing file is not enough for reuse. Bazel needs the action's recorded inputs and execution context to match a prior result. That identity is trustworthy only when the recorded world matches reality, which connects the key to 2.3.1 Hermeticity.
2.4.4 Diagnosing Cache Misses is the first-response workflow when a successful build reruns work you expected to reuse. It exposes the rerun reason in an explain log. Timing profiles belong to 5.4.1 Timing Profile Analysis, and deeper action comparison belongs to 5.7 Cache & Execution.
Match The Symptom To The Reuse Layer
First name the symptom: did Bazel repeat loading or analysis, or did an action execute again? Then match it to the reuse layer and its observable:
- In-memory graph state. Symptom: loading or analysis became cold after the Bazel server restarted. Observable: a JSON trace profile shows that phase work without corresponding action execution. Bazel records phase and action timing in this compressed profile. 5.4.1 Timing Profile Analysis explains how to capture, compare, and interpret it without treating one lane as a diagnosis.1
- Local persistent reuse. Symptom: an action executes again even though a local output or optional disk-cache result seemed reusable. Observable: the explain log names the rerun action and the input, command, environment, or other change that made its previous result unusable. The command workflow is in 2.4.4 Diagnosing Cache Misses.
- Remote action reuse. Symptom: an action executes after reaching a configured remote cache. Observable: the remote-cache client or service reports a miss, lookup failure, or unavailable blob from the Action Cache (AC) or Content Addressable Storage (CAS). Those records are backend-specific. 5.5.3 Remote Cache Diagnostics shows how to capture and compare them rather than inventing a universal Level 2 command.
Capture the observation first, test the matching hypothesis, and move to another layer only when the evidence points there.
Changed flags, platform choices, tools, stamped metadata, and environment can all make an action legitimately different. A cache hit also needs a trustworthy identity. If an action reads host state that Bazel never modeled, Bazel may reuse a result under a false identity. This connects cache behavior back to 2.3 Hermeticity & Sandboxing and forward to 3.2 Project Configuration, 3.3 Configurable Builds & Platform Basics, and 6.2 Remote Cache.
Trace: After bazel shutdown, a repeated build is slower. Its profile shows
new loading and analysis time, but the explain log names no rerun compile action.
Which reuse layer became cold, and what evidence would have pointed somewhere
else?
Reveal
The in-memory Skyframe graph became cold when the server stopped. The profile shows the cost of reconstructing loading and analysis state, while the absence of a rerun compile action shows that persistent action reuse still worked. If the explain log instead named the compile action and a changed input or command, inspect action identity and local persistent reuse. If the action reached a configured remote cache and its client or service reported a miss or unavailable blob, continue with the remote branch. The artifacts select the branch. Elapsed time alone does not.
The stamping-status snippet demonstrates the cache-sensitive boundary directly: its rule consumes stable and volatile status files as distinct declared inputs.
Failed sandboxed actions belong to 2.3.4 First-Response Debugging Flags first so you can see the command and inspect the sandbox. For a successful build that reruns unexpected work, use 2.4.4 Diagnosing Cache Misses and check the named input, command, rc-file, or configuration change. If the explain log is too coarse for a manual comparison, continue with 5.7 Cache & Execution.
Bazel reuses work at several layers, but action identity decides whether a prior result is valid. Separate warm-server state, persistent local reuse, and remote reuse before asking why a build was fast or why an action ran again.
Footnotes
-
JSON Trace Profile — profiles record build-phase and action timing for later inspection ↩
Sections in this chapter · 4
Skyframe's in-memory dependency graph, invalidation closure, change pruning, and warm-server incrementality.
The scope and lifetime of Skyframe, repository, output-tree, disk, and remote cache layers.
The action inputs, command, environment, configuration, and tools that determine whether outputs are reusable.
First-response diagnosis of unexpected rebuilds with --explain, --verbose_explanations, and --announce_rc.