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

Merged opened this

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

Merged

scout/forge:scout/11360-small-the-page-load-budget-lives-in-src → main 3 commits fork 2 threads · 2 unresolved

  • Merged. scout/11360-small-the-page-load-budget-lives-in-src was merged into main as fa200fe.

Review

Sign in to review this change.
5 files changed +55 −31
scripts/unreached-allow.json +1
@@ -10,6 +10,7 @@
1010 "src/flow/review_rounds.ts",
1111 "src/flow/shared_red.ts",
1212 "src/flow/stale_fold.ts",
13 "src/perf/page_budget.ts",

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.

1314 "src/spawn/pod.ts",
1415 "src/tui/gallery.ts",
1516 "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 @@
11 /**
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.
115 */
6 import type { PageBudget } from "../../src/perf/page_budget.ts";
127
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";
329
3310 /** The median of `xs` (the mean of the middle two for an even count); NaN for none. */
3411 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 @@
11 /**
22 * The page-load budget (#11320): the five pages people open first answer
33 * 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`
55 * allows.
66 *
77 * 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";
2020 import { createUser, type User } from "../src/core/users.ts";
2121 import { serve, type Servers } from "../src/http/server.ts";
2222 import {
23 PAGE_BUDGETS, countFinding, median, timeFinding, type PageBudget, type PageMeasure,
23 PAGE_BUDGETS, countFinding, median, pagePath, timeFinding, type PageBudget, type PageMeasure,
2424 } from "./helpers/page_budget.ts";
2525 import { quietLogs } from "./helpers/globals.ts";
2626 import { promote } from "./helpers/members.ts";
@@ -94,7 +94,7 @@ afterAll(() => {
9494 const INIT = (): RequestInit => ({ headers: { cookie }, redirect: "manual" });
9595
9696 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")}`;
9898 }
9999
100100 /** 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. */

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs