Skip to content

Commit 0ebe5b8

Browse files
test: prove page yielding by loop turns and stall ratio instead of a wall-clock budget
The compartment-drop pagination test asserted a 100 ms maximum event-loop gap while scanning 100k parts; on a shared CI runner under parallel test load the yielding traversal measured 136 ms and failed release run r3 (8 ms on a quiet box). The mechanism under test is that the traversal yields between pages, so the test now counts event-loop turns during the scan (>= pages - 1; a non-yielding scan yields 0 and fails) and bounds the longest stall relative to the scan's own duration, which scales with load instead of racing it. Co-authored-by: Alfonso <alfonso-magic-context@users.noreply.github.com>
1 parent 13c9c60 commit 0ebe5b8

1 file changed

Lines changed: 29 additions & 6 deletions

File tree

‎packages/plugin/src/hooks/magic-context/compartment-drop-pagination.test.ts‎

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ describe("compartment drop tag-key pagination", () => {
233233
expect(helperSource).not.toContain("readRawSessionMessages(sessionId)");
234234
});
235235

236-
it("keeps event-loop timer gaps within 100 ms while scanning 100k SQLite parts", async () => {
236+
it("yields to the event loop between pages while scanning 100k SQLite parts", async () => {
237237
const dir = mkdtempSync(join(tmpdir(), "compartment-drop-responsive-"));
238238
tempDirs.push(dir);
239239
const dbPath = join(dir, "opencode.db");
@@ -305,28 +305,51 @@ describe("compartment drop tag-key pagination", () => {
305305
closeReadOnlySessionDb();
306306
resetOpenCodeDbPathStateForTesting();
307307

308+
// Two load-invariant measurements replace an absolute wall-clock budget, which a
309+
// shared CI runner under parallel test load cannot honor (136 ms observed against
310+
// a 100 ms budget with the yielding traversal in place). (1) A self-rescheduling
311+
// immediate counts event-loop turns during the scan: a traversal that yields
312+
// between its 32 pages hands the loop at least 31 turns; a non-yielding scan hands
313+
// it none. (2) The longest stall is compared with the scan's own duration, so
314+
// both scale together under load: a non-yielding scan stalls for the whole scan.
308315
const tickTimes: number[] = [];
309316
const timer = setInterval(() => tickTimes.push(performance.now()), 5);
317+
let loopTurns = 0;
318+
let counting = true;
319+
const countTurns = () => {
320+
if (!counting) return;
321+
loopTurns += 1;
322+
setImmediate(countTurns);
323+
};
310324
try {
311325
await delay(20);
326+
loopTurns = 0;
327+
setImmediate(countTurns);
312328
const scanStartedAt = performance.now();
313329
await getRawSessionTagKeysThrough(sessionId, messageCount, { pageSize: 32 });
314330
const scanFinishedAt = performance.now();
331+
counting = false;
315332
await delay(20);
316333

317-
const observedTimes = [scanStartedAt, ...tickTimes, scanFinishedAt].sort(
318-
(left, right) => left - right,
319-
);
334+
const pages = Math.ceil(messageCount / 32);
335+
expect(loopTurns).toBeGreaterThanOrEqual(pages - 1);
336+
337+
const observedTimes = [
338+
scanStartedAt,
339+
...tickTimes.filter((t) => t >= scanStartedAt && t <= scanFinishedAt),
340+
scanFinishedAt,
341+
].sort((left, right) => left - right);
320342
let maxGapMs = 0;
321343
for (let index = 1; index < observedTimes.length; index += 1) {
322344
maxGapMs = Math.max(
323345
maxGapMs,
324346
(observedTimes[index] ?? 0) - (observedTimes[index - 1] ?? 0),
325347
);
326348
}
327-
expect(tickTimes.length).toBeGreaterThan(2);
328-
expect(maxGapMs).toBeLessThanOrEqual(100);
349+
const scanDurationMs = scanFinishedAt - scanStartedAt;
350+
expect(maxGapMs).toBeLessThanOrEqual(Math.max(scanDurationMs / 4, 20));
329351
} finally {
352+
counting = false;
330353
clearInterval(timer);
331354
}
332355
}, 30_000);

0 commit comments

Comments
 (0)