From 001ce5dfa76d83e45ab9e6842d8cc49a6978c57d Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Sat, 26 Jul 2025 22:32:23 +1000 Subject: [PATCH] Try creating a new worker for each request --- deno.jsonc | 2 +- deno_server.ts | 67 ++++++++++---------------------------------------- 2 files changed, 14 insertions(+), 55 deletions(-) diff --git a/deno.jsonc b/deno.jsonc index c4b5fe9..925fe15 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,6 +1,6 @@ { "compilerOptions": { - "lib": ["deno.window", "deno.worker"] + "lib": ["deno.window", "deno.worker", "deno.ns"] }, "lint": { "files": { diff --git a/deno_server.ts b/deno_server.ts index 7da90a5..56fcb8e 100644 --- a/deno_server.ts +++ b/deno_server.ts @@ -6,13 +6,13 @@ const States = Object.freeze({ TIMEOUT: "TIMEOUT", }); -// Create a pool of workers. -const workerPool: { worker: Worker; inUse: boolean }[] = []; -// Allow the number of workers to be configured via an environment variable. -const numWorkers = - parseInt(Deno.env.get("NUM_WORKERS") || "0") || navigator.hardwareConcurrency; +async function handler(req: Request): Promise { + if (req.method !== "POST" || new URL(req.url).pathname !== "/execute") { + return new Response("Not Found", { status: 404 }); + } -for (let i = 0; i < numWorkers; i++) { + // For each request, spawn a new worker, use it, and then terminate it. + // This ensures a clean, stateless environment for every execution. const worker = new Worker(new URL("./worker.ts", import.meta.url).href, { type: "module", deno: { @@ -20,47 +20,6 @@ for (let i = 0; i < numWorkers; i++) { permissions: "inherit", }, }); - workerPool.push({ worker, inUse: false }); -} - -const requestQueue: (( - value: - | { worker: Worker; inUse: boolean } - | PromiseLike<{ worker: Worker; inUse: boolean }> -) => void)[] = []; - -function getAvailableWorker() { - return new Promise((resolve) => { - const availableWorker = workerPool.find((w) => !w.inUse); - if (availableWorker) { - availableWorker.inUse = true; - resolve(availableWorker); - } else { - requestQueue.push(resolve); - } - }); -} - -function releaseWorker(worker) { - if (requestQueue.length > 0) { - const nextRequest = requestQueue.shift(); - if (nextRequest) { - nextRequest(worker); - } - } else { - worker.inUse = false; - } -} - -async function handler(req: Request): Promise { - if (req.method !== "POST" || new URL(req.url).pathname !== "/execute") { - return new Response("Not Found", { status: 404 }); - } - - const availableWorker = (await getAvailableWorker()) as { - worker: Worker; - inUse: boolean; - }; try { const body = await req.json(); @@ -75,8 +34,8 @@ async function handler(req: Request): Promise { let timeoutId: number; const cleanup = () => { - availableWorker.worker.removeEventListener("message", messageHandler); - availableWorker.worker.removeEventListener("error", errorHandler); + worker.removeEventListener("message", messageHandler); + worker.removeEventListener("error", errorHandler); if (timeoutId) clearTimeout(timeoutId); }; @@ -95,10 +54,10 @@ async function handler(req: Request): Promise { reject(new Error("Timeout")); }, TIMEOUT_MS); - availableWorker.worker.addEventListener("message", messageHandler); - availableWorker.worker.addEventListener("error", errorHandler); + worker.addEventListener("message", messageHandler); + worker.addEventListener("error", errorHandler); - availableWorker.worker.postMessage({ code, request, environment, name }); + worker.postMessage({ code, request, environment, name }); }); const startTime = performance.now(); @@ -124,12 +83,12 @@ async function handler(req: Request): Promise { } catch (e) { return new Response(`Bad Request: ${e.message}`, { status: 400 }); } finally { - releaseWorker(availableWorker); // Release the worker. + worker.terminate(); // Ensure the worker is always terminated. } } const port = parseInt(Deno.env.get("PORT") || "8000"); -console.log(`⚡ Deno server ready on :${port} with ${numWorkers} workers.`); +console.log(`⚡ Deno server ready on :${port}`); await serve(handler, { port });