Skip to content

Share halide-cache entries through a server - #7

Closed
RMPR wants to merge 6 commits into
halide-cache-cleanupfrom
halide-server
Closed

RMPR wants to merge 6 commits into
halide-cache-cleanupfrom
halide-server

Conversation

@RMPR

@RMPR RMPR commented Sep 20, 2026 •

Copy link
Copy Markdown
Member

Share halide-cache entries between machines through a central server. Second of three stacked PRs, on top of #8; monitoring follows in #9.

  • lager: support multi-file entries. An entry is a zstd-compressed tar of the files in a fixed order, written atomically, and openable with zstd -dc entry.tar.zst | tar -x. Eviction is safe against concurrent readers and writers.
  • halide-cache: store both outputs as one entry. The object and header are stored, fetched and evicted together under one address.
  • halide-cache: stabilize entries across machines. The key strips the base directory anywhere in an argument, and includes a scheme version, the platform, the Python interpreter, and halide's native libraries.
  • halide-cache: support Windows clients. Local cleanup is best effort, and CI runs the client tests on Windows.
  • Add halide-cache-server. GET and PUT of entries, eviction to half of --size every 30 minutes, a systemd unit, and static musl binaries in releases.
  • halide-cache: add --remote. Local lookup, then the server, then build and upload. Every remote problem is a warning, never a failed build.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TVxsuu7ZCt2cdfn99fuXaA

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Concurrency, cache-size enforcement, fail-open behavior, security defaults, and CI test selection have unresolved correctness issues.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 4 High severity · 5 Medium severity

Open (9)
What changed in this PR

Adds a shared HTTP cache server and remote-cache support to the existing Halide cache client.

Changes:

  • Adds remote lookup/upload support and cross-machine cache keys.
  • Introduces a cache server with LRU eviction, metrics, dashboard, and service packaging.
  • Expands concurrent end-to-end tests and CI/release coverage.
File Description
lager/​src/​lru.rs Exposes eviction details and skips temporary files.
lager/​src/​lib.rs Exports new storage APIs.
lager/​src/​lager.rs Adds raw compressed-blob operations.
halide-cache/​tests/​run Adds remote and concurrent system tests.
halide-cache/​src/​remote.rs Implements the HTTP cache client.
halide-cache/​src/​main.rs Integrates remote caching and key hardening.
halide-cache/​doc/​remote-cache-design.md Documents architecture and protocol decisions.
halide-cache/​Cargo.toml Adds remote-client dependencies.
halide-cache-server/​src/​metrics.rs Tracks operational cache metrics.
halide-cache-server/​src/​main.rs Implements the HTTP server and eviction.
halide-cache-server/​src/​dashboard.html Adds the monitoring dashboard.
halide-cache-server/​contrib/​halide-cache-server.service Provides systemd deployment configuration.
halide-cache-server/​Cargo.toml Defines the server crate.
Cargo.toml Adds the server to the workspace.
Cargo.lock Locks new dependencies.
.github/​workflows/​rust.yml Expands build and test coverage.
.github/​workflows/​release.yml Publishes static server binaries.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread halide-cache-server/contrib/halide-cache-server.service Outdated
Comment thread halide-cache/src/main.rs Outdated
Comment thread lager/src/lager.rs
Comment thread lager/src/lru.rs
Comment thread .github/workflows/rust.yml Outdated
Comment thread halide-cache-server/src/metrics.rs Outdated
Comment thread halide-cache/src/main.rs Outdated
Comment thread halide-cache/src/main.rs Outdated
Comment thread lager/src/lager.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +14 to +16
# Required: the unit refuses to start without the token file, so a missing or
# misnamed file cannot silently leave uploads open to everyone on the network.
EnvironmentFile=/etc/halide-cache-server.env

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 769af79: new --require-token flag makes the server refuse to start unless a non-empty token is configured; the systemd unit passes it.

Comment thread halide-cache-server/src/blobs.rs Outdated
Comment thread halide-cache-server/src/eviction.rs
Comment thread halide-cache-server/src/main.rs Outdated
let state = Arc::new(AppState {
lager: Lager::new(&args.data_dir)?,
max_size: args.size.as_u64(),
token: args.token.filter(|t| !t.is_empty()),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 769af79: see --require-token. The bare binary keeps the open mode for local/dev use, the unit opts into the strict mode.

Comment thread halide-cache-server/src/main.rs Outdated
.route("/v1/clients", get(stats::clients))
.route("/metrics", get(stats::prometheus))
.route("/healthz", get(|| async { "ok" }))
.layer(DefaultBodyLimit::max(args.max_blob_size.as_u64() as usize))

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 769af79: enforced while streaming, 413 on overflow. See the reply above.

Comment thread halide-cache/src/main.rs Outdated
Comment thread lager/src/lager.rs Outdated
Comment thread halide-cache/tests/run Outdated
Comment thread halide-cache/doc/remote-cache-design.md Outdated
Comment thread halide-cache/doc/remote-cache-design.md Outdated
@RMPR
RMPR force-pushed the halide-server branch 3 times, most recently from d1dddd8 to 603ff1c Compare September 29, 2026 14:10
@RMPR
RMPR requested a balanced review from Copilot September 29, 2026 16:50

Copilot AI left a comment

Copy link
Copy Markdown

.and_then(|v| v.to_str().ok())
.and_then(Kind::parse)
.ok_or(Error::BadKind)?;
let body = response.body_mut().with_config().limit(u64::MAX).reader();
Comment thread lager/src/lager.rs
Comment on lines +159 to +163
if result.is_err() {
let _ = std::fs::remove_file(&tmp);
}
self.remove_other_kind(address, kind)?;
result.map(|_| !existed)
Comment on lines +153 to +157
Ok(Err(e)) => {
// A client that dropped the connection shows up here as an io error
// while reading the body.
warn!(%address, err = %e, "upload failed");
(StatusCode::BAD_REQUEST, "upload failed\n").into_response()
Comment thread halide-cache-server/src/eviction.rs Outdated
Comment on lines +37 to +38
let Ok(_guard) = state.evict_lock.try_lock() else {
return;
Comment on lines +59 to +65
let before = lru.lager_size();
let evicted = if before > max {
lru.evict_until(target)?
} else {
Vec::new()
};
Ok::<_, lager::Error>((before, evicted, lru.lager_size(), lru.entries()))
Comment thread halide-cache-server/src/metrics.rs Outdated
.duration_since(e.last_used)
.map(|d| d.as_secs())
.unwrap_or(0);
let i = AGE_BOUNDS.iter().position(|&b| age < b).unwrap_or(5);
Comment thread halide-cache/src/main.rs Outdated
Comment on lines +250 to +253
let builder_exe = which::which(&args.builder[0])
.map_err(|e| anyhow::anyhow!("cannot locate builder {:?}: {e}", args.builder[0]))?;
let halide = if is_python(&builder_exe) {
Some(locate_halide(&builder_exe)?)
@RMPR
RMPR force-pushed the halide-server branch 17 times, most recently from 6113d4f to e89b74b Compare October 5, 2026 08:50
an entry can hold several files: a zstd-compressed tar archive of the
files in a fixed order, so `zstd -dc entry.tar.zst | tar -x` opens it.

Prework for the server
RMPR added 5 commits October 5, 2026 10:56
The object and header are always produced together, so this commit
changes how halide-cache stores, restores and later: fetches, uploads
and evicts them together with the same address in lager.
Entries are about to be shared, so the key must drop what differs
between machines and include what makes an object valid elsewhere:

- strip the base directory wherever it occurs in an argument and
  normalise path separators
- prefix a key scheme version, and include the host OS and architecture
- hash the builder executable, the Python interpreter, resolved through
  PATH. Ask it where it imports halide from and hash that package's
  native libraries. A missing or unusable halide is an error rather than
  a weaker key.
- reject non-UTF-8 output paths instead of hashing a lossy conversion
Make local cache cleanup best effort, since Windows refuses to delete an
entry another parallel build has open and the outputs are already in
place. Run the client's tests on Windows in CI.
An HTTP server sharing cache entries between machines, storing them
through lager in the same layout as the local cache.

  GET/PUT /v1/blobs/{address}

Ships a systemd unit, and static musl binaries for x86_64 and aarch64 in
CI and releases to be self-contained and independent of the Glibc
version of the host.
Look locally, then on the server, then build and upload. A remote hit is
stored locally and triggers the local LRU cleanup. The remote is
fail-open: connection errors, failed uploads and entries that do not
decompress are warnings, and the build proceeds locally.

The end-to-end tests start the server and cover hits across machines, a
server that is down, corrupt entries, oversized uploads, and concurrent
clients racing on one entry under constant eviction. They run on Unix
only, since the server is deployed on Linux.
@RMPR RMPR changed the title Halide server Share halide-cache entries through a server Oct 5, 2026
@RMPR
RMPR changed the base branch from master to halide-cache-cleanup October 5, 2026 09:48
@RMPR

RMPR commented Oct 5, 2026

Copy link
Copy Markdown
Member Author

Superseded by the stacked PRs #8, the new remote-cache PR, and #9.

@RMPR RMPR closed this Oct 5, 2026
@RMPR

RMPR commented Oct 5, 2026

Copy link
Copy Markdown
Member Author

Replaced by #8, #10 and #9.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants