g1t/scripts/perf/placement-probe.mjs

242 lines10,774 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Fast pages, required checks on the branch, self-hosted runners, honest incidents1#!/usr/bin/env node
2// Where should g1t's Workers run? A throwaway experiment that answers it
3// with numbers, without touching any production Worker.
4//
5// It deploys a handful of tiny Workers named g1t-probe-*, one per
6// placement setting, each bound read-only in practice (it runs only
7// `SELECT 1`) to the g1t-repos D1 database, plus g1t-probe-edge, which runs
8// near the caller and calls each of the others through a service binding,
9// the way the site calls the services. Then it measures them from this
10// machine, prints a table, and deletes them.
11//
12// node scripts/perf/placement-probe.mjs deploy # make the probes (needs `wrangler login`)
13// node scripts/perf/placement-probe.mjs measure # 1 warm-up + 7 requests each, a table
14// node scripts/perf/placement-probe.mjs delete # remove them all
15// node scripts/perf/placement-probe.mjs apply '{"region":"aws:us-west-1"}'
16// # write a placement into every config that should have it
17// node scripts/perf/placement-probe.mjs apply '{"mode":"smart"}' # back to Smart Placement
18//
19// docs/PERFORMANCE.md explains what to look for and which Workers `apply`
20// changes. Nothing here runs in CI.
21
22import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
23import { tmpdir } from "node:os";
24import { join } from "node:path";
25
26import { exec, wranglerEnv } from "../deploy/cloudflare.mjs";
27import { ROOT } from "../deploy/stack.mjs";
28
29const WRANGLER = join(ROOT, "node_modules/wrangler/bin/wrangler.js");
30const DATABASE = { name: "g1t-repos", id: "f9544c51-c3bf-4621-a96f-8a6d5cf24a97" };
31
32/** Each placement tried. Regions near D1's WNAM, one far one as a control. */
33export const VARIANTS = [
34 { name: "off", placement: { mode: "off" } },
35 { name: "smart", placement: { mode: "smart" } },
36 { name: "aws-us-west-1", placement: { region: "aws:us-west-1" } },
37 { name: "aws-us-west-2", placement: { region: "aws:us-west-2" } },
38 { name: "gcp-us-west1", placement: { region: "gcp:us-west1" } },
39 { name: "gcp-us-west2", placement: { region: "gcp:us-west2" } },
40 { name: "aws-us-east-1", placement: { region: "aws:us-east-1" } },
41];
42
43/**
44 * The Workers that read D1 or call services that do, by config: what
45 * `apply` sets the placement of. The rest (pages, models, og, runner,
46 * status, docs) serve from the edge or have no data of their own.
47 */
48export const PLACED = [
49 "apps/web/wrangler.jsonc",
50 "apps/api/wrangler.jsonc",
51 "apps/sudo/wrangler.jsonc",
52 "services/actions/wrangler.jsonc",
53 "services/billing/wrangler.jsonc",
54 "services/context/wrangler.jsonc",
55 "services/deployments/wrangler.jsonc",
56 "services/events/wrangler.jsonc",
57 "services/identity/wrangler.jsonc",
58 "services/integrations/wrangler.jsonc",
59 "services/projects/wrangler.jsonc",
60 "services/repos/wrangler.jsonc",
61 "services/search/wrangler.jsonc",
62 "services/security/wrangler.jsonc",
63 "services/webhooks/wrangler.jsonc",
64 "services/work/wrangler.jsonc",
65];
66
67const PROBE = `
68const QUERIES = 5;
69async function ranIn() {
70 const trace = await (await fetch("https://cloudflare.com/cdn-cgi/trace")).text();
71 return /colo=(\\w+)/.exec(trace)?.[1] ?? null;
72}
73async function timedQueries(db) {
74 const ms = [];
75 let meta = null;
76 for (let i = 0; i < QUERIES; i++) {
77 const started = Date.now();
78 const result = await db.prepare("SELECT 1 AS one").run();
79 ms.push(Date.now() - started);
80 meta = result.meta;
81 }
82 return { ms, region: meta?.served_by_region ?? null, primary: meta?.served_by_primary ?? null, sql_ms: meta?.timings?.sql_duration_ms ?? null };
83}
84export default {
85 async fetch(request, env) {
86 const { pathname } = new URL(request.url);
87 if (pathname === "/chain") {
88 const names = Object.keys(env).filter((key) => key.startsWith("TO_"));
89 const out = {};
90 await Promise.all(names.map(async (name) => {
91 const started = Date.now();
92 try {
93 const answer = await (await env[name].fetch("https://probe/")).json();
94 out[name.slice(3).toLowerCase().replaceAll("_", "-")] = { rpc_ms: Date.now() - started, ran_in: answer.ran_in, primary_ms: answer.primary.ms };
95 } catch (error) {
96 out[name] = { error: String(error) };
97 }
98 }));
99 return Response.json({ worker: env.NAME, ingress: request.cf?.colo ?? null, ran_in: await ranIn(), chain: out });
100 }
101 const where = await ranIn();
102 const primary = await timedQueries(env.DB);
103 const session = await timedQueries(env.DB.withSession("first-unconstrained"));
104 return Response.json({ worker: env.NAME, ingress: request.cf?.colo ?? null, ran_in: where, primary, session });
105 },
106};
107`;
108
109function configFor(variant, bindings = []) {
110 return {
111 name: `g1t-probe-${variant.name}`,
112 main: "worker.js",
113 account_id: "1e6f2cffa3f445920836e8ebe446bb58",
114 compatibility_date: "2026-09-26",
115 workers_dev: true,
116 preview_urls: false,
117 placement: variant.placement,
118 vars: { NAME: variant.name },
119 d1_databases: [{ binding: "DB", database_name: DATABASE.name, database_id: DATABASE.id }],
120 services: bindings,
121 };
122}
123
124async function wrangler(args, cwd) {
125 const { code, out } = await exec(process.execPath, [WRANGLER, ...args], { cwd, env: wranglerEnv() });
126 return { code, out };
127}
128
129const URLS = join(tmpdir(), "g1t-placement-probe", "urls.json");
130
131async function deploy() {
132 const urls = {};
133 const edge = { name: "edge", placement: { mode: "off" } };
134 const bindings = VARIANTS.map((v) => ({ binding: `TO_${v.name.toUpperCase().replaceAll("-", "_")}`, service: `g1t-probe-${v.name}` }));
135 for (const [variant, extra] of [...VARIANTS.map((v) => [v, []]), [edge, bindings]]) {
136 const dir = join(tmpdir(), "g1t-placement-probe", variant.name);
137 rmSync(dir, { recursive: true, force: true });
138 mkdirSync(dir, { recursive: true });
139 writeFileSync(join(dir, "worker.js"), PROBE);
140 writeFileSync(join(dir, "wrangler.json"), JSON.stringify(configFor(variant, extra), null, 2));
141 process.stdout.write(`deploying g1t-probe-${variant.name} ${JSON.stringify(variant.placement)} … `);
142 const { code, out } = await wrangler(["deploy", "--config", "wrangler.json"], dir);
143 const url = /https:\/\/g1t-probe-[\w-]+\.[\w-]+\.workers\.dev/.exec(out)?.[0];
144 console.log(code === 0 ? url ?? "deployed (no URL printed)" : "FAILED");
145 if (code !== 0) console.log(out.split("\n").slice(-15).join("\n"));
146 if (url) urls[variant.name] = url;
147 }
148 writeFileSync(URLS, JSON.stringify(urls, null, 2));
149 console.log("\nSmart Placement needs traffic from several places and up to 15 minutes before it moves anything;");
150 console.log("the region variants are placed at once. Wait a minute, then: node scripts/perf/placement-probe.mjs measure");
151}
152
153const median = (values) => {
154 const sorted = [...values].filter((v) => typeof v === "number").sort((a, b) => a - b);
155 return sorted.length ? sorted[Math.floor(sorted.length / 2)] : null;
156};
157
158async function timedFetch(url) {
159 const started = performance.now();
160 const response = await fetch(url, { headers: { "cache-control": "no-cache" } });
161 const ttfb = performance.now() - started;
162 const body = await response.json();
163 return { ttfb: Math.round(ttfb), placement: response.headers.get("cf-placement"), body };
164}
165
166async function measure() {
167 const urls = JSON.parse(readFileSync(URLS, "utf8"));
168 const rows = [];
169 for (const variant of VARIANTS) {
170 const url = urls[variant.name];
171 if (!url) continue;
172 await timedFetch(url).catch(() => null);
173 const runs = [];
174 for (let i = 0; i < 7; i++) runs.push(await timedFetch(url));
175 const last = runs.at(-1);
176 rows.push({
177 variant: variant.name,
178 "cf-placement": last.placement ?? "",
179 "ran in": last.body.ran_in,
180 "ttfb ms (p50)": median(runs.map((r) => r.ttfb)),
181 "D1 primary ms/query (p50)": median(runs.flatMap((r) => r.body.primary.ms)),
182 "D1 session ms/query (p50)": median(runs.flatMap((r) => r.body.session.ms)),
183 "session served by": `${last.body.session.region ?? "?"}${last.body.session.primary ? " (primary)" : ""}`,
184 });
185 }
186 console.table(rows);
187 if (urls.edge) {
188 await timedFetch(`${urls.edge}/chain`).catch(() => null);
189 const chains = [];
190 for (let i = 0; i < 5; i++) chains.push(await timedFetch(`${urls.edge}/chain`));
191 const names = Object.keys(chains[0].body.chain);
192 console.log(`\nFrom g1t-probe-edge (runs in ${chains[0].body.ran_in}) through a service binding, as the site calls a service:`);
193 console.table(
194 names.map((name) => ({
195 target: name,
196 "target ran in": chains.at(-1).body.chain[name].ran_in,
197 "rpc ms (p50)": median(chains.map((c) => c.body.chain[name].rpc_ms)),
198 "its D1 ms/query (p50)": median(chains.flatMap((c) => c.body.chain[name].primary_ms ?? [])),
199 })),
200 );
201 }
202 console.log("\nPick the region with the lowest D1 primary ms/query (the database's neighbour), then:");
203 console.log(` node scripts/perf/placement-probe.mjs apply '{"region":"<it>"}'`);
204}
205
206async function remove() {
207 for (const name of [...VARIANTS.map((v) => v.name), "edge"]) {
208 const { code } = await wrangler(["delete", "--name", `g1t-probe-${name}`, "--force"], ROOT);
209 console.log(`g1t-probe-${name}: ${code === 0 ? "deleted" : "not deleted (already gone?)"}`);
210 }
211}
212
213/** Sets `"placement"` in every config in PLACED, keeping the line's place. */
214export function withPlacement(text, placement) {
215 const fields = Object.entries(placement).map(([key, value]) => `"${key}": ${JSON.stringify(value)}`);
216 const line = `"placement": { ${fields.join(", ")} },`;
217 if (/"placement":\s*\{[^}]*\},?/.test(text)) return text.replace(/"placement":\s*\{[^}]*\},?/, line);
218 return text.replace(/(\r?\n)([ \t]*)("compatibility_date":[^\r\n]*)/, (_, nl, indent, date) => `${nl}${indent}${date}${nl}${indent}${line}`);
219}
220
221function apply(json) {
222 const placement = JSON.parse(json);
223 for (const file of PLACED) {
224 const path = join(ROOT, file);
225 const before = readFileSync(path, "utf8");
226 const after = withPlacement(before, placement);
227 if (after !== before) writeFileSync(path, after);
228 console.log(`${file}: ${after === before ? "unchanged" : JSON.stringify(placement)}`);
229 }
230}
231
232const [command, argument] = process.argv.slice(2);
233if (process.argv[1]?.replaceAll("\\", "/").endsWith("scripts/perf/placement-probe.mjs")) {
234 if (command === "deploy") await deploy();
235 else if (command === "measure") await measure();
236 else if (command === "delete") await remove();
237 else if (command === "apply" && argument) apply(argument);
238 else {
239 console.log("usage: node scripts/perf/placement-probe.mjs deploy | measure | delete | apply '<placement json>'");
240 process.exitCode = 1;
241 }
242}