Skip to content

Commit 05f357b

Browse files
committed
fix(scripts): bound memory fd ready output
1 parent bd6a404 commit 05f357b

2 files changed

Lines changed: 82 additions & 9 deletions

File tree

scripts/check-memory-fd-repro.mjs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import net from "node:net";
66
import os from "node:os";
77
import path from "node:path";
88
import process from "node:process";
9+
import { pathToFileURL } from "node:url";
910

1011
const ISSUE_FILE_COUNTS = [
1112
["memory/transcripts", 9394],
@@ -23,6 +24,7 @@ const ISSUE_FILE_COUNTS = [
2324
const ISSUE_MEMORY_FILE_COUNT = ISSUE_FILE_COUNTS.reduce((sum, [, count]) => sum + count, 0);
2425
const DEFAULT_FILE_COUNT = 512;
2526
const DEFAULT_MAX_WORKSPACE_REG_FDS = process.platform === "darwin" ? 8 : 64;
27+
export const GATEWAY_READY_OUTPUT_MAX_CHARS = 128 * 1024;
2628

2729
const SKIP_GATEWAY_ENV = {
2830
NODE_ENV: "test",
@@ -261,6 +263,19 @@ function writeConfig({ homeDir, workspaceDir, port, token }) {
261263
return configPath;
262264
}
263265

266+
export function updateGatewayReadyOutputState(
267+
state,
268+
chunk,
269+
maxChars = GATEWAY_READY_OUTPUT_MAX_CHARS,
270+
) {
271+
const text = String(chunk);
272+
const combined = `${state.tail ?? ""}${text}`;
273+
return {
274+
tail: combined.length > maxChars ? combined.slice(-maxChars) : combined,
275+
readySeen: Boolean(state.readySeen || combined.includes("[gateway] ready")),
276+
};
277+
}
278+
264279
function runLsofForPid(pid) {
265280
const result = spawnSync("lsof", ["-nP", "-p", String(pid)], {
266281
encoding: "utf8",
@@ -329,17 +344,17 @@ function sampleFds({ label, pid, workspaceRealPath }) {
329344

330345
async function waitForGatewayReady({ child, port, logPath, timeoutMs }) {
331346
const startedAt = Date.now();
332-
let output = "";
347+
let outputState = { tail: "", readySeen: false };
333348
const append = (chunk) => {
334349
const text = chunk.toString();
335-
output += text;
350+
outputState = updateGatewayReadyOutputState(outputState, text);
336351
fs.appendFileSync(logPath, text);
337352
};
338353
child.stdout.on("data", append);
339354
child.stderr.on("data", append);
340355

341356
while (Date.now() - startedAt < timeoutMs) {
342-
if (output.includes("[gateway] ready") && findGatewayPid(port)) {
357+
if (outputState.readySeen && findGatewayPid(port)) {
343358
return;
344359
}
345360
if (child.exitCode !== null) {
@@ -545,9 +560,16 @@ async function main() {
545560
}
546561
}
547562

548-
main().catch((error) => {
549-
console.error(
550-
`[memory-fd-repro] failed: ${error instanceof Error ? error.message : String(error)}`,
551-
);
552-
process.exit(1);
553-
});
563+
function isMainModule() {
564+
const entrypoint = process.argv[1];
565+
return Boolean(entrypoint && import.meta.url === pathToFileURL(path.resolve(entrypoint)).href);
566+
}
567+
568+
if (isMainModule()) {
569+
main().catch((error) => {
570+
console.error(
571+
`[memory-fd-repro] failed: ${error instanceof Error ? error.message : String(error)}`,
572+
);
573+
process.exit(1);
574+
});
575+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { describe, expect, it } from "vitest";
2+
import {
3+
GATEWAY_READY_OUTPUT_MAX_CHARS,
4+
updateGatewayReadyOutputState,
5+
} from "../../scripts/check-memory-fd-repro.mjs";
6+
7+
describe("check-memory-fd-repro", () => {
8+
it("bounds gateway readiness output while keeping newest logs", () => {
9+
const first = updateGatewayReadyOutputState({ tail: "abc", readySeen: false }, "def", 8);
10+
expect(first).toEqual({ tail: "abcdef", readySeen: false });
11+
12+
const second = updateGatewayReadyOutputState(first, "ghijkl", 8);
13+
expect(second).toEqual({ tail: "efghijkl", readySeen: false });
14+
expect(second.tail).toHaveLength(8);
15+
expect(GATEWAY_READY_OUTPUT_MAX_CHARS).toBeGreaterThan(1024);
16+
});
17+
18+
it("keeps readiness after a coalesced noisy chunk truncates the marker", () => {
19+
const state = updateGatewayReadyOutputState(
20+
{ tail: "", readySeen: false },
21+
`[gateway] ready\n${"x".repeat(10_000)}`,
22+
64,
23+
);
24+
25+
expect(state.readySeen).toBe(true);
26+
expect(state.tail).toHaveLength(64);
27+
expect(state.tail).not.toContain("[gateway] ready");
28+
});
29+
30+
it("recognizes readiness split across the existing tail and new chunk", () => {
31+
const state = updateGatewayReadyOutputState(
32+
{ tail: "[gateway] rea", readySeen: false },
33+
"dy\n",
34+
64,
35+
);
36+
37+
expect(state.readySeen).toBe(true);
38+
expect(state.tail).toBe("[gateway] ready\n");
39+
});
40+
41+
it("preserves previous readiness once seen", () => {
42+
const state = updateGatewayReadyOutputState(
43+
{ tail: "old", readySeen: true },
44+
"new output",
45+
8,
46+
);
47+
48+
expect(state.readySeen).toBe(true);
49+
expect(state.tail).toBe("w output");
50+
});
51+
});

0 commit comments

Comments
 (0)