@saas-packages/queue-manager
Version:
Queue manager for SaaS applications using BullMQ
148 lines • 5.82 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
import { Queue, Worker, DelayedError } from 'bullmq';
import Redis from 'ioredis';
import { injectable, inject } from '@saas-packages/core';
import { EventEmitter } from 'events';
let QueueManager = class QueueManager extends EventEmitter {
config;
logger;
queues = new Map();
workers = new Map();
redis;
constructor(config, logger) {
super();
this.config = config;
this.logger = logger;
this.redis =
this.config.connection instanceof Redis
? this.config.connection.duplicate({ maxRetriesPerRequest: null })
: new Redis(this.config.connection);
this.logger?.info('QueueManager initialized');
}
subscribe(event, listener) {
this.on(event, listener);
this.notify('newListener', event, listener);
}
unsubscribe(event, listener) {
this.off(event, listener);
this.notify('removeListener', event, listener);
}
notify(event, ...data) {
this.emit(event, ...data);
}
createQueue(name, options) {
if (this.queues.has(name)) {
this.logger?.warn(`Queue ${name} already exists, returning existing queue`);
return this.queues.get(name);
}
const queue = new Queue(name, {
...options,
connection: this.redis,
});
this.queues.set(name, queue);
this.logger?.info(`Queue ${name} created`);
this.notify('queueCreated', queue);
return queue;
}
createWorker(queueName, processor, options) {
if (this.workers.has(queueName)) {
this.logger?.warn(`Worker for queue ${queueName} already exists, returning existing worker`);
return this.workers.get(queueName);
}
const workerOptions = {
connection: this.redis,
...options,
};
const wrappedProcessor = async (job, token) => {
try {
const result = await processor.process(job, token);
if (result.delay) {
job.moveToDelayed(Date.now() + result.delay, token);
const message = `Job ${job.id} moved to delay queue with ${result.delay}ms delay`;
job.log(message);
throw new DelayedError(message);
}
return result;
}
catch (error) {
this.logger?.error(`Error processing job ${job.id}:`, error);
throw error;
}
};
const worker = new Worker(queueName, wrappedProcessor, workerOptions);
this.workers.set(queueName, worker);
this.logger?.info(`Worker for queue ${queueName} created`);
this.notify('workerCreated', worker);
return worker;
}
async addJob(queueName, data, options) {
const queue = this.getQueue(queueName);
if (!queue) {
throw new Error(`Queue ${queueName} not found. Create it first using createQueue()`);
}
const jobOptions = {
...this.config.defaultJobOptions,
...options,
};
const job = await queue.add(queueName, data, jobOptions);
this.logger?.info(`Job added to queue ${queueName} with ID ${job.id}`);
return job;
}
getAllQueues() {
return Array.from(this.queues.values());
}
getQueue(name) {
return this.queues.get(name);
}
getAllWorkers() {
return Array.from(this.workers.values());
}
getWorker(queueName) {
return this.workers.get(queueName);
}
async closeQueue(name) {
const queue = this.queues.get(name);
if (queue) {
await queue.close();
this.queues.delete(name);
this.logger?.info(`Queue ${name} closed`);
this.notify('queueRemoved', queue);
}
}
async closeWorker(queueName) {
const worker = this.workers.get(queueName);
if (worker) {
await worker.close();
this.workers.delete(queueName);
this.logger?.info(`Worker for queue ${queueName} closed`);
this.notify('workerRemoved', worker);
}
}
async closeAll() {
const queuePromises = Array.from(this.queues.keys()).map(name => this.closeQueue(name));
const workerPromises = Array.from(this.workers.keys()).map(name => this.closeWorker(name));
await Promise.all([...queuePromises, ...workerPromises]);
await this.redis.quit();
this.logger?.info('All queues and workers closed');
this.notify('queueManagerClosed');
}
};
QueueManager = __decorate([
injectable(),
__param(0, inject('queue.config')),
__param(1, inject('queue.logger')),
__metadata("design:paramtypes", [Object, Object])
], QueueManager);
export { QueueManager };
//# sourceMappingURL=queue-manager.js.map