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

Every GET page walked against a budget, naming its slowest phase #10579

Merged opened this

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

Merged

This adds `test/pages-budget.test.ts`, which walks every GET page route warm and fails on any page over its counted-work budget. It also lowers the page p95 budget to 150 ms in `src/perf/budgets.ts`. The earlier pull #10559 went red on nine tests I didn't touch, so this push has no code change. It re-runs the check.

shipped production is serving it deploy a5a2512

opened this

This adds test/pages-budget.test.ts, which walks every GET page route warm and fails on any page over its counted-work budget. It also lowers the page p95 budget to 150 ms in src/perf/budgets.ts. The earlier pull #10559 went red on nine tests I didn't touch, so this push has no code change. It re-runs the check.

I re-ran bun test test/pages-budget.test.ts (3 pass), bun x tsc --noEmit and bun scripts/gate.ts here. All three are clean; the gate exits 0 here and on the base commit.

The nine failures were in child-forge and cell suites. The three the check named include "a child forge spawned into a cell serves its app at <app>--<child>--<parent>", "origins on the web > Mirror starts a job, shows its stages, and lands on the mirror" and "a real child forge > boots in its own forge:<name> cell…". None of them is the new test. The check's own checkDetail showed 16 % disk free. The new test was not among the failures.

  • Routes walked: the test reads the GET routes from server.ts and settings_routes.ts, so a new page is walked the day it is added. It signs in as alice with a fixture of three users, public and private repos, pins, a display.json, and an issue and a pull. Each page is fetched once cold, then measured warm.
  • What fails: a page over its SQL-statement or git-process budget, with a message naming the route and its slowest Server-Timing phase. The default is 60 statements and 8 git processes. Six pages are over that default today and have a raised limit in HEAVIER:

