Public: anyone can read this; the crew can change it.
small: the page-load budget lives in `src/perf/page_budget.ts` as plain data #11366
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/11360-small-the-page-load-budget-lives-in-src → main 3 commits fork 2 threads · 2 unresolved
5 files
changed +55
−31
scripts/unreached-allow.json +1
| @@ -10,6 +10,7 @@ | ||
| 10 | 10 | "src/flow/review_rounds.ts", |
| 11 | 11 | "src/flow/shared_red.ts", |
| 12 | 12 | "src/flow/stale_fold.ts", |
| 13 | "src/perf/page_budget.ts", | |
| 13 | 14 | "src/spawn/pod.ts", |
| 14 | 15 | "src/tui/gallery.ts", |
| 15 | 16 | "src/tui/ui/qr.ts", |
src/perf/page_budget.ts added +37
| @@ -0,0 +1,37 @@ | ||
| 1 | /** | |
| 2 | * The page-load budget (#11320, #11360): the pages people open first, each | |
| 3 | * with the median it answers in warm and the SQL statements one request of | |
| 4 | * it may run. Plain data, one line per page: add a page in one line here. | |
| 5 | * `test/page-budget.test.ts` walks the table in-process, and a production | |
| 6 | * probe can read the same table. | |
| 7 | * | |
| 8 | * The statement count is the hard limit: it is the same on a loaded machine | |
| 9 | * as on an idle one, so an N+1 fails it every run. The median is the promise | |
| 10 | * people feel; a loaded check host can miss it, so the test measures it again | |
| 11 | * before it judges. | |
| 12 | */ | |
| 13 | ||
| 14 | export interface PageBudget { | |
| 15 | /** What the page is, in words, for the failure line. */ | |
| 16 | readonly page: string; | |
| 17 | /** The router pattern; `:owner` and `:name` are filled by `pagePath`. */ | |
| 18 | readonly pattern: string; | |
| 19 | /** The median of 20 warm requests stays under this many ms. */ | |
| 20 | readonly medianMs: number; | |
| 21 | /** One warm request runs at most this many SQL statements. */ | |
| 22 | readonly statements: number; | |
| 23 | } | |
| 24 | ||
| 25 | /** Statement limits are today's count, signed in, plus a little: lower them as pages get cheaper, raise one only on purpose. */ | |
| 26 | export const PAGE_BUDGETS: readonly PageBudget[] = [ | |
| 27 | { page: "home", pattern: "/", medianMs: 50, statements: 46 }, | |
| 28 | { page: "explore", pattern: "/explore", medianMs: 50, statements: 18 }, | |
| 29 | { page: "map", pattern: "/map", medianMs: 50, statements: 21 }, | |
| 30 | { page: "an app page", pattern: "/:owner/:name", medianMs: 50, statements: 49 }, | |
| 31 | { page: "changelog", pattern: "/changelog", medianMs: 50, statements: 20 }, | |
| 32 | ]; | |
| 33 | ||
| 34 | /** The path a budget's pattern names for one repository: `:owner` and `:name` filled, everything else as written. */ | |
| 35 | export function pagePath(b: PageBudget, owner: string, name: string): string { | |
| 36 | return b.pattern.replace(":owner", encodeURIComponent(owner)).replace(":name", encodeURIComponent(name)); | |
| 37 | } |
test/helpers/page_budget.ts +5 −28
| @@ -1,34 +1,11 @@ | ||
| 1 | 1 | /** |
| 2 | * The page-load budget (#11320): the pages people open first, each with the | |
| 3 | * median it answers in warm and the SQL statements one request of it may run. | |
| 4 | * Plain data, one line per page; `test/page-budget.test.ts` walks the table | |
| 5 | * in-process. It lives beside the test because only the test reads it. | |
| 6 | * | |
| 7 | * The statement count is the hard limit: it is the same on a loaded machine | |
| 8 | * as on an idle one, so an N+1 fails it every run. The median is the promise | |
| 9 | * people feel; a loaded check host can miss it, so the test measures it again | |
| 10 | * before it judges. | |
| 2 | * The page-load budget's judging (#11320): the median, what one page | |
| 3 | * measured, and the finding lines. The table itself is plain data in | |
| 4 | * `src/perf/page_budget.ts` (#11360), re-exported here for the test. | |
| 11 | 5 | */ |
| 6 | import type { PageBudget } from "../../src/perf/page_budget.ts"; | |
| 12 | 7 | |
| 13 | export interface PageBudget { | |
| 14 | /** What the page is, in words, for the failure line. */ | |
| 15 | readonly page: string; | |
| 16 | /** The router pattern; `:owner` and `:name` are filled by the test. */ | |
| 17 | readonly pattern: string; | |
| 18 | /** The median of 20 warm requests stays under this many ms. */ | |
| 19 | readonly medianMs: number; | |
| 20 | /** One warm request runs at most this many SQL statements. */ | |
| 21 | readonly statements: number; | |
| 22 | } | |
| 23 | ||
| 24 | /** Statement limits are today's count, signed in, plus a little: lower them as pages get cheaper, raise one only on purpose. */ | |
| 25 | export const PAGE_BUDGETS: readonly PageBudget[] = [ | |
| 26 | { page: "home", pattern: "/", medianMs: 50, statements: 46 }, | |
| 27 | { page: "explore", pattern: "/explore", medianMs: 50, statements: 18 }, | |
| 28 | { page: "map", pattern: "/map", medianMs: 50, statements: 21 }, | |
| 29 | { page: "an app page", pattern: "/:owner/:name", medianMs: 50, statements: 49 }, | |
| 30 | { page: "changelog", pattern: "/changelog", medianMs: 50, statements: 20 }, | |
| 31 | ]; | |
| 8 | export { PAGE_BUDGETS, pagePath, type PageBudget } from "../../src/perf/page_budget.ts"; | |
| 32 | 9 | |
| 33 | 10 | /** The median of `xs` (the mean of the middle two for an even count); NaN for none. */ |
| 34 | 11 | export function median(xs: readonly number[]): number { |
test/page-budget-path.test.ts added +9
| @@ -0,0 +1,9 @@ | ||
| 1 | import { expect, test } from "bun:test"; | |
| 2 | import { PAGE_BUDGETS, pagePath } from "../src/perf/page_budget.ts"; | |
| 3 | ||
| 4 | test("pagePath fills :owner and :name and leaves fixed paths alone", () => { | |
| 5 | const app = PAGE_BUDGETS.find((b) => b.pattern === "/:owner/:name")!; | |
| 6 | expect(pagePath(app, "alice", "notes")).toBe("/alice/notes"); | |
| 7 | expect(pagePath(app, "a b", "x/y")).toBe("/a%20b/x%2Fy"); | |
| 8 | expect(pagePath(PAGE_BUDGETS[0]!, "alice", "notes")).toBe("/"); | |
| 9 | }); |
test/page-budget.test.ts +3 −3
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | /** |
| 2 | 2 | * The page-load budget (#11320): the five pages people open first answer |
| 3 | 3 | * under their median warm, on a seeded forge booted in-process and walked |
| 4 | * signed in, and run no more SQL statements than `test/helpers/page_budget.ts` | |
| 4 | * signed in, and run no more SQL statements than `src/perf/page_budget.ts` | |
| 5 | 5 | * allows. |
| 6 | 6 | * |
| 7 | 7 | * The count is the hard assertion and is judged first: it does not move with |
| @@ -20,7 +20,7 @@ import { getRepo } from "../src/core/repos.ts"; | ||
| 20 | 20 | import { createUser, type User } from "../src/core/users.ts"; |
| 21 | 21 | import { serve, type Servers } from "../src/http/server.ts"; |
| 22 | 22 | import { |
| 23 | PAGE_BUDGETS, countFinding, median, timeFinding, type PageBudget, type PageMeasure, | |
| 23 | PAGE_BUDGETS, countFinding, median, pagePath, timeFinding, type PageBudget, type PageMeasure, | |
| 24 | 24 | } from "./helpers/page_budget.ts"; |
| 25 | 25 | import { quietLogs } from "./helpers/globals.ts"; |
| 26 | 26 | import { promote } from "./helpers/members.ts"; |
| @@ -94,7 +94,7 @@ afterAll(() => { | ||
| 94 | 94 | const INIT = (): RequestInit => ({ headers: { cookie }, redirect: "manual" }); |
| 95 | 95 | |
| 96 | 96 | function url(b: PageBudget): string { |
| 97 | return `${servers.url}${b.pattern.replace(":owner", "alice").replace(":name", "notes")}`; | |
| 97 | return `${servers.url}${pagePath(b, "alice", "notes")}`; | |
| 98 | 98 | } |
| 99 | 99 | |
| 100 | 100 | /** One request warms; then the fewest statements of `COUNTED` warm requests is the count, so a background tick's queries do not land on a page. */ |
Non-blocking: this entry must come off once something under src/ or scripts/ imports the table, because the unreached test fails on a stale listing.