@sidequest/engine
Version:
@sidequest/engine is the core engine of SideQuest, a distributed background job processing system for Node.js and TypeScript.
96 lines (92 loc) • 3.38 kB
JavaScript
;
var core = require('@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) {
core.logger("Dispatcher").debug(`Queue ${queue.name} limit reached!`);
await this.sleep(this.sleepDelay);
continue;
}
const globalSlots = this.executorManager.availableSlotsGlobal();
if (globalSlots <= 0) {
core.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() {
core.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();
}
}
exports.Dispatcher = Dispatcher;
//# sourceMappingURL=dispatcher.cjs.map