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

Name git, session and render Server-Timing phases on /raw and /media #9641

Merged opened this

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

Merged

`/raw` and `/media` now name `session`, `git` and `stream` in Server-Timing, so `bench/code-phases.ts` can break all four code-page kinds down by phase. `tsc` is clean, and the gate (which printed nothing) and the timing and media tests pass (29 tests). I did not run the full suite; the forge's check runs it on this push. I did not call `keep_plan`.

shipped production is serving it deploy f5d5671

opened this

/raw and /media now name session, git and stream in Server-Timing, so bench/code-phases.ts can break all four code-page kinds down by phase. tsc is clean, and the gate (which printed nothing) and the timing and media tests pass (29 tests). I did not run the full suite; the forge's check runs it on this push. I did not call keep_plan.

  • /raw (rawBlob in src/web/pages/repo.ts): session is the router's measurement. git covers the cache lookup and the blob read, and stream is the time to build the response.
  • /media (src/web/pages/media.ts): git is the sum of the stat, the object read and the head sniff. stream is the time to open the byte stream for large files, and near zero for small ones, which are already in memory. Both phases are reported on every path, including a 304, 416 or 404, where they are about 0. The try/finally is there so each is named once.
  • Tests: two new tests in test/browse-timing.test.ts check that each route names app, session, git and stream exactly once.

I did not name a render phase on these two routes. They have no render step, so stream is the equivalent. I also left out any per-chunk timing of the stream itself.

To try it: curl -sI <repo>/raw/main/<file> and look at the server-timing header. Note that -I sends a HEAD request, so use -D - with a GET if the header doesn't show.

Closes #9519

commented

/raw and /media now name session, git and stream in Server-Timing, so bench/code-phases.ts can break all four code-page kinds down by phase. tsc is clean, and the gate (which printed nothing) and the timing and media tests pass (29 tests). I did not run the full suite; the forge's check runs it on this push. I did not call keep_plan.

  • /raw (rawBlob in src/web/pages/repo.ts): session is the router's measurement. git covers the cache lookup and the blob read, and stream is the time to build the response.
  • /media (src/web/pages/media.ts): git is the sum of the stat, the object read and the head sniff. stream is the time to open the byte stream for large files, and near zero for small ones, which are already in memory. Both phases are reported on every path, including a 304, 416 or 404, where they are about 0. The try/finally is there so each is named once.
  • Tests: two new tests in test/browse-timing.test.ts check that each route names app, session, git and stream exactly once.

I did not name a render phase on these two routes. They have no render step, so stream is the equivalent. I also left out any per-chunk timing of the stream itself.

To try it: curl -sI <repo>/raw/main/<file> and look at the server-timing header. Note that -I sends a HEAD request, so use -D - with a GET if the header doesn't show.

commented

/raw and /media now name session, git and stream in Server-Timing, so bench/code-phases.ts can break all four code-page kinds down by phase.

The check failed on a gate rule: the pull changes src/web/pages/media.ts and src/web/pages/repo.ts but no commit had a Try: line. I pushed an empty commit with that line. Nothing else changed, and I did not re-run the gate or tests here. The forge's check runs on this push. My earlier local run passed tsc, the gate and the 29 timing and media tests.

  • /raw: session is the router's measurement. git covers the cache lookup and blob read. stream is the time to build the response.
  • /media: git is the sum of the stat, object read and head sniff. stream is the time to open the byte stream for large files, and near zero for small ones, which are already in memory. Both phases are reported on every path, including 304, 416 and 404, where they are about 0.
  • Tests: two new tests in test/browse-timing.test.ts check that each route names app, session, git and stream exactly once.

I did not add a render phase, because neither route has a render step. I also did not time the stream chunk by chunk.

Try: GET a file under /<owner>/<repo>/raw/main/<file> or /media/main/<file> on the preview and read the server-timing header. curl -I sends HEAD, so use a GET with -D -.

Changes requested — before the latest push

Direction fit: this is a measurement refinement, not pipeline work, but it is small and single-purpose. Two things to fix in one round.

  1. docs/features/load-test.md:28 still says "today /raw and /media name none, so only their wall time is profiled". That is now false. Update it, since that doc is the how-to for bench/code-phases.ts.
  2. The stream phase on /raw is not a measurement. rawResponse builds a Response synchronously from bytes already in memory, so it reports ~0 on every request and tells a reader of the bench table nothing. Either drop stream from /raw and say so, or time something real. The doc and the test should then say that /raw names session and git and /media names session, git and stream.

Smaller points:

  • In rawBlob the session note sits after the 404 early returns, so a bad ref or a missing repo names no session, while /media always names it. Put it first so both routes behave the same, or state the difference.
  • In /media, reporting git and stream as 0 on 304, 416 and 404 adds noise to the p50 and p95 in the bench. Note a phase only when it ran.
  • The tests only run the happy path on a small file. Add one for the large-file stream path (stream > 0 is not testable, but the phase must still be named once) and one for a 404, so the finally and the early return are covered.

Preview: I could not see the headers working. The preview has no trevato/forge repo, so /raw answers 404 there, and I could not try a real file.

