UNPKG

@sidequest/engine

Version:

@sidequest/engine is the core engine of SideQuest, a distributed background job processing system for Node.js and TypeScript.

154 lines (150 loc) 6.77 kB
'use strict'; var core = require('@sidequest/core'); var nodeCron = require('node-cron'); var engine = require('../engine.cjs'); var dispatcher = require('../execution/dispatcher.cjs'); var executorManager = require('../execution/executor-manager.cjs'); var queueManager = require('../execution/queue-manager.cjs'); var cleanupFinishedJob = require('../routines/cleanup-finished-job.cjs'); var releaseStaleJobs = require('../routines/release-stale-jobs.cjs'); var shutdown = require('../utils/shutdown.cjs'); class MainWorker { shuttingDown = false; dispatcher; engine = new engine.Engine(); backend; /** * Starts a Sidequest worker process with the given configuration. * @param sidequestConfig The Sidequest configuration for the worker. */ async runWorker(sidequestConfig) { if (!this.shuttingDown) { try { const nonNullConfig = await this.engine.configure(sidequestConfig); this.backend = this.engine.getBackend(); this.dispatcher = new dispatcher.Dispatcher(this.backend, new queueManager.QueueManager(this.backend, nonNullConfig.queues, nonNullConfig.queueDefaults), new executorManager.ExecutorManager(this.backend, nonNullConfig)); this.dispatcher.start(); await this.startCron(nonNullConfig.releaseStaleJobsIntervalMin, nonNullConfig.releaseStaleJobsMaxStaleMs, nonNullConfig.releaseStaleJobsMaxClaimedMs, nonNullConfig.cleanupFinishedJobsIntervalMin, nonNullConfig.cleanupFinishedJobsOlderThan); } catch (error) { core.logger("Worker").error(error); process.exit(1); } } else { core.logger("Worker").warn("Worker is already shutting down, ignoring run signal."); } } /** * Gracefully shuts down the worker and releases resources. */ async shutdown() { if (!this.shuttingDown) { this.shuttingDown = true; await this.dispatcher?.stop(); await this.engine.close(); } } /** * Starts cron job for releasing stale jobs. * Also executes the task immediately. */ async startAndExecuteStaleJobsReleaseCron(intervalMin, maxStaleMs, maxClaimedMs) { if (!this.backend) { throw new Error("Backend is not initialized. Cannot start stale jobs release cron."); } core.logger("Worker").debug(`Starting stale jobs release cron with interval: ${intervalMin} minutes`); const releaseTask = nodeCron.schedule(`*/${intervalMin} * * * *`, async () => { try { core.logger("Worker").debug("Running stale jobs release task"); await releaseStaleJobs.releaseStaleJobs(this.backend, maxStaleMs, maxClaimedMs); } catch (error) { core.logger("Worker").error("Error on running ReleaseStaleJob!", error); } }); return releaseTask.execute(); } /** * Starts cron job for cleaning up finished jobs. * Also executes the task immediately. */ async startAndExecuteFinishedJobsCleanupCron(intervalMin, cutoffMs) { if (!this.backend) { throw new Error("Backend is not initialized. Cannot start finished jobs cleanup cron."); } core.logger("Worker").debug(`Starting finished jobs cleanup cron with interval: ${intervalMin} minutes`); const cleanupTask = nodeCron.schedule(`*/${intervalMin} * * * *`, async () => { try { core.logger("Worker").debug("Running finished jobs cleanup task"); await cleanupFinishedJob.cleanupFinishedJobs(this.backend, cutoffMs); } catch (error) { core.logger("Worker").error("Error on running CleanupJob!", error); } }); return cleanupTask.execute(); } /** * Starts cron jobs for releasing stale jobs and cleaning up finished jobs. * * @param staleIntervalMin Interval in minutes for releasing stale jobs, or false to disable. * @param maxStaleMs Maximum age in milliseconds for stale jobs. * @param maxClaimedMs Maximum age in milliseconds for claimed jobs. * @param cleanupIntervalMin Interval in minutes for cleaning up finished jobs, or false to disable * @param cleanupCutoffMs Maximum age in milliseconds for finished jobs to be cleaned up. */ async startCron(staleIntervalMin, maxStaleMs, maxClaimedMs, cleanupIntervalMin, cleanupCutoffMs) { core.logger("Worker").debug("Starting cron jobs"); const promises = []; if (staleIntervalMin !== false) { promises.push(this.startAndExecuteStaleJobsReleaseCron(staleIntervalMin, maxStaleMs, maxClaimedMs)); } if (cleanupIntervalMin !== false) { promises.push(this.startAndExecuteFinishedJobsCleanupCron(cleanupIntervalMin, cleanupCutoffMs)); } await Promise.all(promises).catch((error) => { core.logger("Worker").error(error); }); } } const isChildProcess = !!process.send; if (isChildProcess) { const worker = new MainWorker(); process.on("message", // eslint-disable-next-line @typescript-eslint/no-misused-promises async ({ type, sidequestConfig }) => { if (type === "start") { if (!sidequestConfig) { throw new Error("No Sidequest configuration provided to worker!"); } if (!worker.shuttingDown) { shutdown.gracefulShutdown(worker.shutdown.bind(worker), "Worker", sidequestConfig.gracefulShutdown); core.logger("Worker").info("Starting worker with provided configuration..."); return await worker.runWorker(sidequestConfig); } else { core.logger("Worker").warn("Worker is already shutting down, ignoring start signal."); } } else if (type === "shutdown") { if (!worker.shuttingDown) { core.logger("Worker").info("Received shutdown message, shutting down worker..."); await worker.shutdown(); core.logger("Worker").info("Worker shutdown complete."); process.exit(0); } else { core.logger("Worker").debug("Worker is already shutting down, ignoring shutdown signal."); } } }); process.on("disconnect", () => { core.logger("Worker").error("Parent process disconnected, exiting..."); process.exit(); }); if (process.send) process.send("ready"); } exports.MainWorker = MainWorker; //# sourceMappingURL=main.cjs.map