import { Queue, type DefaultJobOptions } from 'bullmq' import queueConfig from '#config/queue' import gcConfig from '#config/gc' const defaultJobOptions: DefaultJobOptions = { // Transient NNTP errors (e.g. a pooled connection the server closed) are retried. attempts: 3, backoff: { type: 'exponential', delay: 2000 }, removeOnComplete: { age: gcConfig.jobs.completedAgeSeconds, count: gcConfig.jobs.completedMaxCount, }, removeOnFail: { age: gcConfig.jobs.failedAgeSeconds }, } /** * Queues are created (and connect to Redis) on first use. Ace imports every * command at boot, so eager queues would keep one-off commands from exiting. */ class QueueService { private readonly queues = new Map() private queue(name: string): Queue { let queue = this.queues.get(name) if (!queue) { queue = new Queue(name, { connection: queueConfig.connection, defaultJobOptions }) this.queues.set(name, queue) } return queue } get nntpFetchQueue() { return this.queue('nntp-fetch-queue') } get fileQueue() { return this.queue('file-queue') } get bodyQueue() { return this.queue('body-queue') } get collectionQueue() { return this.queue('collection-queue') } /** Complete archive listings, at low priority (ArchiveWalkService). */ get archiveWalkQueue() { return this.queue('archive-walk-queue') } /** Queues no longer used by the pipeline, kept here so GC can empty them. */ get legacy(): Queue[] { return [this.queue('header-queue')] } get all(): Queue[] { return [this.nntpFetchQueue, this.fileQueue, this.bodyQueue, this.collectionQueue, this.archiveWalkQueue] } /** Closes the queues that were opened. */ async closeAll() { await Promise.all([...this.queues.values()].map((queue) => queue.close())) this.queues.clear() } } export default new QueueService()