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.
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r9 | 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. */ | |
| 10 | export const READ_DEADLINE_MS = 15_000; | |
| 11 | ||
| 12 | /** A write: some do real work (a merge, a commit), so they get longer. */ | |
| 13 | export const WRITE_DEADLINE_MS = 60_000; | |
| 14 | ||
| 15 | /** Reads that do real work inline the first time, or walk history. */ | |
| 16 | export 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. */ | |
| 19 | export 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 | */ | |
| 26 | const 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 | * syncing a mirror, a model writing a draft, a computer's home moved in or | |
| 40 | * out of storage, a scan over many repositories. | |
| 41 | */ | |
| 42 | const LONG_CALLS = new Set([ | |
| 43 | "repos create", | |
| 44 | "integrations github_import", | |
| 45 | "integrations mirror_add", | |
| 46 | "integrations mirror_sync", | |
| 47 | "integrations mirror_move_in", | |
| 48 | "integrations mirror_hand_back", | |
| 49 | "security dry_run_pattern", | |
| 50 | "security rescan", | |
| 51 | "security check_updates", | |
| 52 | "artifacts add_repo_space", | |
| 53 | ]); | |
| 54 | ||
| 55 | /** How long a call to `service`'s `method` may take; `writes` as lib/perf.ts `mayWrite` says. */ | |
| 56 | export function deadlineFor(service: string, method: string, writes: boolean): number { | |
| 57 | const call = `${service} ${method}`; | |
| 58 | if (LONG_CALLS.has(call)) return LONG_DEADLINE_MS; | |
| 59 | if (SLOW_READS.has(call)) return SLOW_READ_DEADLINE_MS; | |
| 60 | return writes ? WRITE_DEADLINE_MS : READ_DEADLINE_MS; | |
| 61 | } | |
| 62 | ||
| 63 | /** What a call past its deadline throws. */ | |
| 64 | export class DeadlineExceeded extends Error { | |
| 65 | constructor(what: string, ms: number) { | |
| 66 | super(`${what} did not answer within ${ms}ms`); | |
| 67 | this.name = "DeadlineExceeded"; | |
| 68 | } | |
| 69 | } | |
| 70 | ||
| 71 | /** | |
| 72 | * `run`'s answer, or a `DeadlineExceeded` after `ms`. `run` is handed a | |
| 73 | * signal that aborts at the deadline, so the call it makes is cancelled | |
| 74 | * too; `outer`, when given, aborts it sooner. Whatever `run` reads must be | |
| 75 | * read inside it: the deadline ends when it returns. | |
| 76 | */ | |
| 77 | export async function withDeadline<T>( | |
| 78 | what: string, | |
| 79 | ms: number, | |
| 80 | run: (signal: AbortSignal) => Promise<T>, | |
| 81 | outer?: AbortSignal | null, | |
| 82 | ): Promise<T> { | |
| 83 | const controller = new AbortController(); | |
| 84 | let timer: ReturnType<typeof setTimeout> | undefined; | |
| 85 | const expired = new Promise<never>((_, reject) => { | |
| 86 | timer = setTimeout(() => { | |
| 87 | const error = new DeadlineExceeded(what, ms); | |
| 88 | controller.abort(error); | |
| 89 | reject(error); | |
| 90 | }, ms); | |
| 91 | }); | |
| 92 | const stop = () => controller.abort(outer?.reason); | |
| 93 | if (outer?.aborted) stop(); | |
| 94 | else outer?.addEventListener("abort", stop, { once: true }); | |
| 95 | try { | |
| 96 | // Raced as well as aborted: a binding that ignores the signal still lets go. | |
| 97 | return await Promise.race([run(controller.signal), expired]); | |
| 98 | } finally { | |
| 99 | clearTimeout(timer); | |
| 100 | outer?.removeEventListener("abort", stop); | |
| 101 | } | |
| 102 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.