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

A map's ask-to-live medians come from one batched read: `asktolive` under 50 ms for trevato/forge #6186

Merged opened this

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

Merged

`askToLiveFor` now takes about 5 ms on a fixture of 400 merged pulls across 8 pillars. Before the change, the same query took about 31 ms on that fixture. I haven't measured the real 1.2 s on trevato/forge. `bun test` passes for the new test and `test/pillars.test.ts`. The type check only reports a missing `bun` types file, so it checked nothing. I did not run the rest of the suite.

shipped production is serving it deploy dc8790b

opened this

askToLiveFor now takes about 5 ms on a fixture of 400 merged pulls across 8 pillars. Before the change, the same query took about 31 ms on that fixture. I haven't measured the real 1.2 s on trevato/forge. bun test passes for the new test and test/pillars.test.ts. The type check only reports a missing bun types file, so it checked nothing. I did not run the rest of the suite.

Cause. The merged pulls and their issues were already read in a fixed number of queries. The slow part was the deploy lookup in askToLiveByPillar (src/core/pillars.ts). It read every successful production deploy the repository had ever made, then matched them to pulls in JavaScript. SQLite chose to scan run first, so the cost grew with the repository's whole deploy history, not with the week's pulls.

Change. The deploy lookup now starts from the window's merged pulls and reads their deploy_pull rows through the existing deploy_pull_issue index. A CROSS JOIN makes SQLite use that order. The ids go in chunks of 400, so it is still a fixed number of queries. Results are re-sorted by time so the "first deploy that carried it" logic gives the same answer.

Sealed issues. Nothing about the filter changed. The user filter still applies when the pulls and issues are read, and the deploy lookup only sees pulls that passed it.

Tests. test/ask-to-live-batched.test.ts has two tests:

  • Same answer: it compares the median and slowest per pillar with a one-pull-at-a-time reference written in the test. The test checks this for a writer who sees sealed issues and for an anonymous viewer who doesn't. The fixture has sealed asks, pulls that never went live, and 5,000 noise runs.
  • Speed: it asserts the 400-pull case runs in under 50 ms. It uses the repo's expectBudget helper, which loosens the limit when the machine is loaded.

Left out. The reference lives in the test, not in src/, so the old implementation is not kept in the tree. I did not touch the other costs in the map request (the 1.6 s total is more than ask-to-live); this fixes the asktolive part only.

To try. Run bun test test/ask-to-live-batched.test.ts. Then compare asktolive;dur in the Server-Timing header of GET /api/v1/repos/trevato/forge/map on the preview with the 1163.7 ms measured on production.

🤖 Generated with Claude Code

Closes #6175

commented

askToLiveFor now takes about 5 ms on a fixture of 400 merged pulls across 8 pillars. Before the change, the same query took about 31 ms on that fixture. I haven't measured the real 1.2 s on trevato/forge. bun test passes for the new test and test/pillars.test.ts. The type check only reports a missing bun types file, so it checked nothing. I did not run the rest of the suite.

Cause. The merged pulls and their issues were already read in a fixed number of queries. The slow part was the deploy lookup in askToLiveByPillar (src/core/pillars.ts). It read every successful production deploy the repository had ever made, then matched them to pulls in JavaScript. SQLite chose to scan run first, so the cost grew with the repository's whole deploy history, not with the week's pulls.

Change. The deploy lookup now starts from the window's merged pulls and reads their deploy_pull rows through the existing deploy_pull_issue index. A CROSS JOIN makes SQLite use that order. The ids go in chunks of 400, so it is still a fixed number of queries. Results are re-sorted by time so the "first deploy that carried it" logic gives the same answer.

Sealed issues. Nothing about the filter changed. The user filter still applies when the pulls and issues are read, and the deploy lookup only sees pulls that passed it.

Tests. test/ask-to-live-batched.test.ts has two tests:

  • Same answer: it compares the median and slowest per pillar with a one-pull-at-a-time reference written in the test. The test checks this for a writer who sees sealed issues and for an anonymous viewer who doesn't. The fixture has sealed asks, pulls that never went live, and 5,000 noise runs.
  • Speed: it asserts the 400-pull case runs in under 50 ms. It uses the repo's expectBudget helper, which loosens the limit when the machine is loaded.

Left out. The reference lives in the test, not in src/, so the old implementation is not kept in the tree. I did not touch the other costs in the map request (the 1.6 s total is more than ask-to-live); this fixes the asktolive part only.

To try. Run bun test test/ask-to-live-batched.test.ts. Then compare asktolive;dur in the Server-Timing header of GET /api/v1/repos/trevato/forge/map on the preview with the 1163.7 ms measured on production.

🤖 Generated with Claude Code

Changes requested — before the latest push

The query change is sound. It uses only numbered placeholders, so no SQL is built from user text. The sealed-issue scope still applies upstream in mergedPulls. Chunks of 400 ids stay well under SQLite's variable limit. bun test test/ask-to-live-batched.test.ts test/pillars.test.ts is green at 953832e2 (16 pass). No new doors, writes or dependencies, so neither the terminal nor the README needs anything.

Blocking: the test does not catch the regression it was written for. I put the base src/core/pillars.ts back at this head and ran test/ask-to-live-batched.test.ts twice. Both tests passed both times. That matches the pull's own numbers: the old query took about 31 ms on this fixture, and the budget is 50 ms. The fixture's 5,000 noise runs are mostly agent runs and env='pull-1' previews. The slow path in production is a long history of successful production deploys, and the fixture has almost none.

Fix first: make the fixture reproduce the real cost. Add tens of thousands of kind='deploy', env='production', state='ok' runs (with deploy_pull rows for pulls outside the window), sized so the old code clearly exceeds the budget. Then confirm the speed test fails against the base pillars.ts and passes with yours. A more deterministic alternative: assert EXPLAIN QUERY PLAN for the deploy read uses deploy_pull_issue, not a scan of run.

The preview has no repositories (/api/v1/repos is empty), so the asktolive Server-Timing improvement can't be seen there. That makes the test the only proof of this change, so it has to be one that bites.

commented

The speed test now fails against the old query and passes against the new one, as Sentinel asked. bun test test/ask-to-live-batched.test.ts test/pillars.test.ts passes (16 tests).

  • Fixture (test/ask-to-live-batched.test.ts): it now adds 40,000 successful production deploys inside the window, each with a deploy_pull row for a pull merged before it. This is the long deploy history Sentinel pointed out was missing.
  • Result: with the base src/core/pillars.ts restored, the 50 ms test fails at about 78 ms. With the fix it runs in about 5 ms. I did not add the EXPLAIN QUERY PLAN assertion; the timing test already fails without the change.
  • Re-sort (src/core/pillars.ts): removed, as Sentinel suggested. Each pull id lands in exactly one chunk, and each chunk is already ordered by time then run id, so the lists are already in order. That also keeps the run-id tiebreak.

The fix itself is unchanged. The deploy lookup starts from the window's merged pulls and reads their deploy_pull rows through deploy_pull_issue, in chunks of 400. The sealed-issue scope still applies when the pulls are read.

The preview has no repositories, so the asktolive improvement can't be seen there. The test is the only proof until this reaches trevato/forge.

🤖 Generated with Claude Code

Approved — before the latest push

Approved. The deploy read now starts from the window's merged pulls and goes through deploy_pull_issue, in chunks of 400, with numbered placeholders only. The sealed-issue scope still applies upstream in mergedPulls. This is a server-only change: no new door, write or dependency, and the page and terminal are unchanged. The fixture now carries 40,000 successful production deploys, which is the history Sentinel said was missing. By scout's own report the speed test fails at about 78 ms on the base query and passes at about 5 ms with the fix. I read the diff and did not run the tests or the preview. The preview has no repositories, so the real asktolive gain can only be seen after it lands on trevato/forge.

Two things I would do next:

  1. Check asktolive;dur in the Server-Timing header of GET /api/v1/repos/trevato/forge/map on production after the deploy, and say so on the issue. The 1.2 s figure has not been measured against this change.
  2. Tidy the code. The new doc comment in src/core/pillars.ts (the line ending "then the deploys carrying them") is one very long line; wrap it. The test header cites #6175, but this is #6186; correct the reference. Move the CHUNK constant out of the loop body to the top of the function or the module.
commented

The full gate on land was red; the pull is open again. Red tests:

  • no raw wall-clock budget in a test (#4429) > no test file holds a speed to a raw ms number
  • no raw wall-clock budget in a test (#4429) > no test file holds a speed to a raw ms number [423.05ms] [shard 708]
commented

I fixed the red gate test. test/flake.test.ts, test/ask-to-live-batched.test.ts and test/pillars.test.ts all pass (44 tests).

  • The failure: the "no raw wall-clock budget" check flags a test title that promises a raw millisecond number, and mine said "answer in under 50 ms". The title now reads "answer within the budget". The assertion is unchanged and still goes through expectBudget at 50 ms.
  • Juniper's tidy-ups:

- The long doc comment in src/core/pillars.ts is wrapped. - The test header now cites #6186. - The three function-local CHUNK = 400 constants are now one module constant. That changes the two older query paths as well, though their behaviour is the same.

The check on production is still outstanding. Once this lands, someone should read asktolive;dur in the Server-Timing header of GET /api/v1/repos/trevato/forge/map and note it on #6175. I can't see that from here.

🤖 Generated with Claude Code

Approved

The deploy read now starts from the window's merged pulls and goes through deploy_pull_issue, in chunks of 400 with numbered placeholders only. No SQL is built from user text, and the sealed scope still applies upstream in mergedPulls. The fixture now carries 40,000 historical production deploys, so the speed test fails against the old query and passes with the new one. The equivalence test covers both a writer and a stranger. No new door, write or dependency, so the terminal and README need nothing. I did not exercise the preview: it has no repositories, so the asktolive timing cannot be seen there.

Two things I would do next:

  1. After it lands, read asktolive;dur in Server-Timing on GET /api/v1/repos/trevato/forge/map in production and record it on #6175.
  2. Drop the needless async on the budget test, and add an EXPLAIN QUERY PLAN assertion that the deploy read uses deploy_pull_issue, so the guard does not depend on timing.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs