@backgroundjs/core
Version:
An extendible background job queue for js/ts applications
97 lines • 3.9 kB
JavaScript
import { JobQueue } from "./job-queue";
/**
* DistributedJobQueue extends JobQueue to provide distributed processing
* capabilities across multiple instances/processes using Redis atomic operations.
*/
export class DistributedJobQueue extends JobQueue {
/**
* Create a distributed job queue
*
* @param storage - A storage implementation that supports atomic operations
* @param options - Configuration options
*/
constructor(storage, options = {}) {
super(storage, options);
this.jobTTL = 30; // seconds
this.queueName = "distributed-queue";
this.redisStorage = storage;
this.jobTTL = options.jobTTL || 30;
this.logging = options.logging || false;
this.queueName = options.name || "distributed-queue";
this.standAlone = options.standAlone ?? true;
}
/**
* Process jobs with distributed locking
* Override the parent's protected method
*/
async processNextBatch() {
try {
if (this.isStopping && this.logging) {
console.log(`[${this.queueName}] Stopping job queue ... skipping`);
}
if (this.activeJobs.size >= this.concurrency || this.isStopping) {
return;
}
const availableSlots = this.concurrency - this.activeJobs.size;
let jobsProcessed = 0;
for (let i = 0; i < availableSlots; i++) {
const job = await this.redisStorage.acquireNextJob(this.jobTTL);
if (!job) {
break; // No more jobs available
}
if (this.logging) {
console.log(`[${this.queueName}] Processing job:`, job);
console.log(`[${this.queueName}] Available handlers:`, Array.from(this.handlers.keys()));
console.log(`[${this.queueName}] Has handler for ${job.name}:`, this.handlers.has(job.name));
}
this.activeJobs.add(job.id);
// Create a promise for processing this job
this.processJob(job)
.catch((error) => {
console.error(`[${this.queueName}] Error processing job ${job.id}:`, error);
})
.finally(() => {
this.activeJobs.delete(job.id);
});
jobsProcessed++;
}
this.updatePollingInterval(jobsProcessed > 0);
}
catch (error) {
if (this.logging) {
console.error(`[${this.queueName}] Error in processNextBatch:`, error);
}
}
}
/**
* Process a single job with locking
* This is called by the parent class
*/
async processJob(job) {
try {
if (this.logging) {
console.log(`[${this.queueName}] Starting to process job ${job.id} (${job.name})`);
}
// Check if handler exists before trying to process
if (!this.handlers.has(job.name)) {
throw new Error(`Handler for job "${job.name}" not found`);
}
// Get the handler using the parent's processJob method
await super.processJob(job);
if (this.logging && job.repeat) {
console.log(`[${this.queueName}] Completed repeatable job ${job.id}`);
}
}
catch (error) {
// Log the error
if (this.logging) {
console.error(`[${this.queueName}] Error in processJob:`, error);
}
// Mark job as failed atomically
const errorMessage = error instanceof Error ? error.message : String(error);
await this.redisStorage.failJob(job.id, errorMessage);
throw error;
}
}
}
//# sourceMappingURL=distributed-job-queue.js.map