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

A map's share card resolves under 150 ms and draws under 300 KB for 2,000 closed and 300 open issues #6265

Merged opened this

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

Merged

scout/forge:scout/6206-a-map-s-share-card-resolves-under-150-ms → main 1 commit fork

  • Merged. scout/6206-a-map-s-share-card-resolves-under-150-ms was merged into main as f059892.

Review

Sign in to review this change.
3 files changed +31 −17
src/web/og/map.ts +17 −9
@@ -103,7 +103,13 @@ export function mapStat(counts: Record<Word, number>): string {
103103 }
104104
105105 /** A repository's map card, from its graph as nobody reads it and its marks. */
106 export function mapInput(title: string, graph: Graph, marks: Marks): CardInput {
106 export function mapInput(
107 title: string,
108 graph: Graph,
109 marks: Marks,
110 /** Names a stretch of the work; the route times it, a test need not. */
111 stretch: <T>(name: string, work: () => T) => T = (_name, work) => work(),
112 ): CardInput {
107113 const ready = readyNow(graph);
108114 const path = criticalPath(graph);
109115 const readySet = new Set(ready.map((r) => r.issue.id));
@@ -111,14 +117,16 @@ export function mapInput(title: string, graph: Graph, marks: Marks): CardInput {
111117 const open = graph.issues.filter((i) => i.state === "open").length;
112118 // The thumbnail draws what is open: done issues are counted in the caption, not drawn.
113119 const live = new Set(graph.issues.filter((i) => i.state === "open").map((i) => i.id));
114 const figure = graphFigure(
115 {
116 ...graph,
117 issues: graph.issues.filter((i) => live.has(i.id)),
118 edges: graph.edges.filter((e) => live.has(e.issueId) && live.has(e.blockedById)),
119 },
120 words,
121 path.map((i) => i.id),
120 const figure = stretch("layout", () =>
121 graphFigure(
122 {
123 ...graph,
124 issues: graph.issues.filter((i) => live.has(i.id)),
125 edges: graph.edges.filter((e) => live.has(e.issueId) && live.has(e.blockedById)),
126 },
127 words,
128 path.map((i) => i.id),
129 ),
122130 );
123131 // Next is what a free hand would take: ready, and nobody on it yet.
124132 const next = ready.find((r) => words.get(r.issue.id) === "ready")?.issue;
src/web/pages/og.ts +10 −6
@@ -291,12 +291,16 @@ function mapCardInput(ctx: Ctx, ref: string): CardInput | undefined {
291291 const repo = twoPart(ctx, ref);
292292 if (!repo) return undefined;
293293 const db = ctx.app.db;
294 const graph = repoGraph(db, repo.id, sealedScope(db, repo, undefined));
295 // A run on a sealed report is not shown to who may not see the report.
296 const runs = listActiveRuns(db, [repo.id]).filter((r) =>
297 mayViewId(db, repo, r.issueId, undefined),
298 );
299 return mapInput(`${repo.ownerName}/${repo.name}`, graph, marksFor(db, repo.id, graph, runs));
294 const stretch = <T>(name: string, work: () => T): T => timePhase(ctx.request, name, work);
295 const graph = stretch("graph", () => repoGraph(db, repo.id, sealedScope(db, repo, undefined)));
296 const marks = stretch("marks", () => {
297 // A run on a sealed report is not shown to who may not see the report.
298 const runs = listActiveRuns(db, [repo.id]).filter((r) =>
299 mayViewId(db, repo, r.issueId, undefined),
300 );
301 return marksFor(db, repo.id, graph, runs);
302 });
303 return mapInput(`${repo.ownerName}/${repo.name}`, graph, marks, stretch);
300304 }
301305
302306 /** The public apps, read the way the audit page reads them for someone signed out. */
test/og-map.test.ts +4 −2
@@ -50,10 +50,12 @@ test("the card draws open issues only, names the done count, and resolves fast",
5050 const res = await fetch(`${servers.url}/og/map/alice~notes.svg`);
5151 const svg = await res.text();
5252 expect(res.status).toBe(200);
53 expect(new TextEncoder().encode(svg).length).toBeLessThan(200_000);
53 expect(new TextEncoder().encode(svg).length).toBeLessThan(300_000);
5454 expect(svg).toContain("2000 done");
5555 expect(svg).toContain("300 open issues");
5656 const resolve = /resolve;dur=([\d.]+)/.exec(res.headers.get("server-timing") ?? "");
5757 expect(resolve).not.toBeNull();
58 expect(Number(resolve![1])).toBeLessThan(100);
58 expect(Number(resolve![1])).toBeLessThan(150);
59 const names = (res.headers.get("server-timing") ?? "").split(", ").map((p) => p.split(";")[0]);
60 for (const n of ["graph", "marks", "layout"]) expect(names).toContain(n);
5961 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs