Vectrum
Sign In
31 lines · 1.3 KB · TypeScript
import { json, originOf, readBody, runnerFromRequest } from "@/lib/runner-api";
import { touchRunner } from "@/lib/runners";
import { claimJob } from "@/lib/runs";

/** How long a claim waits for a job to turn up before answering "nothing yet". */
const MAX_WAIT_MS = 25_000;

const sleep = (ms: number, signal: AbortSignal) =>
  new Promise<void>((resolve) => {
    const timer = setTimeout(resolve, ms);
    signal.addEventListener("abort", () => (clearTimeout(timer), resolve()), { once: true });
  });

/** A runner asking for work: answers with a job as soon as one it can run is queued (long polling). */
export async function POST(request: Request) {
  const info = await readBody(request);
  const origin = originOf(request);
  const deadline = Date.now() + MAX_WAIT_MS;

  for (;;) {
    // Checked every round: the runner's labels may be edited, or the runner removed, while it waits.
    const runner = await runnerFromRequest(request);
    if (runner instanceof Response) return runner;
    await touchRunner(runner.id, info);
    if (request.signal.aborted) return new Response(null, { status: 204 });
    const job = await claimJob(runner, origin);
    if (job) return json({ job });
    if (Date.now() >= deadline) return new Response(null, { status: 204 });
    await sleep(1000, request.signal);
  }
}