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
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/11002-sentinel-shared-01m23vygyffx5hg2f5vm8r74 → main 2 commits fork 1 thread · 1 unresolved
3 files
changed +20
−4
src/perf/loop.ts +7
| @@ -57,6 +57,8 @@ export interface LoopWatch { | ||
| 57 | 57 | stalled(lateMs: number, at: number): Stall | null; |
| 58 | 58 | /** The stalls so far by route, the hour ending at `now` (same clock as `at`). */ |
| 59 | 59 | 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; | |
| 60 | 62 | } |
| 61 | 63 | |
| 62 | 64 | /** 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 = () | ||
| 107 | 109 | if (hour.length > HOUR_KEPT) hour.shift(); |
| 108 | 110 | return stall; |
| 109 | 111 | }, |
| 112 | clear() { | |
| 113 | recent.length = 0; | |
| 114 | totals.clear(); | |
| 115 | hour.length = 0; | |
| 116 | }, | |
| 110 | 117 | tally(now) { |
| 111 | 118 | while (hour.length > 0 && hour[0]!.at <= now - HOUR_MS) hour.shift(); |
| 112 | 119 | return { |
test/mcp-system-stalls.test.ts +3 −4
| @@ -35,10 +35,9 @@ afterAll(async () => { | ||
| 35 | 35 | }); |
| 36 | 36 | |
| 37 | 37 | 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(); | |
| 42 | 41 | during("test-job-10907", () => 1); |
| 43 | 42 | loopWatch.stalled(LIMIT_MS + 10, performance.now()); |
| 44 | 43 | 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", () => { | ||
| 21 | 21 | expect(s.routes).toEqual([]); |
| 22 | 22 | expect(stallLine(s)).toContain("no request"); |
| 23 | 23 | }); |
| 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 | }); |
Add a unit test for
clear()in the perf-loop tests. It should coverrecent,totalsandhour: afterclear(),tallyis empty and a stall inside a cleared request window has no routes.