Skip to content

Commit 1d1fe41

Browse files
fix(clawsweeper): address review for automerge-openclaw-openclaw-85348 (1)
1 parent c7c1719 commit 1d1fe41

2 files changed

Lines changed: 80 additions & 1 deletion

File tree

packages/memory-host-sdk/src/host/embeddings-worker.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ const WORKER_UNSAFE_EXEC_ARGV_OPTION_PREFIXES = [
126126
"--inspect-port=",
127127
];
128128

129+
const WORKER_CLOSE_GRACE_MS = 250;
130+
129131
function resolveWorkerExecArgv(): string[] {
130132
const args: string[] = [];
131133
let skipNext = false;
@@ -183,9 +185,21 @@ class LocalEmbeddingWorkerClient {
183185
if (!child) {
184186
return;
185187
}
188+
let timeout: NodeJS.Timeout | undefined;
189+
const closeRequest = this.send({ type: "close" }).then(() => "closed" as const);
190+
const closeTimeout = new Promise<"timeout">((resolve) => {
191+
timeout = setTimeout(() => resolve("timeout"), WORKER_CLOSE_GRACE_MS);
192+
timeout.unref?.();
193+
});
186194
try {
187-
await this.send({ type: "close" });
195+
const result = await Promise.race([closeRequest, closeTimeout]);
196+
if (result === "timeout") {
197+
closeRequest.catch(() => {});
198+
}
188199
} finally {
200+
if (timeout) {
201+
clearTimeout(timeout);
202+
}
189203
this.shutdownChild();
190204
}
191205
}

packages/memory-host-sdk/src/host/embeddings.test.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,71 @@ process.on("message", (message) => {
378378
await expect(provider.close?.()).resolves.toBeUndefined();
379379
});
380380

381+
it("terminates the worker when close runs behind a pending request", async () => {
382+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-local-embedding-worker-"));
383+
const workerScript = path.join(tempDir, "worker.cjs");
384+
const embedStartedPath = path.join(tempDir, "embed-started");
385+
await fs.writeFile(
386+
workerScript,
387+
`
388+
const fs = require("node:fs");
389+
const embedStartedPath = ${JSON.stringify(embedStartedPath)};
390+
let busy = false;
391+
392+
process.on("message", (message) => {
393+
if (busy) {
394+
return;
395+
}
396+
if (message.type === "initialize") {
397+
process.send({ id: message.id, ok: true });
398+
return;
399+
}
400+
if (message.type === "embedQuery") {
401+
busy = true;
402+
fs.writeFileSync(embedStartedPath, "1");
403+
}
404+
});
405+
`,
406+
"utf8",
407+
);
408+
const provider = await createLocalEmbeddingWorkerProvider(
409+
{
410+
config: {} as never,
411+
provider: "local",
412+
model: "",
413+
fallback: "none",
414+
},
415+
{ workerScriptPath: workerScript },
416+
);
417+
418+
const embedPromise = provider.embedQuery("stuck");
419+
const embedError = embedPromise.then(
420+
() => undefined,
421+
(err) => err,
422+
);
423+
await expect
424+
.poll(async () => {
425+
try {
426+
await fs.access(embedStartedPath);
427+
return true;
428+
} catch {
429+
return false;
430+
}
431+
})
432+
.toBe(true);
433+
434+
const closePromise = provider.close?.() ?? Promise.resolve();
435+
const closeResult = await Promise.race([
436+
closePromise.then(() => "closed" as const),
437+
new Promise<"timeout">((resolve) => setTimeout(() => resolve("timeout"), 1_000)),
438+
]);
439+
440+
expect(closeResult).toBe("closed");
441+
await expect(embedError).resolves.toMatchObject({
442+
code: LOCAL_EMBEDDING_WORKER_ERROR_CODES.exited,
443+
});
444+
});
445+
381446
it("does not pass inline-source or inspector exec args to the file-backed worker", async () => {
382447
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-local-embedding-worker-"));
383448
const workerScript = path.join(tempDir, "worker.cjs");

0 commit comments

Comments
 (0)