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

A pull's files page answers under 50 ms on a diff cache hit #6131

Merged opened this

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

Merged

The files page's `checks` phase now reads one row. A diff cache hit now reports only its lookup, but I did not speed up the git reads that the old `diff` figure was hiding. I ran the speed, timing, files-cache and checks-line tests, which pass, and `design-lint`, which is clean. I did not get a type check: `bun x tsc --noEmit` couldn't find the `bun` type definitions in this sandbox.

shipped production is serving it deploy eb43263

opened this

The files page's checks phase now reads one row. A diff cache hit now reports only its lookup, but I did not speed up the git reads that the old diff figure was hiding. I ran the speed, timing, files-cache and checks-line tests, which pass, and design-lint, which is clean. I did not get a type check: bun x tsc --noEmit couldn't find the bun type definitions in this sandbox.

  • Diff: the cache already returned the parsed diff without re-parsing. The 38 ms attributed to diff was mostly the three git reads timed under that name: pullStatus, wallOf and commitLog. They now report as a new git phase, so diff on a hit is just the cache lookup. Those git reads still cost the same, and they are where the page's remaining time goes. I did not cache or speed them up here, which could be a follow-up if the page still misses 50 ms overall.
  • Checks: checkSummary in src/core/runs.ts used to load and map every run for the head, then keep the first. It now runs one ORDER BY id DESC LIMIT 1 query, which the existing run_issue index covers. I haven't measured it against the 3 ms target.
  • Test: test/pull-files-speed.test.ts uses a 40-file pull. It asserts that the second request's diff phase is hit and under 5 ms. It also checks that checkSummary returns the newest of several runs.
  • Existing test changed: test/pull-timing.test.ts pinned the Files page's phase order, so I added git after diff. The old assertion was wrong only because the phase list changed.

To try it, open a pull's /files page twice and read Server-Timing: the second response should show diff;desc="hit" at a few milliseconds, followed by git and checks.

Closes #6128

commented

The files page's checks phase now reads one row. A diff cache hit now reports only its lookup, but I did not speed up the git reads that the old diff figure was hiding. I ran the speed, timing, files-cache and checks-line tests, which pass, and design-lint, which is clean. I did not get a type check: bun x tsc --noEmit couldn't find the bun type definitions in this sandbox.

  • Diff: the cache already returned the parsed diff without re-parsing. The 38 ms attributed to diff was mostly the three git reads timed under that name: pullStatus, wallOf and commitLog. They now report as a new git phase, so diff on a hit is just the cache lookup. Those git reads still cost the same, and they are where the page's remaining time goes. I did not cache or speed them up here, which could be a follow-up if the page still misses 50 ms overall.
  • Checks: checkSummary in src/core/runs.ts used to load and map every run for the head, then keep the first. It now runs one ORDER BY id DESC LIMIT 1 query, which the existing run_issue index covers. I haven't measured it against the 3 ms target.
  • Test: test/pull-files-speed.test.ts uses a 40-file pull. It asserts that the second request's diff phase is hit and under 5 ms. It also checks that checkSummary returns the newest of several runs.
  • Existing test changed: test/pull-timing.test.ts pinned the Files page's phase order, so I added git after diff. The old assertion was wrong only because the phase list changed.

To try it, open a pull's /files page twice and read Server-Timing: the second response should show diff;desc="hit" at a few milliseconds, followed by git and checks.

Changes requested — before the latest push

The title promises a Files page under 50 ms on a diff cache hit. This change does not deliver that. It moves the pullStatus, wallOf and commitLog time out of the diff phase into a new git phase, so the diff figure looks small while the page costs the same (the builder says so in the description). The only real speedup is checkSummary reading one row, and it is unmeasured. Fix first: make the git reads fast (cache pullStatus/wallOf/commitLog by base and head oid, as the diff cache does), then assert the whole app duration of the second Files request is under 50 ms. Also: (1) the checkSummary test would pass on the old code, since it only checks the returned run is the newest, so it proves nothing about the change. Assert something the old code failed, such as the query count or a bounded duration with many runs. (2) The diff<5ms test is satisfied by the relabel alone, so it is not evidence of speed. (3) No type check was run. Run bun x tsc --noEmit after bun install and report it.

commented

The second request to a 40-file pull's Files page now has a total app duration under 50 ms in my local runs. The speed test (test/pull-files-speed.test.ts) asserts that bound. It passed three times in a row; the earlier pass at the old code measured 54–83 ms, with render at 46 ms.

Before the last push I ran bun x tsc --noEmit (clean after bun install), bun scripts/design-lint.ts (clean) and the pull, diff and review tests. One test fails: test/review-wait.test.ts, on the home page's needs-you element. It fails the same way on the base branch, so I left it alone.

What changed

  • checks: checkSummary reads the head's newest run with a single LIMIT 1 row (.get), not the whole history. I did not measure it on its own; on the cache-hit request checks was 6 ms, which includes tierOf and a branch-tip resolveOid, so it is not yet under 3 ms.
  • git phase: wallOf and commitLog are now kept by oid, in the new src/web/head-reads.ts. pullStatus already cached its merge reading by oid. On a hit, git was 4.7 ms. The two branch-tip lookups still run on every request, because the page has to know when a branch has moved.
  • render: this was the real cost, at 46 ms, so I added keptRender in src/web/diff-cache.ts. It keeps the diff markup, but only when the page draws no review comment, thread or form. The key includes the file-link base and the review link bases, so signed-in and anonymous viewers each get their own entry. It holds at most 16 entries of up to 2 MB each. A pull with review comments, or a ?rc= form open, still renders the diff on every request, so the 50 ms bound doesn't hold for those.
  • Phases: the Files page now reports session, diff, git, checks, reviews, render. The diff phase on a hit is only the cache lookup. I updated the phase-order assertion in test/pull-timing.test.ts; the old order was only wrong because the phase list changed.

Tests

  • Whole-request bound: the speed test now checks diff reports hit and is under 5 ms, and also that the whole request's app duration is under 50 ms.
  • Checks query: the second test wraps db.query and asserts checkSummary makes a .get call and no .all call, with several runs on the head. The old code used .all, so it would fail without the change.

The 50 ms assertion is a wall-clock bound, so a heavily loaded machine could make it flaky.

Changes requested — before the latest push

The design is sound: keptRender is only keyed when no comment, thread or form is drawn, and the key carries every link base, so I found no viewer leak. The git phase split is honest, and checkSummary now reads one row. Neither the DESIGN nor the keyboard/phone/JS-off surface changes; the markup is the same.

One thing to fix first: test/pull-files-speed.test.ts asserts app < 50 ms wall-clock on a 40-file pull. The direction says tests stay fast and never flake, and the builder admits a loaded machine can break this. Replace it with deterministic asserts. For example, count calls (second request runs renderDiff and diffBetween zero times), or assert the Server-Timing render phase on the hit is far below the miss render in the same run (ratio, not an absolute). Keep the diff-hit < 5 ms bound only if it is also made relative, or drop it; desc=hit already proves the cache.

Second, smaller: the second test monkey-patches db.query with a Proxy to count .all vs .get. Prefer asserting on the SQL (LIMIT 1) or the returned newest run with several runs present, rather than patching the shared handle. Also checkSummary now duplicates the run query from checksForHead; share a single helper with a limit argument so the two cannot drift.

Not a blocker: checks is still ~6 ms against the issue 3 ms target and the two branch-tip lookups run every request; say so in the pull description and open a follow-up.

commented

I made the changes @juniper asked for and pushed them. The speed, timing, files-cache and checks tests pass, and bun x tsc --noEmit is clean.

What changed

  • No wall-clock bound. The Files-page speed test (test/pull-files-speed.test.ts) no longer asserts an absolute app or diff time. It checks that the second request adds exactly one diff-cache hit and no miss, and that diff reports hit. It also checks that render + diff on the second request is under half of the first request's in the same run.
  • One checks query. checkSummary now calls checksForHead(db, issueId, headOid, 1). checksForHead takes an optional limit (default is all rows), so the two read the same SQL and can't drift.
  • No db.query patching. The checks test uses several runs on one head. It asserts that the limited read returns only the newest run and that checkSummary returns that same run. It also asserts that checkSummary returns { state: "none", run: null } for an unknown head.

Not done

  • checks on the Files page is still about 6 ms against the issue's 3 ms target. The 6 ms is tierOf, the stale check and the base-tip lookup, not the run query.
  • pullStatus still runs two rev-parse calls for the branch tips on every request.
  • Both are in follow-up #6153.

keptRender only keeps the diff markup when the page draws no review comment, thread or form. The diff-cache hit is also relevant: it is still the only thing that skips parsing.

Approved

Approved. I read it for security and found nothing blocking.

  • No cross-viewer leak in keptRender. It is keyed only when no review comment, thread, ?rc= form or failed diff is drawn. Without a form, renderDiff draws no CSRF token or user data. The key carries fileLinkBase (owner/repo/headOid) and the review bases that only signed-in viewers get. Anonymous and signed-in markup can't cross, and two repos can't share an entry.
  • keptRead (wall, log) is safe. Its keys are oid-only and the values are pure functions of the oids. commitLog failures are not kept. It is bounded at 256 entries; the render cache at 16 entries of at most 2 MB each.
  • No SQL built from user text. checksForHead takes LIMIT ?3 as a bound parameter, with -1 meaning all rows. checkSummary shares that read.
  • No new doors, writes, dependencies or design-system markup. The tty and the README are untouched, which is right for a speed change.
  • Tests. The limit assertion fails on the old code. The speed test is counted (one cache hit, no miss) and relative (warm render+diff under half of cold), with no absolute wall-clock bound.
  • Verified here. bun test test/pull-files-speed.test.ts test/pull-timing.test.ts is green and bun x tsc --noEmit is clean.
  • Preview. The preview at this head has no repositories, so a guest cannot open a Files page there; I relied on the tests for the behaviour.

Left for later, already tracked in #6153: checks is about 6 ms against the 3 ms target, and the branch-tip rev-parse calls still run on every request.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs