Skip to content

serve: add --requestTimeout and --warmupRevision controls#415

Merged
tinder-maxwellelliott merged 1 commit into
masterfrom
claude/serve-timeout-control-6255f7
Jul 8, 2026
Merged

serve: add --requestTimeout and --warmupRevision controls#415
tinder-maxwellelliott merged 1 commit into
masterfrom
claude/serve-timeout-control-6255f7

Conversation

@tinder-maxwellelliott

Copy link
Copy Markdown
Collaborator

Overview

Adds two operability controls to the bazel-diff serve query service (issue #29):

  1. --requestTimeout <seconds> — bounds how long a request may run.
  2. --warmupRevision <revs> — primes the hash cache + Bazel server at startup.

--requestTimeout

Bounds how long an /impacted_targets / /impacted_targets_with_distances request may run before the server abandons it and returns 504. 0 (the default) means no timeout, preserving current behavior.

Why in-handler rather than a JVM property: the JDK HttpServer's sun.net.httpserver.maxReqTime / maxRspTime only bound reading the request (instant for a GET) and writing the response — neither covers the bazel query that runs synchronously inside the handler, which is the slow part. So they can't ever interrupt a slow query. The timeout is therefore enforced in the handler: when set, the query runs on a bounded worker executor and the handler waits with future.get(timeout), mapping TimeoutException504.

  • Health checks are unaffected (they never run the query path).
  • Caveat: future.cancel(true) interrupts the handler thread, but the underlying bazel query subprocess may not honor the interrupt and can keep running in the background. That's fine — it frees the client immediately and the query still populates the per-SHA cache, so a retry is fast.

--warmupRevision

Comma-separated git revisions (branch/tag/SHA) whose hashes are generated and cached at startup, before the server reports healthy, so the first real request is warm and the Bazel analysis server is primed (the cold first bazel query is the expensive one).

  • Readiness-gated: warmup runs before ready.set(true), so a load balancer won't route to the instance until it's hydrated.
  • Best-effort: a revision that fails to warm (bad ref, transient bazel error) is logged and skipped — the server still becomes ready and serves it cold on demand, so a typo'd --warmupRevision can't lame-duck a deploy. (Deliberately different from fetch failure, which does lame-duck, because a broken clone can't serve correct answers.)
  • Also runs under --no-initial-fetch (resolving against the local clone) for local/offline priming.

Note for reviewers: warmup makes serve do real Bazel work at startup, increasing time-to-healthy. Deploy/health-check timeouts should be sized accordingly (called out in the flag help text).

Tests

  • BazelDiffServerTest.slowRequestTimesOutWith504 — a provider that blocks past a 1s budget returns 504.
  • ServeCommandTest.warmUpCacheGeneratesEachRevisionAndIsBestEffort — each revision is warmed; a failing one neither throws nor stops the others.
  • ServeCommandTest.warmupFailureStillBecomesReady — health is 200 even when warmup fails.

All serve tests pass (//cli:ServeCommandTest, //cli:BazelDiffServerTest). README regenerated via bazel run //tools:generate-readme (only the serve help block changed).

Follow-ups (not in this PR)

  • An end-to-end tools/serve_harness.py case that starts serve --warmupRevision <sha> against the real fixture repo and asserts the first request logs a cache hit.
  • Optionally killing the underlying bazel process on request timeout (requires plumbing cancellation through ImpactedTargetsServiceHashService).

🤖 Generated with Claude Code

Two operability controls for the `serve` query service:

- --requestTimeout <seconds>: bounds how long an
  /impacted_targets(_with_distances) request may run before the server
  abandons it and returns 504. The JDK HttpServer's maxReqTime/maxRspTime
  system properties can't do this — they only cover reading the request and
  writing the response, not the bazel query that runs inside the handler. So
  the timeout is enforced in-handler: when set, the query runs on a bounded
  worker and the handler waits with future.get(timeout). 0 (default) keeps the
  original unbounded behavior. Health checks are unaffected.

- --warmupRevision <revs>: comma-separated git revisions whose hashes are
  generated and cached at startup, before the server reports healthy, so the
  first real request is warm and the Bazel analysis server is primed.
  Readiness-gated (the load balancer won't route until warm) and best-effort
  (a revision that fails to warm is logged and the server still becomes ready,
  serving it cold on demand) so a bad --warmupRevision can't lame-duck a
  deploy.

Tests cover the 504 timeout path, warmup coverage/best-effort, and that a
failed warmup still becomes ready. README regenerated for the two new flags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tinder-maxwellelliott tinder-maxwellelliott marked this pull request as ready for review July 8, 2026 23:23
@tinder-maxwellelliott tinder-maxwellelliott merged commit c964559 into master Jul 8, 2026
15 checks passed
@tinder-maxwellelliott tinder-maxwellelliott deleted the claude/serve-timeout-control-6255f7 branch July 8, 2026 23:23
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.

1 participant