fix(scripts): bound memory fd ready output

This commit is contained in:
Vincent Koc
2026-05-28 09:05:44 +02:00
parent bd6a404aa3
commit 05f357b13b
2 changed files with 82 additions and 9 deletions

View File

@@ -6,6 +6,7 @@ import net from "node:net";
import os from "node:os";
import path from "node:path";
import process from "node:process";
import { pathToFileURL } from "node:url";
const ISSUE_FILE_COUNTS = [
["memory/transcripts", 9394],
@@ -23,6 +24,7 @@ const ISSUE_FILE_COUNTS = [
const ISSUE_MEMORY_FILE_COUNT = ISSUE_FILE_COUNTS.reduce((sum, [, count]) => sum + count, 0);
const DEFAULT_FILE_COUNT = 512;
const DEFAULT_MAX_WORKSPACE_REG_FDS = process.platform === "darwin" ? 8 : 64;
export const GATEWAY_READY_OUTPUT_MAX_CHARS = 128 * 1024;
const SKIP_GATEWAY_ENV = {
NODE_ENV: "test",
@@ -261,6 +263,19 @@ function writeConfig({ homeDir, workspaceDir, port, token }) {
return configPath;
}
export function updateGatewayReadyOutputState(
state,
chunk,
maxChars = GATEWAY_READY_OUTPUT_MAX_CHARS,
) {
const text = String(chunk);
const combined = `${state.tail ?? ""}${text}`;
return {
tail: combined.length > maxChars ? combined.slice(-maxChars) : combined,
readySeen: Boolean(state.readySeen || combined.includes("[gateway] ready")),
};
}
function runLsofForPid(pid) {
const result = spawnSync("lsof", ["-nP", "-p", String(pid)], {
encoding: "utf8",
@@ -329,17 +344,17 @@ function sampleFds({ label, pid, workspaceRealPath }) {
async function waitForGatewayReady({ child, port, logPath, timeoutMs }) {
const startedAt = Date.now();
let output = "";
let outputState = { tail: "", readySeen: false };
const append = (chunk) => {
const text = chunk.toString();
output += text;
outputState = updateGatewayReadyOutputState(outputState, text);
fs.appendFileSync(logPath, text);
};
child.stdout.on("data", append);
child.stderr.on("data", append);
while (Date.now() - startedAt < timeoutMs) {
if (output.includes("[gateway] ready") && findGatewayPid(port)) {
if (outputState.readySeen && findGatewayPid(port)) {
return;
}
if (child.exitCode !== null) {
@@ -545,9 +560,16 @@ async function main() {
}
}
main().catch((error) => {
console.error(
`[memory-fd-repro] failed: ${error instanceof Error ? error.message : String(error)}`,
);
process.exit(1);
});
function isMainModule() {
const entrypoint = process.argv[1];
return Boolean(entrypoint && import.meta.url === pathToFileURL(path.resolve(entrypoint)).href);
}
if (isMainModule()) {
main().catch((error) => {
console.error(
`[memory-fd-repro] failed: ${error instanceof Error ? error.message : String(error)}`,
);
process.exit(1);
});
}

View File

@@ -0,0 +1,51 @@
import { describe, expect, it } from "vitest";
import {
GATEWAY_READY_OUTPUT_MAX_CHARS,
updateGatewayReadyOutputState,
} from "../../scripts/check-memory-fd-repro.mjs";
describe("check-memory-fd-repro", () => {
it("bounds gateway readiness output while keeping newest logs", () => {
const first = updateGatewayReadyOutputState({ tail: "abc", readySeen: false }, "def", 8);
expect(first).toEqual({ tail: "abcdef", readySeen: false });
const second = updateGatewayReadyOutputState(first, "ghijkl", 8);
expect(second).toEqual({ tail: "efghijkl", readySeen: false });
expect(second.tail).toHaveLength(8);
expect(GATEWAY_READY_OUTPUT_MAX_CHARS).toBeGreaterThan(1024);
});
it("keeps readiness after a coalesced noisy chunk truncates the marker", () => {
const state = updateGatewayReadyOutputState(
{ tail: "", readySeen: false },
`[gateway] ready\n${"x".repeat(10_000)}`,
64,
);
expect(state.readySeen).toBe(true);
expect(state.tail).toHaveLength(64);
expect(state.tail).not.toContain("[gateway] ready");
});
it("recognizes readiness split across the existing tail and new chunk", () => {
const state = updateGatewayReadyOutputState(
{ tail: "[gateway] rea", readySeen: false },
"dy\n",
64,
);
expect(state.readySeen).toBe(true);
expect(state.tail).toBe("[gateway] ready\n");
});
it("preserves previous readiness once seen", () => {
const state = updateGatewayReadyOutputState(
{ tail: "old", readySeen: true },
"new output",
8,
);
expect(state.readySeen).toBe(true);
expect(state.tail).toBe("w output");
});
});