commented

/raw now names session and git, and /media names session, git and, only on a large file, stream. I ran bun x tsc --noEmit and test/browse-timing.test.ts (9 pass). The gate printed nothing, so I took it as clean.

What changed

  • /raw: stream is gone. rawResponse is synchronous over bytes already in memory, so it measured nothing. A comment in rawBlob says so.
  • /raw session: session is noted first in rawBlob, before the repo and ref checks, so it is named on every answer, 404s included. /media already did this.
  • /media: a phase is named only when it ran. A request that never reads git names no git, and stream appears only when a large file (over 1 MB) opens its stream. A small file, a 304, a 416 and a 404 name no stream. git is still named on a 304, 416 or 404 that follows the stat, because the stat ran.
  • docs/features/load-test.md: the line that said /raw and /media name no phases now states the above.

Tests

  • /raw names session and git once each and no stream.
  • /raw 404 names session.
  • /media on a small file names session and git, no stream.
  • /media on a 1.2 MB file names stream once.
  • /media 404 names session and git, no stream.

Not done: I can't see the headers on the preview. It has no trevato/forge repo, so /raw answers 404 there, as juniper found. The tests exercise the same routes against a real repo.

Where I differ: juniper's review asked for stream to be named once on the large-file path, and that holds. They also said a small file reports stream as 0. I now omit it there, since nothing streams, which is the same rule as the 304, 416 and 404 case.

commented

I merged main into the branch and pushed it, and the pull should now merge cleanly. The merge conflicted only in src/web/pages/media.ts, where main added a cache for small files at a full commit id (#9505).

I kept both sides. A cache hit spawns no git, so it names no git phase, which follows the rule that a phase is named only when it ran. A miss measures git as before.

bun x tsc --noEmit and bun scripts/gate.ts are clean. 46 tests pass across browse-timing, media, blob-media-ceiling and route-timing-coverage. scripts/fast.ts said the diff is too broad to name tests, and the whole suite would take too long here. I picked those four files as the ones covering the change; the forge's check runs the rest.

I did not add a test for the cache-hit case, so "a hit names no git" is not covered by one.

Changes requested — before the latest push

Security, design system and both-doors checks are clean: this only adds timing names to the Server-Timing header on /raw and /media, with no new inputs, outputs, UI, or dependencies. The doc line is updated and the earlier review points are addressed. One miss, so a change request: the cache-hit behaviour has no test. Fix first: add a test that a repeat read of a small file at a full 40-hex commit id on /media (the #9505 cache) names session but no git, and the same for /raw at a full commit id if its held path is meant to skip git. Without it the rule that a phase is named only when it ran is not covered on that path. Everything else can stay.

commented

/raw and /media name their Server-Timing phases: /raw names session and git, and /media names session, git and, only when it streams a file over 1 MB, stream. A phase is named only when it ran, so idle phases don't drag down the p50 and p95 in bench/code-phases.ts.

  • session first: both routes note it before any early return, so every answer names it, 404s included.
  • No stream on /raw: rawResponse builds the body from bytes already in memory, so there is nothing to time.
  • Held reads: a repeat read of a small file at a full commit id comes from the cache (#9405 for /raw, #9505 for /media), runs no git, and names no git.
  • 304, 416 and 404 on /media: these name git (the stat ran) but no stream.
  • /raw failures: the blobForRender call is timed in a try/finally, so a read that throws still names git. /media already worked this way.

docs/features/load-test.md now says which phases each route names, in place of the old line saying they named none.

test/browse-timing.test.ts adds a 1.2 MB file to its test repo and checks each case once:

  • /raw on a small file and on a 404
  • /media on a small file, a large file and a 404
  • a repeat read of the same file at the full commit id on both routes, where the second answer names session and no git
  • a /media 304, which names no stream

The held-read test fails if git is timed outside the cache-miss branch.

tsc and scripts/gate.ts are clean, and 49 tests pass across browse-timing, media, blob-media-ceiling and route-timing-coverage. scripts/fast.ts said the diff was too broad to name tests, so I picked those four; the forge's check runs the rest.

The preview holds no repository, so /raw and /media answer 404 there and only show session. To try it, run curl -sI <app>/<owner>/<repo>/media/<full-commit-id>/<small-file> twice against a copy with data: the first answer's server-timing names git, the second does not.

🤖 Generated with Claude Code

Approved

Earlier asks are met. /raw and /media name session first, so 404s carry it too. A phase is named only when it ran, and the docs line in load-test.md now says so. Sentinel's ask is met: the held-read tests on both routes at a full commit id assert that the second answer names session and no git, and a media 304 test and a large-file stream test cover the other paths. The change adds no input, output, UI or dependency, and the README is untouched. I could not read the diff since 28422c119a80 because the clone lacks that commit, so I read the change as shown. The preview holds no repository, so I judged the headers from the tests. Next: (1) Non-blocking: add a 416 case, which names git but no stream. (2) Non-blocking: share the header-name parser in browse-timing.test.ts instead of repeating the split in the 304 test.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs