UNPKG

@saas-packages/queue-manager

Version:

Queue manager for SaaS applications using BullMQ

167 lines 6.71 kB
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, } 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.moveToDelay) { const targetQueueName = result.moveToDelay.queueName || queueName; const targetQueue = this.getQueue(targetQueueName); if (!targetQueue) { throw new Error(`Target queue ${targetQueueName} not found for moveToDelay`); } const jobOptions = { delay: result.moveToDelay.delay, ...job.opts, }; if (job.id) { jobOptions.jobId = job.id; } await targetQueue.add(job.name, job.data, jobOptions); job.log(`Job ${job.id} moved to delay queue ${targetQueueName} with ${result.moveToDelay.delay}ms delay`); return { success: true, data: { movedToDelay: true, targetQueue: targetQueueName, delay: result.moveToDelay.delay, originalResult: result.data, }, }; } 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