Skip to content

feat: live results while a run is in progress - #19

Merged
psanders merged 2 commits into
mainfrom
feat/live-results
Nov 5, 2026
Merged

psanders merged 2 commits into
mainfrom
feat/live-results

Conversation

@psanders

@psanders psanders commented Nov 5, 2026

Copy link
Copy Markdown
Member

While a run is in progress, the benchmarks page shows it live. Live is on for every run.

What changes

  • live.json (perfkit/live/v1). Zod schema plus schemas/live-v1.schema.json. The agent writes <run root>/live.json every 15 s (temp file, then rename): run id, version, commit, status (running, finished, failed with reason), project, estimated end, and every benchmark in perfkit.yaml order with its state (queued, running, done, not-run).
    • Capacity: the current step (from the clock), every step the agent already judged (offered, placed, calls, failed, p50/p95/p99, failed %, ok, stop reason), heldCps, and once done result (limit, lower bound, stop reason).
    • Soak: elapsed, warm-up and duration, calls placed and failed so far, raw RSS per process (at most 300 points), and growthMBPerHour, which stays null until there is a full hour of data after the warm-up.
    • It reuses the analyzer: evaluateCapacityStep now returns the step's numbers, and the soak's least-squares growth is moved into memoryGrowthMBPerHour, which the verdict also calls.
  • Serving it. GET /live.json from a node:http server on 127.0.0.1:<live.port> (default 8787, --live-port):
    • responses carry Cache-Control: no-store;
    • CORS allows the origin of project.siteUrl and http://localhost:*, plus the ngrok-skip-browser-warning header, and the server answers the OPTIONS preflight;
    • it answers 503 before the first snapshot and 404 for anything else.
    • With NGROK_AUTHTOKEN, an @ngrok/ngrok tunnel opens on live.domain or NGROK_DOMAIN. The server and tunnel close when the run ends.
    • On DigitalOcean the agent serves it and stays up 30 s after the run so open pages see it end. With --local, the run/collect process serves it from this machine.
  • Where the page finds it. You can set project.live.url or live.url in perfkit.yaml, or let it default to https://<live.domain>/live.json. The URL goes into report.json project.live.url, an additive optional field. ?live=<url> on the page overrides it.
  • Live view in the app. Built from the approved "State / Live (preliminary)" frame: live picker, preliminary banner with a ticking "updated N s ago" (warning after 60 s, "connection lost, retrying" after 3 min or a failed poll), header, progress cards and overall bar, and live numbers.
    • Capacity uses the report's own latency chart, drawn up to the current step, with a "now" marker, the steps not run yet shaded, and a "So far" card with the last published limit.
    • Soak uses the report's memory chart with the warm-up band, a "now" line and the MB/h note.
    • When the run finishes, the page says the report is being published and draws it once data/runs.json lists the run. Run pages never go live.
    • Respects prefers-reduced-motion. The final report is unchanged: live CSS and chart options are added only when live.
  • perfkit serve --site <dir> [--port 8080]. A static server for local previews. The README has the local recipe.
  • Workflow. run.yaml takes an optional NGROK_AUTHTOKEN secret and passes it only to the start job, which puts it into the agent container's environment.

Verified

  • npm test (626 passing), npm run typecheck, npm run lint.
  • A real perfkit run --local: a 3-step ramp, then a 3-minute soak with a 1-minute warm-up, watched with perfkit serve and headless Chromium. The page went live, updated, showed "Run finished; the report is being published", then drew the final report once it was rendered into the site.

Notes for review

  • The @ngrok/ngrok native binding for the agent's Alpine container is installed on the load generator at start (npm install of the pinned 1.7.0 in node:22-alpine), only when a token is set. It never fails the run.
  • Docker keeps a container's environment in its config on the droplet, so the token lives there until the droplet is deleted. It is never in run.json, a log or the report.

🤖 Generated with Claude Code

The agent writes live.json (perfkit/live/v1) every 15 s and serves it
read-only with CORS for the project's site and localhost, tunnelled with
@ngrok/ngrok when NGROK_AUTHTOKEN is set. The latest page polls it and
draws the live view; perfkit serve previews a site locally.
@psanders
psanders merged commit 090ef92 into main Nov 5, 2026
2 checks passed
@psanders psanders mentioned this pull request Nov 5, 2026
@psanders
psanders deleted the feat/live-results branch November 5, 2026 14:26
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