Skip to content
231 linesCodeBlameRaw
1/**
2 * The pure parts of request timing and D1 read consistency for the site:
3 * the `g1t_d1` cookie, which session each service call asks for, which
4 * calls may write, and the `Server-Timing` header. perf.server.ts holds
5 * the per-request state; docs/PERFORMANCE.md explains the whole.
6 */
7
8/** The cookie that carries D1 bookmarks between a person's requests. */
9export const D1_COOKIE = "g1t_d1";
10
11/**
12 * How long after a write the services a request did not get a bookmark
13 * from read their primary. A write can reach a service the site did not
14 * call itself (work writing to repos, say), whose bookmark the site never
15 * sees; D1 replicas trail the primary by well under a second, so 30
16 * seconds covers that with room to spare.
17 */
18export const PRIMARY_WINDOW_SECONDS = 30;
19
20/** How long the bookmarks are kept: far longer than any replica trails. */
21export const D1_COOKIE_MAX_AGE = 300;
22
23/**
24 * The services whose RPCs open a D1 session (crates/kit/src/d1.rs and
25 * @g1t/contracts d1.ts), by the name the site gives their binding.
26 */
27export const SESSION_SERVICES = new Set(["identity", "repos", "work", "search", "billing", "projects", "deployments"]);
28
29/** What the site remembers from a person's last writes. */
30export type Bookmarks = {
31 /** Unix seconds of the last request that may have written, if recent. */
32 at: number | null;
33 /** The latest bookmark each service returned after it. */
34 services: Record<string, string>;
35};
36
37const BOOKMARK = /^[0-9A-Za-z-]{1,256}$/;
38const SERVICE = /^[a-z]{1,32}$/;
39
40/** Reads the `g1t_d1` cookie; anything malformed is dropped. */
41export function readBookmarks(cookieHeader: string | null): Bookmarks {
42 const empty: Bookmarks = { at: null, services: {} };
43 if (!cookieHeader) return empty;
44 const match = new RegExp(`(?:^|;\\s*)${D1_COOKIE}=([^;]*)`).exec(cookieHeader);
45 if (!match) return empty;
46 const found: Bookmarks = { at: null, services: {} };
47 for (const entry of match[1].split("~")) {
48 const colon = entry.indexOf(":");
49 if (colon < 1) continue;
50 const key = entry.slice(0, colon);
51 const value = entry.slice(colon + 1);
52 if (key === "at") {
53 const at = Number(value);
54 if (Number.isSafeInteger(at) && at > 0) found.at = at;
55 } else if (SERVICE.test(key) && SESSION_SERVICES.has(key) && BOOKMARK.test(value)) {
56 found.services[key] = value;
57 }
58 }
59 return found;
60}
61
62/** The `g1t_d1` cookie's value. */
63export function writeBookmarks(bookmarks: Bookmarks): string {
64 const entries = bookmarks.at ? [`at:${bookmarks.at}`] : [];
65 for (const [service, bookmark] of Object.entries(bookmarks.services).sort()) {
66 if (SESSION_SERVICES.has(service) && BOOKMARK.test(bookmark)) entries.push(`${service}:${bookmark}`);
67 }
68 return entries.join("~");
69}
70
71/** The `Set-Cookie` value for `bookmarks`. */
72export function bookmarkCookie(bookmarks: Bookmarks, secure: boolean): string {
73 return `${D1_COOKIE}=${writeBookmarks(bookmarks)}; Path=/; HttpOnly;${secure ? " Secure;" : ""} SameSite=Lax; Max-Age=${D1_COOKIE_MAX_AGE}`;
74}
75
76/**
77 * What a call to `service` sends as `x-d1-bookmark`, or null for none
78 * (that service reads its primary, as before sessions):
79 *
80 * - A request that writes (any method but GET and HEAD) starts every
81 * session on the primary, so what it checks before writing is current.
82 * - Within `PRIMARY_WINDOW_SECONDS` of the person's last write, the
83 * primary, for every service: that write may have reached a service
84 * through another one, whose bookmark the site never saw.
85 * - Otherwise the bookmark that service returned after that write: never
86 * older than what they did, however far a replica trails.
87 * - Otherwise the nearest copy.
88 */
89export function sessionFor(service: string, bookmarks: Bookmarks, writing: boolean, nowSeconds: number): string | null {
90 if (!SESSION_SERVICES.has(service)) return null;
91 if (writing) return "first-primary";
92 if (bookmarks.at && nowSeconds - bookmarks.at < PRIMARY_WINDOW_SECONDS) return "first-primary";
93 return bookmarks.services[service] ?? "first-unconstrained";
94}
95
96/**
97 * Methods that only read. Anything not listed is taken to write, so a new
98 * method errs towards a cookie and a primary read, never a stale page.
99 * `get`, `list`, `queue` and `pulls_for_repos` were missing: every project
100 * page called `get` (repos, projects), so every one set the cookie, was
101 * never kept in the public cache, and sent the next 30 s of the person's
102 * reads to the primary. `stars`, `about` and `public_links` (2026-10-08)
103 * did the same to every project page and Explore for 13 hours. `tags`,
104 * `commit_checks` and `shortcuts` (every overview), and the Files page's
105 * `last_commits`, `languages`, `contributors` and `license`, still made a
106 * signed-in view count as a write until 2026-10-08: the cookie, 30 s of
107 * primary reads, and no sidebar cache after every overview.
108 */
109const READS = new Set(
110 (
111 "account active_agents all_ids get list queue pulls_for_repos blame blob branches by_author by_repo catalog check_invite check_limit " +
112 "check_workspace_deletion check_workspace_rename collaborator_permission compare counts deleted deliveries " +
113 "dependencies domains entitlements entity explore features git_access graph has_feature invoices ledger limit " +
114 "limit_requests links log logs managed_pulls memories_by_id memory_context my_repo_invitations " +
115 "outside_collaborators overview path_by_id prices profile profile_workspaces public_namespaces read_session " +
116 "readable ready_issues references registration repo_access resolve resolve_branch resolve_path resolve_slug " +
117 "routes run run_context run_cost runner_groups runner_settings runners runs scorecards search search_memories " +
118 "settings statement statement_entries status status_by_id suggest tree usage usage_meters user_by_username " +
119 "user_for_session usernames waiting_workspaces workflows workspace workspace_invites github_enabled " +
120 "stars about public_links branch_drift tags last_commits languages contributors license releases release " +
121 "stargazers starred commit_checks shortcuts"
122 ).split(" "),
123);
124
125/**
126 * Whether a response sets the `g1t_d1` cookie. A signed-out GET never
127 * does, whatever it called: nobody signed out can write anything their
128 * next page must read, and a cookie keeps the page out of the public
129 * cache. Signing in on a GET (GitHub) starts a session, so it does.
130 */
131export function setsBookmark(request: { writing: boolean; wrote: boolean; hasSession: boolean; signedIn: boolean }): boolean {
132 if (request.writing || request.signedIn) return true;
133 return request.wrote && request.hasSession;
134}
135
136/** Whether an RPC to `method` may write. */
137export function mayWrite(method: string): boolean {
138 if (READS.has(method)) return false;
139 return !(method.startsWith("get_") || method.startsWith("list_"));
140}
141
142/** The method name of an RPC URL (`https://service/rpc/<method>`). */
143export function rpcMethodOf(input: string): string {
144 const at = input.indexOf("/rpc/");
145 return at < 0 ? "" : input.slice(at + 5).split(/[?#]/)[0];
146}
147
148/** One service's calls during a request. */
149export type ServiceTiming = {
150 calls: number;
151 /** Summed wall time of its calls, from here. */
152 wallMs: number;
153 /** Summed time its own `server-timing: svc;dur` reported. */
154 serviceMs: number;
155 /**
156 * Of that, summed time it reported waiting on its database
157 * (`db;dur`, crates/kit/src/d1.rs `Timing`) and in how many round trips.
158 * Absent for services that do not time them.
159 */
160 dbMs?: number;
161 dbTrips?: number;
162};
163
164/** The `svc;dur=N` a service reports, or null. */
165export function serviceDuration(header: string | null): number | null {
166 if (!header) return null;
167 const match = /(?:^|,)\s*svc;dur=([0-9.]+)/.exec(header);
168 return match ? Number(match[1]) : null;
169}
170
171/**
172 * The `db;dur=N;desc="T round trips, …"` a service reports: its summed
173 * database time and round trips, or null.
174 */
175export function databaseTime(header: string | null): { ms: number; trips: number } | null {
176 if (!header) return null;
177 const match = /(?:^|,)\s*db;dur=([0-9.]+)(?:;desc="(\d+) round trips?)?/.exec(header);
178 return match ? { ms: Number(match[1]), trips: Number(match[2] ?? 0) } : null;
179}
180
181/** Total time covered by overlapping [start, end] intervals. */
182export function coveredMs(intervals: [number, number][]): number {
183 const sorted = [...intervals].sort((a, b) => a[0] - b[0]);
184 let total = 0;
185 let end = -Infinity;
186 let start = -Infinity;
187 for (const [from, to] of sorted) {
188 if (from > end) {
189 if (end > start) total += end - start;
190 start = from;
191 end = to;
192 } else if (to > end) {
193 end = to;
194 }
195 }
196 if (end > start) total += end - start;
197 return total;
198}
199
200/** A Server-Timing metric name: a token, so `/` and spaces become `.`. */
201export function metricName(name: string): string {
202 return name.replace(/^routes\//, "").replace(/[^A-Za-z0-9_.-]+/g, ".");
203}
204
205/**
206 * The `Server-Timing` header for a request: the whole, the loaders, the
207 * time spent waiting on services (overlap counted once), then each
208 * service. DevTools shows them in this order under Network → Timing.
209 */
210export function serverTiming(input: {
211 totalMs: number;
212 loaders: { id: string; ms: number; kind: "loader" | "action" }[];
213 rpcMs: number;
214 services: Record<string, ServiceTiming>;
215 sessions: string;
216}): string {
217 const parts = [`total;dur=${input.totalMs};desc="web to first byte"`];
218 for (const loader of input.loaders) {
219 parts.push(`${loader.kind}.${metricName(loader.id)};dur=${loader.ms}`);
220 }
221 const calls = Object.values(input.services).reduce((sum, timing) => sum + timing.calls, 0);
222 if (calls > 0) parts.push(`rpc;dur=${input.rpcMs};desc="${calls} service calls, overlap counted once"`);
223 const ranked = Object.entries(input.services).sort((a, b) => b[1].wallMs - a[1].wallMs);
224 for (const [name, timing] of ranked) {
225 const db = timing.dbTrips ? `, db ${timing.dbMs ?? 0}ms in ${timing.dbTrips} round trip${timing.dbTrips === 1 ? "" : "s"}` : "";
226 const inside = timing.serviceMs > 0 ? `, ${timing.serviceMs}ms inside${db}` : "";
227 parts.push(`${metricName(name)};dur=${timing.wallMs};desc="${timing.calls} call${timing.calls === 1 ? "" : "s"}${inside}"`);
228 }
229 if (input.sessions) parts.push(`d1;desc="${input.sessions}"`);
230 return parts.join(", ");
231}