Skip to content

feat(api): report renderer health to Kubernetes - #797

Open
vaernion wants to merge 2 commits into
mainfrom
agents/implement-issue-779
Open

feat(api): report renderer health to Kubernetes#797
vaernion wants to merge 2 commits into
mainfrom
agents/implement-issue-779

Conversation

@vaernion

@vaernion vaernion commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Why

The existing /health endpoint only confirmed that Fastify could answer HTTP. A pod could therefore remain ready while all document-generation slots were stalled and requests eventually received HAProxy 504 responses.

Closes #779.

What changed

  • track active generation jobs and meaningful renderer progress using a monotonic clock
  • add cached startup rendering and separate startup, readiness, and liveness endpoints while preserving /health
  • mark readiness stalled only when all 10 slots have stopped progressing, and fail liveness after a recovery grace period, while also reporting per-job stall counts for partial degradation
  • bound the complete acquisition/render/recycle/retry path to 50 seconds measured from the moment a request enters the queue, so queue wait plus render stays below the Fastify backstop instead of stacking on top of it
  • treat running out of the generation budget as distinct from Chromium instability, so a busy pod does not recycle the browser out from under queued requests
  • validate at startup that queue wait and the renderer stall timings fit inside the job duration, so a misconfiguration fails loudly instead of silently disabling the probes
  • keep getSnapshot() pure and drive health transitions from job lifecycle events plus a 5-second monitor interval, rather than from whoever happens to poll
  • preserve normal Chromium recycle/retry behavior within the total generation budget
  • expose OpenTelemetry health gauges and transition counters, plus transition-only structured logs
  • report health gauges from a single batched observation, emitting 0/1 for every state
  • expose per-job stall counts so partial slot degradation is visible, not just an all-slots freeze
  • configure NAIS startup, readiness, and liveness probes

Renderer progress uses six operational stages: browser acquisition, page creation, content loading, output production, browser recycling, and retrying.

Timings

  • generation deadline: 50 seconds
  • Fastify handler timeout: 55 seconds
  • readiness stall threshold: 20 seconds
  • liveness recovery grace: an additional 20 seconds
  • renderer monitor interval: 5 seconds

Validation

  • pnpm test --filter api — 64 tests passed
  • pnpm typecheck --filter api
  • pnpm lint --filter api
  • pnpm build --filter api
  • NAIS validation with both dev and prod vars

Copilot AI review requested due to automatic review settings August 7, 2026 11:19
@vaernion
vaernion requested a review from a team as a code owner August 7, 2026 11:19
@github-actions github-actions Bot added documentation Improvements or additions to documentation api nais labels Aug 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances apps/api Kubernetes probe behavior by modeling renderer (Chromium/Puppeteer) progress across the 10 generation slots, adding dedicated startup/readiness/liveness health endpoints, and enforcing a bounded end-to-end generation deadline to avoid pods staying ready while rendering is effectively stalled.

Changes:

  • Introduces a renderer health model (RendererHealth) based on per-job progress stages and monotonic time, with tests validating stall/readiness/liveness transitions.
  • Adds cached startup warm-up rendering plus new /health/startup, /health/readiness, and /health/liveness endpoints while preserving /health.
  • Bounds full generation (including retries/recycle) to 50s and configures Fastify handler timeout at 55s; wires new env vars and NAIS probe configuration.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated no comments.

Show a summary per file
File Description
apps/api/server.ts Wires scheduler health, registers renderer health metrics, and registers new health routes plus startup warm-up render.
apps/api/README.md Documents new timing/env settings and the health/probe behavior.
apps/api/lib/rendererHealth.ts Implements renderer health state machine based on active-job progress and monotonic time.
apps/api/lib/rendererHealth.test.ts Adds unit tests for health transitions, stall detection, and snapshot reporting.
apps/api/lib/otel.ts Adds renderer health-related metrics and registers observable gauges/counters.
apps/api/lib/healthRoutes.ts Adds startup/readiness/liveness endpoints and preserves /health.
apps/api/lib/healthRoutes.test.ts Tests cached startup warm-up behavior and readiness vs liveness semantics.
apps/api/lib/handler.ts Passes scheduler-provided progress reporter and timeout budget into generation pipeline.
apps/api/lib/generationScheduler.ts Adds max job duration support, tracks pending jobs, and integrates renderer health + transition logging/metrics.
apps/api/lib/generationScheduler.test.ts Extends environment parsing tests to cover new timing variables and job duration constraint.
apps/api/lib/generateDocument.ts Plumbs progress + timeout budget into mdToPdf for both template and custom paths.
apps/api/lib/documentGenerationRoute.ts Uses request.signal for cancellation propagation and removes manual abort-signal wiring.
apps/api/lib/core/md-to-pdf.ts Plumbs progress reporting down into output generation.
apps/api/lib/core/index.ts Adds progress + total timeout budget support to mdToPdf via useBrowserWithRetry.
apps/api/lib/core/helpers.ts Introduces DOCUMENT_GENERATION_TIMEOUT_MS and HTTP_HANDLER_TIMEOUT_MS constants.
apps/api/lib/core/get-browser.ts Enforces total generation deadline across acquire/render/retry/recycle and reports renderer progress stages.
apps/api/lib/core/get-browser.test.ts Adds deadline/retry behavior tests with progress stage expectations.
apps/api/lib/core/generate-output.ts Emits progress stage updates across page creation, content loading, and output production.
apps/api/lib/app.test.ts Verifies Fastify handler timeout configuration.
apps/api/app.ts Configures Fastify handlerTimeout from the new constant.
.nais/api.yaml Configures new env vars and switches probes to startup/readiness/liveness endpoints with updated timings.

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

Track generation-slot progress and expose separate startup, readiness, and liveness checks so stalled renderer capacity can be removed from service and eventually restarted. Bound generation below the ingress timeout, preserve browser recycle/retry within that budget, and publish health diagnostics through metrics and transition logs.\n\nCloses #779\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@vaernion
vaernion force-pushed the agents/implement-issue-779 branch from ab3fad0 to 7a1a774 Compare August 7, 2026 11:49
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Brevgen2 Unit Test Results

  5 files   29 suites   1s ⏱️
239 tests 236 ✅ 3 💤 0 ❌
485 runs  476 ✅ 9 💤 0 ❌

Results for commit 4f6a1b8.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Integration Test Results

30 tests   30 ✅  1m 13s ⏱️
 1 suites   0 💤
 1 files     0 ❌

Results for commit 4f6a1b8.

♻️ This comment has been updated with latest results.

Address code review on the renderer health work:

- measure the generation deadline from the moment a request enters the
  queue, so queue wait plus render stays below the Fastify handler
  timeout instead of stacking on top of it
- validate that queue wait and the renderer stall timings fit inside the
  job duration, so a misconfiguration fails at startup instead of
  silently disabling the probes
- lower the default stall threshold and recovery grace to 20s each so
  stalled and unhealthy are reachable within a job's budget
- stop treating deadline exhaustion as browser instability, which made a
  busy pod recycle Chromium out from under queued requests
- resolve the remaining budget before invoking the render function so a
  passed deadline cannot orphan the render promise
- make getSnapshot pure and drive transitions from job lifecycle events
  plus a monitor interval, instead of from whoever happens to poll
- report renderer health gauges from a single batched observation and
  emit 0/1 for every health state
- surface per-job stall counts so partial slot degradation is visible
- pass the scheduler clock through to renderer health
- relax the readiness probe to failureThreshold 3 with a 5s timeout

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sonarqubecloud

sonarqubecloud Bot commented Aug 7, 2026

Copy link
Copy Markdown

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

Labels

api documentation Improvements or additions to documentation nais

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(api): report renderer health to Kubernetes

2 participants