mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-14 01:50:39 +09:00
Persist heartbeat child pid before stdin handoff
This commit is contained in:
parent
3baebee2df
commit
26d4cabb2e
2 changed files with 53 additions and 10 deletions
38
packages/adapter-utils/src/server-utils.test.ts
Normal file
38
packages/adapter-utils/src/server-utils.test.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { randomUUID } from "node:crypto";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { runChildProcess } from "./server-utils.js";
|
||||
|
||||
describe("runChildProcess", () => {
|
||||
it("waits for onSpawn before sending stdin to the child", async () => {
|
||||
const spawnDelayMs = 150;
|
||||
const startedAt = Date.now();
|
||||
let onSpawnCompletedAt = 0;
|
||||
|
||||
const result = await runChildProcess(
|
||||
randomUUID(),
|
||||
process.execPath,
|
||||
[
|
||||
"-e",
|
||||
"let data='';process.stdin.setEncoding('utf8');process.stdin.on('data',chunk=>data+=chunk);process.stdin.on('end',()=>process.stdout.write(data));",
|
||||
],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
env: {},
|
||||
stdin: "hello from stdin",
|
||||
timeoutSec: 5,
|
||||
graceSec: 1,
|
||||
onLog: async () => {},
|
||||
onSpawn: async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, spawnDelayMs));
|
||||
onSpawnCompletedAt = Date.now();
|
||||
},
|
||||
},
|
||||
);
|
||||
const finishedAt = Date.now();
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.stdout).toBe("hello from stdin");
|
||||
expect(onSpawnCompletedAt).toBeGreaterThanOrEqual(startedAt + spawnDelayMs);
|
||||
expect(finishedAt - startedAt).toBeGreaterThanOrEqual(spawnDelayMs);
|
||||
});
|
||||
});
|
||||
|
|
@ -1069,16 +1069,12 @@ export async function runChildProcess(
|
|||
}) as ChildProcessWithEvents;
|
||||
const startedAt = new Date().toISOString();
|
||||
|
||||
if (opts.stdin != null && child.stdin) {
|
||||
child.stdin.write(opts.stdin);
|
||||
child.stdin.end();
|
||||
}
|
||||
|
||||
if (typeof child.pid === "number" && child.pid > 0 && opts.onSpawn) {
|
||||
void opts.onSpawn({ pid: child.pid, startedAt }).catch((err) => {
|
||||
onLogError(err, runId, "failed to record child process metadata");
|
||||
});
|
||||
}
|
||||
const spawnPersistPromise =
|
||||
typeof child.pid === "number" && child.pid > 0 && opts.onSpawn
|
||||
? opts.onSpawn({ pid: child.pid, startedAt }).catch((err) => {
|
||||
onLogError(err, runId, "failed to record child process metadata");
|
||||
})
|
||||
: Promise.resolve();
|
||||
|
||||
runningProcesses.set(runId, { child, graceSec: opts.graceSec });
|
||||
|
||||
|
|
@ -1116,6 +1112,15 @@ export async function runChildProcess(
|
|||
.catch((err) => onLogError(err, runId, "failed to append stderr log chunk"));
|
||||
});
|
||||
|
||||
const stdin = child.stdin;
|
||||
if (opts.stdin != null && stdin) {
|
||||
void spawnPersistPromise.finally(() => {
|
||||
if (child.killed || stdin.destroyed) return;
|
||||
stdin.write(opts.stdin as string);
|
||||
stdin.end();
|
||||
});
|
||||
}
|
||||
|
||||
child.on("error", (err: Error) => {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
runningProcesses.delete(runId);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue