Skip to main content

Caching

heph caches at the granularity of a target. Before running anything it hashes a target's complete inputs into a single digest; if that digest is already in the cache, heph returns the stored output instead of re-running the action. That is a cache hit.

terminal
$ heph run //app:server
0.565s · 1 / 1 done · 1 cached · 0 failed

What goes into the hash

Everything a target declares folds into its input hash: its sources, its dependencies' outputs, the tools it uses, its environment, and the target definition itself. Change any of them and the digest changes, so the target re-runs. Leave them all identical and you get the cached bytes — on your laptop or in CI. This is the foundation of reproducibility.

note

Because outputs are content-addressed by their inputs, a cache hit is provably the exact artifact you would have built. The cache is trustworthy, not a guess.

Fixed-point caching

A target whose inputs equal its outputs reaches a fixed point: re-running it produces the same digest, so it's a cache hit and never executes again. This is what makes idempotent codegen (an in_place formatter, say) free to re-run — once the tree is formatted, the formatter is a no-op hit.

Controlling caching per target

Whether a target caches, and how aggressively, is a property of the driver that runs it. The exec driver exposes a cache field — a bool, or a map of {enabled, remote, history} — to opt a target out or tune retention. See the driver's page for the exact shape.

In-memory cache

Within a single run, heph also keeps results in memory. Size it with the memCache block in .hephconfig; capacityBytes: 0 disables it.

Durable cache storage

Between runs, artifacts are stored on disk. Small artifacts (≤ 8 MiB by default) are kept in the cache database; larger ones are stored as plain files under <homeDir>/cache/blobs/. GC reclaims both. The threshold is tunable with cache.spillThresholdBytes in .hephconfig.

Remote shared cache

A remote cache lets multiple machines share build artifacts. When one machine builds a target, its artifacts are pushed to the remote; any other machine with the same inputs pulls them instead of rebuilding.

Configure one or more remote caches in .hephconfig:

.hephconfig
caches:
shared:
uri: s3://my-bucket/heph-cache # s3://, gs://, az://, https://, file://

Writes happen on a background task after a local cache write — every writable remote receives the artifacts in parallel. The build's critical path does not wait on the network; heph stays open until all uploads drain.

Reads happen on a local cache miss. heph tries remotes in ascending-latency order; the first cache holding a matching entry is a cache hit. Confirming the hit only costs a small metadata fetch — the output bytes themselves transfer later and lazily, and only for the outputs something actually reads. Latency is measured once per process (or loaded from a persisted file) and refreshed when the cache definitions change.

A failing remote never fails the build — errors are logged once and, after three consecutive failures, that cache is paused with an exponential backoff. It resumes automatically the next time a request to it succeeds.

For the full set of options and supported backends, see caches in the configuration reference. For a step-by-step CI walkthrough, see the Remote cache guide.

Filesystem scan cache

heph also remembers the results of workspace file scans — glob expansion, package discovery — across runs, validated against file and directory metadata. Any change to the tree re-scans the affected paths, so edits are always picked up. This is transparent and needs no configuration.

tip

When debugging, rule the scan cache out by setting HEPH_DEBUG_CACHED_WALKER=0 — every scan then reads the filesystem directly, with no caching at all.

Reclaiming space

A target's revisions beyond its configured history count are trimmed automatically at the end of the run that writes the new one — there's nothing to run for that.

The on-disk cache still grows as inputs change. Garbage-collect everything else that's no longer reachable (removed targets, orphaned entries):

terminal
heph tool gc