Skip to content

feat: add health and readiness checks - #20

Open
lukekim wants to merge 1 commit into
trunkfrom
feat/health-and-readiness-checks
Open

feat: add health and readiness checks#20
lukekim wants to merge 1 commit into
trunkfrom
feat/health-and-readiness-checks

Conversation

@lukekim

@lukekim lukekim commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

What

Adds SpiceClient.IsSpiceHealthyAsync and SpiceClient.IsSpiceReadyAsync, wrapping the runtime's
/health and /v1/ready endpoints.

Why

Readiness is a core part of the runtime's API surface — it is what tells an application the runtime
has finished loading its datasets and accelerations and can serve queries. gospice
(IsSpiceHealthy / IsSpiceReady) and spice.js (isSpiceHealthy / isSpiceReady) both expose it;
the .NET SDK did not, so .NET callers had to stand up their own HttpClient and re-supply the
endpoint, credentials and TLS settings they had already given SpiceClientBuilder. That is also the
check a containerised service needs before it starts issuing queries.

Behaviour notes, for review:

  • Both probes reuse the client's configured HttpClient, so address, API key, TLS/mTLS and user
    agent carry over.
  • An unreachable or unhealthy runtime returns false rather than throwing — that is the state the
    caller is asking about, and it matches gospice and spice.js. Cancellation requested by the caller
    is propagated, so a probe can take part in a wider timeout.
  • The response body is matched exactly rather than by substring: /v1/ready answers not ready
    while components are still loading, which contains the success token. Covered by a test.

Part of aligning capabilities across the Spice SDKs.

Verification

  • Builds locally — dotnet build -c Release, clean across netstandard2.0, net8.0, net9.0
    and net10.0 (0 warnings, 0 errors, with TreatWarningsAsErrors).
  • Unit tests pass — dotnet test -c Release: 123 passed / 0 failed on net8.0 and net10.0
    (up from 115; 8 new tests). net9.0 compiles but could not be executed locally — no .NET 9
    runtime on this machine — so CI is the first run of that leg.
  • Integration tests — not run locally; they need a live runtime or a cloud API key. The 8 new
    tests need neither: they run against an in-process loopback stub endpoint.

Note on the red CI

The Spice.ai Cloud integration tests are failing on this branch, and they fail identically on
trunk. Baseline run dispatched on trunk at the same commit this branch forks from
(run 30173590110):

Total Passed Failed
trunk 148 115 33
this branch 156 123 33

Same 33 tests, same failure — the Flight handshake against Spice.ai Cloud throws in
SpiceFlightClient.AuthenticateAsync, which this PR does not touch. Net effect of this change is
+8 passing tests and no new failures. The pre-existing cloud-auth failure looks like an environment
or credential problem and is worth a separate issue.

Adds SpiceClient.IsSpiceHealthyAsync and SpiceClient.IsSpiceReadyAsync, wrapping
the runtime's /health and /v1/ready endpoints. Both probes reuse the configured
HTTP client, so endpoint, credentials, TLS/mTLS and user agent already set on the
builder apply without the caller reassembling them.

An unreachable or unhealthy runtime is reported as false rather than raised as an
exception, since that is the state the caller is asking about; cancellation
requested by the caller propagates so a probe can participate in a wider timeout.
The readiness body is matched exactly rather than by substring, because the
runtime answers "not ready" while components are still loading.

gospice and spice.js already expose these checks; this brings the .NET SDK in
line.
@lukekim lukekim self-assigned this Jul 26, 2026
@lukekim
lukekim requested a review from Copilot July 26, 2026 01:02
@lukekim lukekim added the enhancement New feature or request label Jul 26, 2026
@lukekim lukekim added this to the v0.3.0 milestone Jul 26, 2026

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.

Pull request overview

Adds first-class health and readiness probes to the .NET SDK (SpiceClient.IsSpiceHealthyAsync / IsSpiceReadyAsync) by wrapping the runtime’s /health and /v1/ready HTTP endpoints, plus unit tests and README guidance so callers can gate startup without re-creating HTTP configuration.

Changes:

  • Added IsSpiceHealthyAsync and IsSpiceReadyAsync to SpiceClient and ISpiceHttpClient and implemented them in SpiceHttpClient via a shared probe helper.
  • Added loopback HTTP stub-based unit tests covering healthy/ready/unreachable/cancellation/disposed behaviors and exact-body matching.
  • Documented health/readiness usage patterns in README.md.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
SpiceTest/HealthCheckTest.cs Adds unit tests with an in-process HTTP stub for health/readiness probe behavior.
Spice/src/SpiceClient.cs Exposes new public health/readiness APIs on the main client surface.
Spice/src/Http/SpiceHttpClient.cs Implements probe calls, including exact response-body matching and transport-failure handling.
Spice/src/Http/ISpiceHttpClient.cs Extends the HTTP client interface with the new probe methods.
README.md Documents how to use the new probes, including polling readiness.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +166 to +172
var (status, body) = respond(path);
var payload = Encoding.UTF8.GetBytes(body);
context.Response.StatusCode = (int)status;
context.Response.ContentType = "text/plain";
context.Response.ContentLength64 = payload.Length;
await context.Response.OutputStream.WriteAsync(payload.AsMemory()).ConfigureAwait(false);
context.Response.Close();
Comment thread README.md
Comment on lines +165 to +166
Both probes return `false` when the runtime is unreachable rather than throwing, so they can be
polled directly. Pass a `CancellationToken` to bound how long a probe waits:
Comment on lines +91 to +92
var unreachable = $"http://127.0.0.1:{StubRuntime.ReserveFreePort()}";
using var client = new SpiceClientBuilder().WithHttpAddress(unreachable).Build();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants