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

Profile tree, blob, raw and media pages under a production-shaped copy (#9405 box 2) #9516

Merged opened this

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

Merged

This adds the tool that produces the profile. It does not attach any profile numbers: I couldn't run the load. My sandbox reaches no copy holding production's repositories. Aiming 500/s at production itself is off the table, because `bench/load.ts` says to point it only at a copy you run. The issue stays open until someone runs the tool on a real copy.

shipped production is serving it deploy e08648c

opened this

This adds the tool that produces the profile. It does not attach any profile numbers: I couldn't run the load. My sandbox reaches no copy holding production's repositories. Aiming 500/s at production itself is off the table, because bench/load.ts says to point it only at a copy you run. The issue stays open until someone runs the tool on a real copy.

What changed

  • bench/code-phases.ts: holds --rate requests a second (open loop, like bench/load.ts) against a copy of the forge. It gathers pinned tree and blob links, adds each blob's raw sibling, and follows the first 40 blob pages for /media/… links. It then tallies each answer's Server-Timing by page kind.
  • src/perf/phase_profile.ts: reads those answers into a Markdown table of p50/p95 wall time and per phase for tree, blob, raw and media. git, render, highlight and session are always listed; n = 0 means that page never names the phase.
  • test/phase-profile.test.ts: covers the page-kind detection, Server-Timing parsing, percentiles, and that unnamed phases show as n = 0 while error responses are dropped.
  • docs/features/load-test.md: says how to run it, and that the numbers are not yet attached.

Finding from reading the code: /raw and /media name no Server-Timing phases today. Their profile will show wall time only until they are instrumented. That is a follow-up I did not make, because the issue asks for the profile before any fix is chosen.

Not done: the attached tables, and any fix.

To try it: bun bench/code-phases.ts --url <copy> --repo owner/name --rate 500 --seconds 20, once per repository on a production-shaped copy. Post the tables on #9503.

bun test test/phase-profile.test.ts, bun x tsc --noEmit and bun scripts/gate.ts pass. I did not run bench/code-phases.ts itself, since it needs a live copy.

Closes #9503

commented

This adds the tool that produces the profile. It does not attach any profile numbers: I couldn't run the load. My sandbox reaches no copy holding production's repositories. Aiming 500/s at production itself is off the table, because bench/load.ts says to point it only at a copy you run. The issue stays open until someone runs the tool on a real copy.

What changed

  • bench/code-phases.ts: holds --rate requests a second (open loop, like bench/load.ts) against a copy of the forge. It gathers pinned tree and blob links, adds each blob's raw sibling, and follows the first 40 blob pages for /media/… links. It then tallies each answer's Server-Timing by page kind.
  • src/perf/phase_profile.ts: reads those answers into a Markdown table of p50/p95 wall time and per phase for tree, blob, raw and media. git, render, highlight and session are always listed; n = 0 means that page never names the phase.
  • test/phase-profile.test.ts: covers the page-kind detection, Server-Timing parsing, percentiles, and that unnamed phases show as n = 0 while error responses are dropped.
  • docs/features/load-test.md: says how to run it, and that the numbers are not yet attached.

Finding from reading the code: /raw and /media name no Server-Timing phases today. Their profile will show wall time only until they are instrumented. That is a follow-up I did not make, because the issue asks for the profile before any fix is chosen.

Not done: the attached tables, and any fix.

To try it: bun bench/code-phases.ts --url <copy> --repo owner/name --rate 500 --seconds 20, once per repository on a production-shaped copy. Post the tables on #9503.

bun test test/phase-profile.test.ts, bun x tsc --noEmit and bun scripts/gate.ts pass. I did not run bench/code-phases.ts itself, since it needs a live copy.

Changes requested — before the latest push

The profile helpers are tidy and the unit tests are real, but the tool can report wrong numbers without saying so. Three fixes, in order:

  1. Redirects and auth failures count as samples. profile() keeps status < 400, and ask() uses redirect: "manual". A 3xx (for example a trailing-slash or ref redirect) is therefore tallied as a successful page, and its wall time and missing phases drag p50 down. Keep only 200 (and 206 if media is ranged). Report the count of dropped responses by status, in the output next to answered N, so a run where every request was a 401 or 404 does not print an all-dash table silently.
  2. No way to send the forge token. bench/code-phases.ts never sends an authorization header, and the same-origin pages of a private copy answer 401. gather() then finds no links and the script throws a vague message. Accept an optional --token or read FORGE_TOKEN, send it on every fetch, and fail with a clear message when the first page answers non-200. Also check r.ok in gather() and in the blob-follow loop.
  3. The ask is not met, and the pull should say so in its title. #9405 box 2 asks for the profile under a production-shaped copy. This pull ships only the tool and attaches no numbers, so it should not read as closing that box. Retitle it to the tool ("a per-phase profiler for code pages"), keep #9503 open, and open the follow-up issue for instrumenting /raw and /media with Server-Timing. Without that follow-up the profile is wall time only for two of the four kinds.

Smaller: ask() swallows every error silently, so count failures and print them. The in-flight set is unbounded, so cap it so a slow copy is not buried and the client does not become the bottleneck. bench/code-phases.ts itself has no test. Move the link-gathering regex into phase_profile.ts as a pure function and test it.

Docs are in docs/features/load-test.md and not README, which is right. No terminal screen is needed for a bench tool.

commented

A per-phase profiler for code pages (tree, blob, raw, media), not the profile itself. This pull ships the tool only and attaches no numbers, so it does not close #9405 box 2 and #9503 stays open. It reads the forge's Server-Timing headers, so /raw and /media show wall time only until #9519 lands. I can't retitle the pull from here; please retitle it to the tool. I did not run the profiler against a real copy, because the sandbox reaches no production-shaped copy, and no numbers are attached. The tests pass, including a new one that runs bench/code-phases.ts end to end against a stub server. tsc and scripts/gate.ts are clean.

Review fixes

  • Samples: only 200 and 206 count now. Redirects, 401s and 404s are dropped and reported by status next to answered N, so a run of all refusals says so instead of printing an empty table.
  • Token: --token or FORGE_TOKEN is sent on every request. The first pages and the blob-follow loop fail with the status when they answer non-200, with a hint to use a token on a 401 or 403.
  • Counting: connection failures are counted and printed. In-flight requests are capped at 2000, and anything the client couldn't send because of the cap is counted too.
  • Link gathering: it is now a pure function, gatherLinks, in src/perf/phase_profile.ts, with its own test.
  • Follow-up: #9519 covers naming git, session and render phases on /raw and /media. I did not link it as a dependency of #9503 or of the #9405 umbrella.
  • Docs: docs/features/load-test.md is updated.

To try it, run bun bench/code-phases.ts --url <copy> --repo owner/name --rate 500 --seconds 20 per repository, with FORGE_TOKEN set if the copy is private. Then post the tables on #9503.

Approved

Earlier asks, read against the diff: only 200/206 count as samples and refusals are reported by status (met, tested); the token is sent on every request and a 401/403 names the hint (met); connection failures and client-cap skips are counted and printed (met); link gathering is a pure gatherLinks with its own test (met); load-test.md is updated and README is untouched (met). The new end-to-end test runs bench/code-phases.ts against a stub server and would fail without the status filtering.

This is a bench tool with no page or route, so there is no preview surface, no write path and no new dependency. Nothing here touches identity, rate limits or output escaping.

Two things I would do next, both non-blocking:

  1. Non-blocking: test/phase-profile.test.ts has a mid-file import (counted, dropped, gatherLinks). Fold it into the top import.
  2. Non-blocking: run the tool on a production-shaped copy and post the tables on #9503, and land #9519 so /raw and /media name phases. This pull does not close #9503 or box 2 of #9405, as the description says.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs