Repository navigation
feat(cache): shared sccache compile cache for contextful gate parts - #238
Conversation
The dispatcher serves sccache's WebDAV backend at /v1/compile-cache from RUNS_STORAGE under compile-cache/<owner>/<name>/, admitting a token the workflow mints per execution: an HMAC under a key derived from HMAC_SECRET, bound to the repository and execution, expiring at the instance's creation plus its queue age and maxDurationSec. Entries are bounded per object and per repository prefix; an hourly sweep deletes expired entries and evicts the oldest-written down to 90% of the ceiling. A run whose definition answers compileCache with true gets the endpoint and token; check wraps its command so rustc runs under sccache with CARGO_INCREMENTAL=0, prints sccache --show-stats at the end, and compiles locally when the sccache server does not start. contextful-gate asks for the cache on every part. The Rust sandbox variant bakes sha256-pinned sccache v0.18.0. Spec: specs/adr/0007-cost-efficient-execution.md
There was a problem hiding this comment.
AI code review — 🛑 Request changes
Risk tier: full · 1 critical · 0 warnings · 0 suggestions
Reviewers: security
1. 🛑 Critical — Truncated sweeps can undercount repository usage and bypass the byte ceiling
📍 apps/dispatcher/src/routes/compile-cache.ts:278-321
When the R2 listing reaches MAX_SWEEP_PAGES, the sweep marks the result truncated but still records usage metadata for repositories present in the partial listing. PUT admission then trusts that lower-bound 'bytes' value, so a large repository can be admitted as if it were only partially measured and can exceed COMPILE_CACHE_MAX_REPO_BYTES without bound. Do not persist or use partial measurements for admission; retain the prior usage record or fail closed until a complete measurement succeeds.
Every contextful gate part compiles the same workspace cold. This PR gives those parts a shared sccache cache backed by the dispatcher's existing R2 binding, so after the first part compiles a crate, later parts read it from the cache instead of compiling it again.
Problem & Insight
workspace.compiletakes 1642 s and the CLI shards take 18–40 min each. Most of that work is the same rustc invocations repeated with identical inputs.services-webdav, sccache 0.18) uses only GET, PUT,Depth: 0PROPFIND and MKCOL. Before each PUT it runs a PROPFIND on the parent. If the server reports every collection as existing, a write costs one PROPFIND plus one PUT, and MKCOL is never sent.SCCACHE_IGNORE_SERVER_IO_ERRORdoes not help here because it only applies once the server is running. So starting the server has to be the explicit fallback point.Take
routes/compile-cache.ts): serves/v1/compile-cache/<owner>/<name>/…fromRUNS_STORAGEundercompile-cache/<owner>/<name>/. It is wired in before the typed router (an escape hatch) so PUT bodies stream straight into R2.COMPILE_CACHE_MAX_OBJECT_BYTES→ 413. A write that would push the repo's prefix pastCOMPILE_CACHE_MAX_REPO_BYTES(as measured by the last sweep) → 507.41 * * * *) deletes entries older thanCOMPILE_CACHE_TTL_DAYS. If a repo is still over its ceiling, it deletes the oldest-written entries until the repo is at 90% of the ceiling.compile-cache-token.ts):HMAC_SECRET, so no new secret is needed.maxDurationSec. Because it is derived from the creation time, every invocation mints the same token.RunSpec.compileCachelets a run opt in. When it does, the workflow injects the endpoint and token through config overrides.checkreads them directly (inline, not in a checkpointed step), wraps the command withcompileCacheCommandand redacts the token from logs.contextful-gatesetscompileCache: trueon every part.SCCACHE_WEBDAV_KEY_PREFIX=<repo>/<rustc release>/<host triple>fromrustc -vVinside the checkout.CARGO_INCREMENTAL=0,SCCACHE_IDLE_TIMEOUT=0andSCCACHE_IGNORE_SERVER_IO_ERROR=1.sccache --start-serverfirst and only setsRUSTC_WRAPPERif the server starts.sccache --show-statsinto the part's log and exits with the command's own status.Dockerfile.sandbox-demoinstalls sccache v0.18.0 (static musl build), checked against a pinned SHA-256.Verified locally with sccache 0.18.0 against this route served over Node HTTP:
Cache hits 1,Cache hits rate 100.00 %.Key actions / todos
/bin/shwith stub binariescheckenv wiring and token redaction; gate parts request the cache--show-statslines on the next gates, then decide whether the 20 GiB ceiling holds the workspace's feature sets.