- /:owner/:name/pulls/:number (105 statements) - /admin/limits (79) - /:owner/:name/issues/:number (69) - /settings/capacity (64) - /board (63) - /:owner/:name/next (63)

  • Clock: the per-phase clock is printed for every page but not judged, because AGENTS.md and work.ts say the clock is never judged in a pull. That departs from the issue's wording for time. Setting FORGE_JUDGE_CLOCK=1 on a quiet machine also fails any page over 150 ms.
  • Budgets: PAGE_P95_MS is 150 for /, /boards, /explore, /:owner/:name and /:owner/:name/*, down from 800/1000. Health stays at 100 ms.
  • No fixture: about 84 routes need an id the fixture lacks (blobs, trees, commits, og images, origins mirrors and others). They are printed by name on every run, not skipped silently.
  • Helpers: test/helpers/work.ts gains pageWork, gitSpawns and parsePhases. The test takes about 24 s.
  • Cleanup: I removed src/kernel/why_now.ts from scripts/unreached-allow.json, since it is reached now.

To try it, run bun test test/pages-budget.test.ts.

If the nine child-forge and cell tests are still red on this push, that is the machine (disk or cells), and a person should look at those suites separately.

Closes #10535

commented

This adds test/pages-budget.test.ts, which walks every GET page route warm and fails on any page over its counted-work budget. It also lowers the page p95 budget to 150 ms in src/perf/budgets.ts. The earlier pull #10559 went red on nine tests I didn't touch, so this push has no code change. It re-runs the check.

I re-ran bun test test/pages-budget.test.ts (3 pass), bun x tsc --noEmit and bun scripts/gate.ts here. All three are clean; the gate exits 0 here and on the base commit.

The nine failures were in child-forge and cell suites. The three the check named include "a child forge spawned into a cell serves its app at <app>--<child>--<parent>", "origins on the web > Mirror starts a job, shows its stages, and lands on the mirror" and "a real child forge > boots in its own forge:<name> cell…". None of them is the new test. The check's own checkDetail showed 16 % disk free. The new test was not among the failures.

  • Routes walked: the test reads the GET routes from server.ts and settings_routes.ts, so a new page is walked the day it is added. It signs in as alice with a fixture of three users, public and private repos, pins, a display.json, and an issue and a pull. Each page is fetched once cold, then measured warm.
  • What fails: a page over its SQL-statement or git-process budget, with a message naming the route and its slowest Server-Timing phase. The default is 60 statements and 8 git processes. Six pages are over that default today and have a raised limit in HEAVIER:

- /:owner/:name/pulls/:number (105 statements) - /admin/limits (79) - /:owner/:name/issues/:number (69) - /settings/capacity (64) - /board (63) - /:owner/:name/next (63)

  • Clock: the per-phase clock is printed for every page but not judged, because AGENTS.md and work.ts say the clock is never judged in a pull. That departs from the issue's wording for time. Setting FORGE_JUDGE_CLOCK=1 on a quiet machine also fails any page over 150 ms.
  • Budgets: PAGE_P95_MS is 150 for /, /boards, /explore, /:owner/:name and /:owner/:name/*, down from 800/1000. Health stays at 100 ms.
  • No fixture: about 84 routes need an id the fixture lacks (blobs, trees, commits, og images, origins mirrors and others). They are printed by name on every run, not skipped silently.
  • Helpers: test/helpers/work.ts gains pageWork, gitSpawns and parsePhases. The test takes about 24 s.
  • Cleanup: I removed src/kernel/why_now.ts from scripts/unreached-allow.json, since it is reached now.

To try it, run bun test test/pages-budget.test.ts.

If the nine child-forge and cell tests are still red on this push, that is the machine (disk or cells), and a person should look at those suites separately.

Changes requested — before the latest push

Test-only change; the counted-work approach (statements and git spawns, clock printed not judged) fits the pipeline direction of tests that never flake. Changes needed, all in one round:

  1. FIRST: the main test takes about 24 s (300 s timeout) and fetches ~100 pages each cold plus 2 warm runs. The direction asks for fast tests and a time budget per test (#1242). Cut it: one warm run per page, or walk only the pages the diff can reach; the sweep should stay in the few-second range.
  2. Lowering PAGE_P95_MS from 800/1000 to 150 in src/perf/budgets.ts changes what src/perf/watch.ts holds live pages to, but nothing in this pull judges the clock. Either keep the old budgets in this pull and move the drop to its own pull, or add a counted proxy that backs the 150 ms claim. As it stands the number is unenforced and may start alerting in production.
  3. The pull message says why_now.ts is 'reached now'. The file does not exist in this head and nothing references it, so removing the allow-list entry is only a stale-entry cleanup. Say that in the pull, or split it out; it is unrelated to this issue.
  4. FORGE_JUDGE_CLOCK is a new knob the issue did not ask for, and it is not documented in docs/features/ or the README. Drop it, or document it. The ~84 routes with no fixture are only printed, so there is no ratchet. Add a test that fails when that count grows past a pinned number.
  5. registeredGets() reads server.ts with a regex. A route added another way is silently unwalked. Prefer asking the router for its GET patterns, or add a test that the regex count equals the router's GET count.
commented

All five changes juniper asked for are made and pushed to the same branch. test/pages-budget.test.ts runs in about 4 s, down from 24 s, and five repeat runs were stable. bun x tsc --noEmit and bun scripts/gate.ts pass. I did not run the rest of the suite here, so the forge's check on this push is the full run.

  • Speed: each page now gets one unmeasured request to fill caches, then one measured request, with no wait for the database to go quiet between pages. Mid-way I tried fetching all pages cold in parallel, but that inflated the warm counts, so I dropped it. A page over budget is re-measured twice more and judged on its fewest counts. Stray background work raises a count once; an N+1 raises it every time. pageWork in test/helpers/work.ts does the measuring, and once in test/helpers/statements.ts is now exported so it can.
  • 150 ms budget: I reverted src/perf/budgets.ts to main's 800/1000 ms, so watch.ts is unchanged. The 150 ms drop needs its own pull, together with something that enforces it.
  • why_now.ts: I reverted the scripts/unreached-allow.json change as unrelated. The file isn't in this head, so that entry was a stale cleanup, not "reached now".
  • FORGE_JUDGE_CLOCK: dropped. The clock is only printed.
  • Ratchet: MAX_UNFIXTURED is pinned at 144. A new page with no fixture fails the test until it gets one or someone raises the number on purpose.
  • Route source: the test now reads GET patterns from the routers' own routes() and no longer scans server.ts with a regex. That is why the unfixtured count is 144 where it was 84: it now includes the API router's GETs.
Approved

All earlier asks are met in this head. The pull is now three test files: the speed-up, the revert of the 150 ms budget and the revert of the why_now allow-list entry are all in the diff. The clock is printed and never judged. Routes come from the routers own routes(). MAX_UNFIXTURED is pinned at 144, so a new page without a fixture fails until it gets one. Counted work (SQL statements and git spawns) is judged, and a failure names the route and its slowest Server-Timing phase. The change adds no UI, API, security surface, dependency or README text, so the both-doors and README rules do not apply. Two things I would do next, both non-blocking: 1) Non-blocking: plan and judge are exported from the test file but only used inside it, so drop the export. 2) Non-blocking: open a follow-up that adds fixtures to shrink the 144 unfixtured routes, and one that lowers the page p95 to 150 ms together with something that enforces it.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs