@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
JavaScript
;
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