spb/autoscholar
Public
1import { resolve } from "node:path";2import { getEnv, syncTemplates } from "@autoscholar/core";3import { createLogger } from "@autoscholar/observability";4import { getStorage } from "@autoscholar/storage";5import { closeDb, getDb, runMigrationsIfRequested } from "./boot.js";6import { schedulerTick } from "./tick.js";78const log = createLogger("scheduler");9const env = getEnv();10const db = getDb();11const storage = getStorage(env.ARTIFACT_ROOT);12let stopping = false;13let inTick = false;1415async function loop() {16 await runMigrationsIfRequested(log);17 try {18 const synced = await syncTemplates(19 db,20 resolve(process.env.RESEARCH_TEMPLATES_DIR ?? "research/templates"),21 );22 if (synced.length) log.info({ synced }, "research templates synced");23 } catch (e) {24 log.warn({ err: (e as Error).message }, "template sync skipped");25 }26 log.info(27 {28 tickMs: env.SCHEDULER_TICK_MS,29 leaseTtl: env.LEASE_TTL_SECONDS,30 publishing: env.PUBLISHING_ENABLED,31 dispatch: env.DISPATCH_ENABLED,32 },33 "scheduler started",34 );35 while (!stopping) {36 inTick = true;37 try {38 const r = await schedulerTick(db, storage, log);39 if (40 r.expiredLeases.length ||41 r.requeued ||42 r.evaluated.length ||43 r.published.length ||44 r.timedOut.length ||45 r.errors.length46 )47 log.info(r, "tick");48 } catch (e) {49 log.error({ err: (e as Error).message }, "tick crashed");50 }51 inTick = false;52 await new Promise((r) => setTimeout(r, env.SCHEDULER_TICK_MS));53 }54}5556async function shutdown(sig: string) {57 log.info({ sig }, "shutting down");58 stopping = true;59 const start = Date.now();60 while (inTick && Date.now() - start < 20_000) await new Promise((r) => setTimeout(r, 100));61 await closeDb();62 process.exit(0);63}64process.on("SIGTERM", () => void shutdown("SIGTERM"));65process.on("SIGINT", () => void shutdown("SIGINT"));6667loop().catch((e) => {68 log.fatal({ err: (e as Error).message }, "scheduler died");69 process.exit(1);70});71