Public: anyone can read this; the crew can change it.
A main check stopped by its cell's limit holds no pull's check #3121
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/3119-a-main-check-stopped-by-its-cell-s-limit → main 1 commit fork
2 files
changed +28
−2
src/checks/red_main.ts +10 −2
| @@ -29,6 +29,8 @@ export interface RedMain { | ||
| 29 | 29 | /** The failing tests' names the check recorded, else its summary. */ |
| 30 | 30 | readonly failing: readonly string[]; |
| 31 | 31 | readonly summary: string; |
| 32 | /** The check was stopped by its cell's limit or its budget, not by a test: no pull shares it. */ | |
| 33 | readonly limit: boolean; | |
| 32 | 34 | readonly since: string; |
| 33 | 35 | } |
| 34 | 36 | |
| @@ -78,6 +80,9 @@ const lastMain = (from: string): string => ` | ||
| 78 | 80 | AND r.state IN ('ok', 'failed') |
| 79 | 81 | ORDER BY r.id DESC LIMIT 1)`; |
| 80 | 82 | |
| 83 | /** A summary of a cell stop (`apps/contain.ts`) or a budget kill (`kernel/budgets.ts`). */ | |
| 84 | const LIMIT = /^(?:check failed: )?(?:stopped: |budget:)/; | |
| 85 | ||
| 81 | 86 | function toRed(row: Row): RedMain | undefined { |
| 82 | 87 | if (row.state !== "failed") return undefined; |
| 83 | 88 | let failing: string[] = []; |
| @@ -95,6 +100,7 @@ function toRed(row: Row): RedMain | undefined { | ||
| 95 | 100 | commitOid: row.commit_oid, |
| 96 | 101 | failing, |
| 97 | 102 | summary: row.summary, |
| 103 | limit: LIMIT.test(row.summary), | |
| 98 | 104 | since: row.finished_at ?? row.created_at, |
| 99 | 105 | }; |
| 100 | 106 | } |
| @@ -108,6 +114,7 @@ export function heldByRed( | ||
| 108 | 114 | ): boolean { |
| 109 | 115 | return ( |
| 110 | 116 | red !== undefined && |
| 117 | !red.limit && | |
| 111 | 118 | run.kind === "check" && |
| 112 | 119 | run.issueId !== null && |
| 113 | 120 | run.reason !== ASKED && |
| @@ -144,7 +151,8 @@ const HELD = ` | ||
| 144 | 151 | |
| 145 | 152 | /** |
| 146 | 153 | * Each red main with pull checks waiting on it, and how many wait: one |
| 147 | * query. A check waiting past the hold is still counted until it runs. | |
| 154 | * query. A check waiting past the hold is still counted until it runs. A main | |
| 155 | * stopped by a limit holds nothing, so it has no card. | |
| 148 | 156 | */ |
| 149 | 157 | export function heldChecks(db: Database): { red: RedMain; held: number }[] { |
| 150 | 158 | const rows = db |
| @@ -157,7 +165,7 @@ export function heldChecks(db: Database): { red: RedMain; held: number }[] { | ||
| 157 | 165 | const out: { red: RedMain; held: number }[] = []; |
| 158 | 166 | for (const row of rows) { |
| 159 | 167 | const red = toRed(row); |
| 160 | if (red !== undefined) out.push({ red, held: row.n }); | |
| 168 | if (red !== undefined && !red.limit) out.push({ red, held: row.n }); | |
| 161 | 169 | } |
| 162 | 170 | return out; |
| 163 | 171 | } |
test/red-main.test.ts +18
| @@ -65,6 +65,24 @@ describe("red main", () => { | ||
| 65 | 65 | expect(unheld(queued(), red, HOLD).map((x) => x.id)).not.toContain(held.id); |
| 66 | 66 | }); |
| 67 | 67 | |
| 68 | test("a main stopped by its cell's limit or budget holds nothing, and a failed test still does", () => { | |
| 69 | const stop = (summary: string) => { | |
| 70 | const run = createRun(db, { repoId: "r1", kind: "check", env: "main", commitOid: OID("b"), reason: "push" }); | |
| 71 | claimRun(db, run.id); | |
| 72 | finishRun(db, run.id, { state: "failed", summary }); | |
| 73 | }; | |
| 74 | const waiting = pullCheck("a"); | |
| 75 | stop("check failed: stopped: used 4105 MiB of memory, over its 4096 MiB limit"); | |
| 76 | expect(redMain(db, "r1")).toMatchObject({ limit: true, commitOid: OID("b") }); | |
| 77 | expect(unheld(queued(), red, HOLD).map((x) => x.id)).toContain(waiting.id); | |
| 78 | expect(heldChecks(db)).toEqual([]); | |
| 79 | stop("budget: check took 31 min, limit 30"); | |
| 80 | expect(unheld(queued(), red, HOLD).map((x) => x.id)).toContain(waiting.id); | |
| 81 | main("c", "failed", ["x"]); | |
| 82 | expect(redMain(db, "r1")!.limit).toBe(false); | |
| 83 | expect(unheld(queued(), red, HOLD).map((x) => x.id)).not.toContain(waiting.id); | |
| 84 | }); | |
| 85 | ||
| 68 | 86 | test("a person's ask runs while main is red", () => { |
| 69 | 87 | main("b", "failed", ["x"]); |
| 70 | 88 | const asked = pullCheck("a", "asked"); |
new above
offline