diff --git a/packages/server/src/test-utils/main-thread-work.ts b/packages/server/src/test-utils/main-thread-work.ts index 66bb55e491..c823a96abd 100644 --- a/packages/server/src/test-utils/main-thread-work.ts +++ b/packages/server/src/test-utils/main-thread-work.ts @@ -24,6 +24,8 @@ export async function measureMainThreadWork(label: string, action: () => T | let previousCpu = threadCpuUsage(); let probe: ReturnType; let drain: ReturnType; + const types = new Map(); + const stacks = new Map(); const callbacks = new Map(); const record = (wallMs: number, cpu: NodeJS.CpuUsage) => { const cpuMs = (cpu.user + cpu.system) / 1_000; @@ -46,6 +48,10 @@ export async function measureMainThreadWork(label: string, action: () => T | gc.observe({ entryTypes: ["gc"] }); const activity = createHook({ init(_id, type) { + if (label === "project config main-thread work") { + types.set(_id, type); + stacks.set(_id, new Error().stack ?? ""); + } if (type.startsWith("FSREQ")) filesystemRequests++; if (!draining && type === "Immediate") yields++; if (type === "WORKER") workerEvents++; @@ -60,7 +66,13 @@ export async function measureMainThreadWork(label: string, action: () => T | const start = callbacks.get(id); if (!start) return; callbacks.delete(id); - record(performance.now() - start.wall, threadCpuUsage(start.cpu)); + const wall = performance.now() - start.wall; + record(wall, threadCpuUsage(start.cpu)); + if (wall > 10 && label === "project config main-thread work") { + process.stderr.write( + JSON.stringify({ wall, type: types.get(id), stack: stacks.get(id) }) + "\n", + ); + } }, }).enable(); const sample = () => { diff --git a/packages/server/src/utils/paseo-config-file.test.ts b/packages/server/src/utils/paseo-config-file.test.ts index 186a5259ed..176c5754be 100644 --- a/packages/server/src/utils/paseo-config-file.test.ts +++ b/packages/server/src/utils/paseo-config-file.test.ts @@ -111,20 +111,23 @@ describe("paseo config file substrate", () => { expect(blockingMs).toBeLessThan(20); }); - it("keeps main-thread work below 20ms while admitting large configs", async () => { - const config = { - scripts: Object.fromEntries( - Array.from({ length: 7_000 }, (_, i) => [`script${i}`, { command: "echo hi" }]), - ), - worktree: { setup: "npm ci" }, - }; - await writeFile(join(tempDir, "paseo.json"), JSON.stringify(config)); - const { result, blockingMs, filesystemRequests, yields } = await measureConfigRead(tempDir); - expect(result).toEqual({ ok: true, config, revision: await statPaseoConfigPath(tempDir) }); - expect(blockingMs).toBeLessThan(20); - expect(filesystemRequests).toBeGreaterThan(0); - expect(yields).toBeGreaterThan(0); - }); + it.each(Array.from({ length: 25 }, (_, i) => i))( + "keeps main-thread work below 20ms while admitting large configs (%i)", + async () => { + const config = { + scripts: Object.fromEntries( + Array.from({ length: 7_000 }, (_, i) => [`script${i}`, { command: "echo hi" }]), + ), + worktree: { setup: "npm ci" }, + }; + await writeFile(join(tempDir, "paseo.json"), JSON.stringify(config)); + const { result, blockingMs, filesystemRequests, yields } = await measureConfigRead(tempDir); + expect(result).toEqual({ ok: true, config, revision: await statPaseoConfigPath(tempDir) }); + expect(blockingMs).toBeLessThan(20); + expect(filesystemRequests).toBeGreaterThan(0); + expect(yields).toBeGreaterThan(0); + }, + ); it("rejects a 32MiB config with main-thread work below 20ms", async () => { await writeFile(