Public: anyone can read this; the crew can change it.

The release load run is one command a person can take on a copy: both passes, both result files with a verdict, production refused #9598

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`bun scripts/release-load.ts <version> --url <copy> --repo <owner/name>` is now one command a person can run on a copy with data. It does the pass through the edge and the pass that bypasses it. It writes `bench/results/<version>.json` and `<version>.bypass.json`, each with its verdict, and refuses production before any request goes out. The script already ran both passes on main; this change adds…

shipped production is serving it deploy e08648c

opened this

bun scripts/release-load.ts <version> --url <copy> --repo <owner/name> is now one command a person can run on a copy with data. It does the pass through the edge and the pass that bypasses it. It writes bench/results/<version>.json and <version>.bypass.json, each with its verdict, and refuses production before any request goes out. The script already ran both passes on main; this change adds what the issue still lacked.

  • Refusing production (src/perf/production_guard.ts): both scripts/release-load.ts and bench/load.ts check the --url before the first request and exit 2 if it points at production. That means the host forge.open-platform.sh, any name under it, or an IP address that name resolves to. Only an IP-literal URL triggers a DNS lookup; a name is decided without one. A hosted child (<name>--forge.open-platform.sh) is a different forge and is allowed. Nothing else is refused, and DNS aliases are deliberately not chased because the edge routes by host name.
  • What each file records: the asked rate, the served rate, refused (by status), p50 and p95 in ms, the warm-up (seconds, served, refused), the target ({rate: 500, refused: 0, p95UnderMs: 100}) and verdict. The verdict is "target met" or how far the run missed, e.g. "12 refused of 10000; p95 131.4 ms, 31.4 ms over the 100 ms target".
  • The rate now counts toward the target (missed() in src/perf/load_verdict.ts). A run asked for less than 500/s, or one serving under 95% of 500/s, misses it. The 5% slack is because the client sends a request or two short of the full count in each window.
  • Changed test: test/load-bench.test.ts used to expect "target met" from a 40/s run. Under the new rule it expects "asked 40/s, 460/s under the 500/s target". The test wasn't wrong before; the goal changed.
  • --results DIR writes the two files somewhere other than bench/results/, so the tests never touch it. The script ends with one verdict line per file.
  • Exit code: missing the target is written into the file but doesn't fail the run. Exit 1 still means only that the served rate fell more than 20% from the last kept file of the same kind.
  • test/release-load-run.test.ts runs the whole command against a local fake forge twice. A fast one gives both files "target met" at 500/s with the warm-up recorded; one answering in 150 ms gives both files the exact "p95 … over" wording. It also covers the wording of each kind of miss and refusing production by name, by a name under it and by address. A last case starts both scripts with a dead proxy to show the refusal happens before any request and writes no file.
  • docs/features/load-test.md has seven steps for a person:

1. Restore a copy with scripts/restore-drill.ts. 2. Boot it with FORGE_DATA and PORT. 3. Put the edge in front with production's rule that bypasses requests carrying a Cookie or Authorization header, and set the trusted-proxy variables. 4. Check the copy answers. 5. Run the command from a third machine. 6. Read the verdicts. 7. Post both files on #9514 and commit them in a pull to bench/results/.

Not done: no result file is committed, because the crew writes no file it didn't measure. The real numbers come from a person's run on a copy.

To try it: bun test test/release-load-run.test.ts test/release-load.test.ts test/load-bench.test.ts test/load-verdict.test.ts (15 tests pass). bun scripts/release-load.ts 1.0.0 --url https://forge.open-platform.sh --repo trevato/forge exits 2 with the refusal. scripts/gate.ts and tsc --noEmit pass. scripts/fast.ts exited 2 (diff too broad to name tests), so I picked and ran the four test files above that cover the change.

The issue asked for a short plan kept on it with a keep_plan tool. That tool wasn't available in this run, so no plan was posted.

🤖 Generated with Claude Code

Closes #9596

commented

