UNPKG

@sidequest/engine

Version:

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

94 lines (91 loc) 3.34 kB
import { logger } from '@sidequest/core'; /** * Dispatcher for managing job execution and queue polling. */ class Dispatcher { backend; queueManager; executorManager; sleepDelay; /** Indicates if the dispatcher is currently running */ isRunning = false; /** * Creates a new Dispatcher. * @param backend The backend instance. * @param queueManager The queue manager instance. * @param executorManager The executor manager instance. */ constructor(backend, queueManager, executorManager, sleepDelay) { this.backend = backend; this.queueManager = queueManager; this.executorManager = executorManager; this.sleepDelay = sleepDelay; } /** * Main loop for polling queues and dispatching jobs. * @private */ async listen() { while (this.isRunning) { const queues = await this.queueManager.getActiveQueuesWithRunnableJobs(); let shouldSleep = true; for (const queue of queues) { const availableSlots = this.executorManager.availableSlotsByQueue(queue); if (availableSlots <= 0) { logger("Dispatcher").debug(`Queue ${queue.name} limit reached!`); await this.sleep(this.sleepDelay); continue; } const globalSlots = this.executorManager.availableSlotsGlobal(); if (globalSlots <= 0) { logger("Dispatcher").debug(`Global concurrency limit reached!`); await this.sleep(this.sleepDelay); continue; } const jobs = await this.backend.claimPendingJob(queue.name, Math.min(availableSlots, globalSlots)); if (jobs.length > 0) { // if a job was found on any queue do not sleep shouldSleep = false; } for (const job of jobs) { // adds jobs to active sets before execution to avoid race conditions // because the execution is not awaited. This way we ensure that available slots // are correctly calculated. this.executorManager.queueJob(queue, job); // does not await for job execution. void this.executorManager.execute(queue, job); } } if (shouldSleep) { await this.sleep(this.sleepDelay); } } } /** * Sleeps for the given delay in milliseconds. * @param delay The delay in milliseconds. * @returns A promise that resolves after the delay. * @private */ sleep(delay) { return new Promise((r) => setTimeout(r, delay)); } /** * Starts the dispatcher loop. */ start() { logger("Dispatcher").debug(`Starting dispatcher...`); this.isRunning = true; void this.listen(); } /** * Stops the dispatcher and waits for all active jobs to finish. * @returns A promise that resolves when all jobs are finished. */ async stop() { this.isRunning = false; await this.executorManager.destroy(); } } export { Dispatcher }; //# sourceMappingURL=dispatcher.js.map