Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
105 linesCodeBlameRaw
1/**
2 * How long the site waits on a service before it gives up. Nothing on
3 * Workers ends a subrequest that stops answering: the page waits as long
4 * as the visitor does, so a service stuck on one call holds the page on
5 * its loading bar for good. With a deadline it fails instead, as an error
6 * the page can draw and the next click can get past.
7 */
8
9/** A read: no page should wait longer than this on one service call. */
10export const READ_DEADLINE_MS = 15_000;
11
12/** A write: some do real work (a merge, a commit), so they get longer. */
13export const WRITE_DEADLINE_MS = 60_000;
14
15/** Reads that do real work inline the first time, or walk history. */
16export const SLOW_READ_DEADLINE_MS = 60_000;
17
18/** Jobs a person starts and waits on: an import, a model's draft, a computer waking. */
19export const LONG_DEADLINE_MS = 600_000;
20
21/**
22 * Reads that can take longer than `READ_DEADLINE_MS` and still be right,
23 * by service binding and method. Cut off, their work is lost and starts
24 * again on the next visit, so they get the time to finish.
25 */
26const SLOW_READS = new Set([
27 // A repository's first look runs its dependency scan inline (services/security/src/deps.rs).
28 "security overview",
29 "security dependency_graph",
30 "security sbom",
31 // Walks up to 400 commits, reading trees and blobs one after another (services/repos/src/blame.rs).
32 "repos blame",
33 // Up to 2,500 store reads per call (services/repos/src/last_commits.rs).
34 "repos last_commits",
35]);
36
37/**
38 * Writes that run for minutes and still finish: copying a repository in,
39 * a model writing a draft, a computer's home moved in or
40 * out of storage, a scan over many repositories.
41 */
42const LONG_CALLS = new Set([
43 "repos create",
44 "integrations github_import",
45 "agents draft",
46 "agents try_draft",
47 "agents redraft",
48 "agents draft_skill",
49 "agents sync_skill_mirror",
50 "agents computer_wake",
51 "agents computer_sleep",
52 "security dry_run_pattern",
53 "security rescan",
54 "security check_updates",
55 "artifacts add_repo_space",
56]);
57
58/** How long a call to `service`'s `method` may take; `writes` as lib/perf.ts `mayWrite` says. */
59export function deadlineFor(service: string, method: string, writes: boolean): number {
60 const call = `${service} ${method}`;
61 if (LONG_CALLS.has(call)) return LONG_DEADLINE_MS;
62 if (SLOW_READS.has(call)) return SLOW_READ_DEADLINE_MS;
63 return writes ? WRITE_DEADLINE_MS : READ_DEADLINE_MS;
64}
65
66/** What a call past its deadline throws. */
67export class DeadlineExceeded extends Error {
68 constructor(what: string, ms: number) {
69 super(`${what} did not answer within ${ms}ms`);
70 this.name = "DeadlineExceeded";
71 }
72}
73
74/**
75 * `run`'s answer, or a `DeadlineExceeded` after `ms`. `run` is handed a
76 * signal that aborts at the deadline, so the call it makes is cancelled
77 * too; `outer`, when given, aborts it sooner. Whatever `run` reads must be
78 * read inside it: the deadline ends when it returns.
79 */
80export async function withDeadline<T>(
81 what: string,
82 ms: number,
83 run: (signal: AbortSignal) => Promise<T>,
84 outer?: AbortSignal | null,
85): Promise<T> {
86 const controller = new AbortController();
87 let timer: ReturnType<typeof setTimeout> | undefined;
88 const expired = new Promise<never>((_, reject) => {
89 timer = setTimeout(() => {
90 const error = new DeadlineExceeded(what, ms);
91 controller.abort(error);
92 reject(error);
93 }, ms);
94 });
95 const stop = () => controller.abort(outer?.reason);
96 if (outer?.aborted) stop();
97 else outer?.addEventListener("abort", stop, { once: true });
98 try {
99 // Raced as well as aborted: a binding that ignores the signal still lets go.
100 return await Promise.race([run(controller.signal), expired]);
101 } finally {
102 clearTimeout(timer);
103 outer?.removeEventListener("abort", stop);
104 }
105}