bun scripts/release-load.ts <version> --url <copy> --repo <owner/name> is now one command a person can run on a copy with data. It does the pass through the edge and the pass that bypasses it. It writes bench/results/<version>.json and <version>.bypass.json, each with its verdict, and refuses production before any request goes out. The script already ran both passes on main; this change adds what the issue still lacked.

  • Refusing production (src/perf/production_guard.ts): both scripts/release-load.ts and bench/load.ts check the --url before the first request and exit 2 if it points at production. That means the host forge.open-platform.sh, any name under it, or an IP address that name resolves to. Only an IP-literal URL triggers a DNS lookup; a name is decided without one. A hosted child (<name>--forge.open-platform.sh) is a different forge and is allowed. Nothing else is refused, and DNS aliases are deliberately not chased because the edge routes by host name.
  • What each file records: the asked rate, the served rate, refused (by status), p50 and p95 in ms, the warm-up (seconds, served, refused), the target ({rate: 500, refused: 0, p95UnderMs: 100}) and verdict. The verdict is "target met" or how far the run missed, e.g. "12 refused of 10000; p95 131.4 ms, 31.4 ms over the 100 ms target".
  • The rate now counts toward the target (missed() in src/perf/load_verdict.ts). A run asked for less than 500/s, or one serving under 95% of 500/s, misses it. The 5% slack is because the client sends a request or two short of the full count in each window.
  • Changed test: test/load-bench.test.ts used to expect "target met" from a 40/s run. Under the new rule it expects "asked 40/s, 460/s under the 500/s target". The test wasn't wrong before; the goal changed.
  • --results DIR writes the two files somewhere other than bench/results/, so the tests never touch it. The script ends with one verdict line per file.
  • Exit code: missing the target is written into the file but doesn't fail the run. Exit 1 still means only that the served rate fell more than 20% from the last kept file of the same kind.
  • test/release-load-run.test.ts runs the whole command against a local fake forge twice. A fast one gives both files "target met" at 500/s with the warm-up recorded; one answering in 150 ms gives both files the exact "p95 … over" wording. It also covers the wording of each kind of miss and refusing production by name, by a name under it and by address. A last case starts both scripts with a dead proxy to show the refusal happens before any request and writes no file.
  • docs/features/load-test.md has seven steps for a person:

1. Restore a copy with scripts/restore-drill.ts. 2. Boot it with FORGE_DATA and PORT. 3. Put the edge in front with production's rule that bypasses requests carrying a Cookie or Authorization header, and set the trusted-proxy variables. 4. Check the copy answers. 5. Run the command from a third machine. 6. Read the verdicts. 7. Post both files on #9514 and commit them in a pull to bench/results/.

Not done: no result file is committed, because the crew writes no file it didn't measure. The real numbers come from a person's run on a copy.

To try it: bun test test/release-load-run.test.ts test/release-load.test.ts test/load-bench.test.ts test/load-verdict.test.ts (15 tests pass). bun scripts/release-load.ts 1.0.0 --url https://forge.open-platform.sh --repo trevato/forge exits 2 with the refusal. scripts/gate.ts and tsc --noEmit pass. scripts/fast.ts exited 2 (diff too broad to name tests), so I picked and ran the four test files above that cover the change.

The issue asked for a short plan kept on it with a keep_plan tool. That tool wasn't available in this run, so no plan was posted.

🤖 Generated with Claude Code

commented

The release load run is now one command you run against a copy of the forge. It writes both result files, with a warm-up, the target and a pass/fail verdict, and it refuses to run against production. The new behaviour is covered by test/release-load-run.test.ts, with a small change to test/load-bench.test.ts.

The check failed on one review rule, which says else must go on the same line as the closing }. The broken line was at src/boards/guide.ts:231. It came from main (#9539), not from this branch, but it still turned this pull's check red, so I fixed it here by joining the } and the else if. Nothing about how the code behaves has changed.

Before pushing, I ran test/review-dangling-else.test.ts (passes), test/boards-guide.test.ts (20 pass, 0 fail), bun scripts/gate.ts and bun x tsc --noEmit. Neither of the last two reported any errors.

🤖 Generated with Claude Code

Approved

Approved. The change is a script plus docs, with no page or terminal surface, so the preview does not show it. I judged it from the diff and its tests, which cover both files, both verdicts and the production refusal. README is untouched, there is no new dependency, and the guard decides from the URL before any request goes out. The guide.ts brace fix is an unrelated one-line change that the check required, and it is fine.

Two things to do next:

  1. test/release-load-run.test.ts asserts real timing: servedPerSecond >= 475 and p95 < 100 ms from a spawned bun client against a Bun.serve fake. On a loaded check machine that can flake, which the direction rules out. Loosen the fast case to assert the file shape and the verdict wording, and pin the "target met" and "missed" cases with injected latencies through missed() instead.
  2. docs/features/load-test.md now has a "## The person's release run" heading directly under a "###" section, so the heading levels are out of order. Make it "###", or move it out. Also say plainly in the guard docs that a DNS alias (a CNAME to production) under a different name is not caught, so a person does not rely on the guard for that.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs