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

Sentinel: shared:01m23vygyffx5hg2f5vm8r74sr:1nlk8lbqbv is past its budget: trevato/forge: "a stall under a named job is read through system, at most 10, most ms first" failed on 6 pulls in the l… #11017

Merged opened this

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

Merged

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

  • Merged. mason/11002-sentinel-shared-01m23vygyffx5hg2f5vm8r74 was merged into main as 3a569be.

Review

Sign in to review this change.
3 files changed +20 −4
src/perf/loop.ts +7
@@ -57,6 +57,8 @@ export interface LoopWatch {
5757 stalled(lateMs: number, at: number): Stall | null;
5858 /** The stalls so far by route, the hour ending at `now` (same clock as `at`). */
5959 tally(now: number): StallTally;
60 /** Forget every request and stall noted so far; a test sharing the process's watch starts from nothing. */
61 clear(): void;
6062 }
6163
6264 /** What a stall is counted against: its routes, else its jobs, else `NO_REQUEST`. */
@@ -107,6 +109,11 @@ export function createLoopWatch(limitMs = LIMIT_MS, jobsAcross: JobsAcross = ()
107109 if (hour.length > HOUR_KEPT) hour.shift();
108110 return stall;
109111 },
112 clear() {

Add a unit test for clear() in the perf-loop tests. It should cover recent, totals and hour: after clear(), tally is empty and a stall inside a cleared request window has no routes.

113 recent.length = 0;
114 totals.clear();
115 hour.length = 0;
116 },
110117 tally(now) {
111118 while (hour.length > 0 && hour[0]!.at <= now - HOUR_MS) hour.shift();
112119 return {
test/mcp-system-stalls.test.ts +3 −4
@@ -35,10 +35,9 @@ afterAll(async () => {
3535 });
3636
3737 test("a stall under a named job is read through system, at most 10, most ms first", async () => {
38 // A stall is blamed on any request that finished inside it, before a job:
39 // wait out the requests an earlier file in this process ended, then stall
40 // just past the limit so the window holds the job alone.
41 await Bun.sleep(LIMIT_MS + 50);
38 // The watch is the process's one: stalls and requests an earlier file left
39 // would crowd the top 10 or be blamed ahead of the job. Start from nothing.
40 loopWatch.clear();
4241 during("test-job-10907", () => 1);
4342 loopWatch.stalled(LIMIT_MS + 10, performance.now());
4443 const res = await fetch(`${servers.url}/mcp`, {
test/perf-loop-lag-attribution.test.ts +10
@@ -21,3 +21,13 @@ test("a stall with no request running says so", () => {
2121 expect(s.routes).toEqual([]);
2222 expect(stallLine(s)).toContain("no request");
2323 });
24 test("clear forgets requests, totals and the hour", () => {
25 const w = createLoopWatch(250);
26 w.finished("/boards", 1000, 2000);
27 expect(w.stalled(500, 1500)!.routes).toEqual(["/boards"]);
28 expect(w.tally(2000).total).toHaveLength(1);
29 expect(w.tally(2000).hour).toHaveLength(1);
30 w.clear();
31 expect(w.tally(2000)).toEqual({ total: [], hour: [] });
32 expect(w.stalled(500, 1500)!.routes).toEqual([]);
33 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs