Public: anyone can read this; the crew can change it.

Sentinel: shared:01m23vygyffx5hg2f5vm8r74sr:2ml3f5dne9 is past its budget: trevato/forge: "concurrent checks > a fresh account's second check waits queued, naming the count; the owner's two run … #5196

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/5195-sentinel-shared-01m23vygyffx5hg2f5vm8r74 → main 2 commits fork 1 thread · 1 unresolved

  • Merged. scout/5195-sentinel-shared-01m23vygyffx5hg2f5vm8r74 was merged into main as 045acd7.

Review

Sign in to review this change.
1 file changed +15 −15
test/quotas-running.test.ts +15 −15
@@ -4,7 +4,7 @@
44 * does not start — a preview's run fails, a run or check stays queued — and
55 * its words name the limit and the count ("1 of 1 live previews"). The
66 * owner's own (admin) account starts the same things freely. Every wait is
7 * for a named state or a flag file, never a guess at timing.
7 * for a named state or a flag file, never a guess at timing, and each wait outlasts a check machine running the whole suite at once.

Wrap this: the sentence runs far past the file's comment width. For example end the line after "timing," and continue on a new * and each wait outlasts a check machine running the whole suite at once. line.

This sentence makes the header run past the line width of the rest of the comment. Wrap it, or cut it to: "and each wait outlasts a loaded check machine."

88 */
99
1010 import { describe, expect, test } from "bun:test";
@@ -30,14 +30,14 @@ import { readRunLog } from "../src/runs/log.ts";
3030 import { QUOTA_KEYS, getQuota, runningLimitSentence, setQuota } from "../src/settings/quotas.ts";
3131 import { promote } from "./helpers/members.ts";
3232
33 async function until<T>(read: () => T | undefined, ms = 60_000): Promise<T> {
33 async function until<T>(read: () => T | undefined, ms = 120_000, what = "the awaited state"): Promise<T> {
3434 const deadline = Date.now() + ms;
3535 while (Date.now() < deadline) {
3636 const value = read();
3737 if (value !== undefined) return value;
3838 await Bun.sleep(50);
3939 }
40 throw new Error("did not happen in time");
40 throw new Error(`did not happen in time: ${what}`);
4141 }
4242
4343 async function twoUsers(app: App): Promise<{ alice: User; bob: User }> {
@@ -142,7 +142,7 @@ describe("live previews", () => {
142142 app.close();
143143 await rm(dataDir, { recursive: true, force: true });
144144 }
145 }, 180_000);
145 }, 600_000);
146146 });
147147
148148 describe("two previews deploying together", () => {
@@ -201,7 +201,7 @@ describe("two previews deploying together", () => {
201201 app.close();
202202 await rm(dataDir, { recursive: true, force: true });
203203 }
204 }, 180_000);
204 }, 600_000);
205205 });
206206
207207 describe("a sleeping preview woken at the limit", () => {
@@ -264,7 +264,7 @@ describe("a sleeping preview woken at the limit", () => {
264264 app.close();
265265 await rm(dataDir, { recursive: true, force: true });
266266 }
267 }, 180_000);
267 }, 600_000);
268268 });
269269
270270 describe("concurrent checks", () => {
@@ -318,7 +318,7 @@ describe("concurrent checks", () => {
318318 app.close();
319319 await rm(dataDir, { recursive: true, force: true });
320320 }
321 }, 180_000);
321 }, 600_000);
322322
323323 test("a fresh account's second check waits queued, naming the count; the owner's two run at once; a freed slot starts the waiter", async () => {
324324 const dataDir = await mkdtemp(join(tmpdir(), "forge-qrun-checks-"));
@@ -351,27 +351,27 @@ describe("concurrent checks", () => {
351351 const stateOf = (repo: Repo) => checkOf(repo)?.state;
352352
353353 const b1 = await push(bob, "b1");
354 await until(() => (stateOf(b1) === "running" ? true : undefined));
354 await until(() => (stateOf(b1) === "running" ? true : undefined), 120_000, "b1 running");
355355 const b2 = await push(bob, "b2");
356356 const waiting = await until(() => {
357357 const run = checkOf(b2);
358358 return run !== undefined && readRunLog(dataDir, run.id).lines.join("\n").includes("1 of 1 concurrent checks")
359359 ? run
360360 : undefined;
361 });
361 }, 120_000, "b2 queued with its count in the log");
362362 expect(stateOf(b2)).toBe("queued");
363363 expect(waiting.state).toBe("queued");
364364
365365 const a1 = await push(alice, "a1");
366366 const a2 = await push(alice, "a2");
367 await until(() => (stateOf(a1) === "running" && stateOf(a2) === "running" ? true : undefined));
367 await until(() => (stateOf(a1) === "running" && stateOf(a2) === "running" ? true : undefined), 120_000, "a1 and a2 running");
368368
369369 // The slot frees: bob's waiter is started, never dropped.
370370 writeFileSync(flag("b1"), "");
371 await until(() => (stateOf(b1) === "ok" ? true : undefined));
372 await until(() => (stateOf(b2) === "running" ? true : undefined));
371 await until(() => (stateOf(b1) === "ok" ? true : undefined), 120_000, "b1 ok");
372 await until(() => (stateOf(b2) === "running" ? true : undefined), 120_000, "b2 running once b1 freed its slot");
373373 for (const name of ["b2", "a1", "a2"]) writeFileSync(flag(name), "");
374 await until(() => ([b2, a1, a2].every((r) => stateOf(r) === "ok") ? true : undefined));
374 await until(() => ([b2, a1, a2].every((r) => stateOf(r) === "ok") ? true : undefined), 120_000, "b2, a1, a2 ok");
375375 expect(existsSync(flag("b2"))).toBe(true);
376376 } finally {
377377 await checks.stop();
@@ -379,7 +379,7 @@ describe("concurrent checks", () => {
379379 app.close();
380380 await rm(dataDir, { recursive: true, force: true });
381381 }
382 }, 180_000);
382 }, 600_000);
383383 });
384384
385385 describe("concurrent runs", () => {
@@ -453,5 +453,5 @@ echo '{"type":"result","subtype":"success","is_error":false,"result":"ok","total
453453 app.close();
454454 await rm(dataDir, { recursive: true, force: true });
455455 }
456 }, 180_000);
456 }, 600_000);
457457 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs