UNPKG

@penkov/tasks_queue

Version:

A lightweight PostgreSQL-backed task queue system with scheduling, retries, backoff strategies, and priority handling. Designed for efficiency and observability in modern Node.js applications.

126 lines (125 loc) 5.34 kB
import { __decorate, __metadata } from "tslib"; import { Injectable, } from "@nestjs/common"; import { DiscoveryService, MetadataScanner } from "@nestjs/core"; import { mutable, option } from "scats"; import { DEFAULT_POOL, TasksPoolsService } from "./tasks-pools.service.js"; import { TASKS_QUEUE_SCHEDULED_TASK_METADATA, } from "./scheduled-task.decorator.js"; import { TASKS_QUEUE_WORKER_METADATA } from "./worker.decorator.js"; import { DecoratedMethodWorker } from "./decorated-method-worker.js"; let ScheduledTasksRegistrar = class ScheduledTasksRegistrar { discoveryService; metadataScanner; tasksPoolsService; scheduledMethods = new mutable.ArrayBuffer(); constructor(discoveryService, metadataScanner, tasksPoolsService) { this.discoveryService = discoveryService; this.metadataScanner = metadataScanner; this.tasksPoolsService = tasksPoolsService; } onModuleInit() { this.discoveryService.getProviders().forEach((wrapper) => { const instance = wrapper.instance; if (instance === undefined || instance === null || typeof instance !== "object") { return; } const prototype = Object.getPrototypeOf(instance); if (prototype === undefined || prototype === null) { return; } this.metadataScanner.scanFromPrototype(instance, prototype, (methodName) => { const method = prototype[methodName]; if (typeof method !== "function") { return; } const options = Reflect.getMetadata(TASKS_QUEUE_SCHEDULED_TASK_METADATA, method); if (options === undefined) { return; } this.assertWorkerDecoratorNotPresent(methodName, method); this.registerWorker(instance, methodName, options); this.scheduledMethods.append({ options, }); }); }); } async onApplicationBootstrap() { const schedulePromises = this.scheduledMethods.map((definition) => this.schedule(definition.options)).toArray; await Promise.all(schedulePromises); } assertWorkerDecoratorNotPresent(methodName, method) { const workerOptions = Reflect.getMetadata(TASKS_QUEUE_WORKER_METADATA, method); if (workerOptions !== undefined) { throw new Error(`@ScheduledTask cannot be combined with @Worker on method '${methodName}'`); } } registerWorker(instance, methodName, options) { const decoratedWorker = new DecoratedMethodWorker(instance, methodName); this.tasksPoolsService.registerWorker(options.queue, decoratedWorker, option(options.pool).getOrElseValue(DEFAULT_POOL)); } async schedule(options) { if (options.cron !== undefined) { const task = { name: options.name, queue: options.queue, cronExpression: options.cron, startAfter: options.startAfter, priority: options.priority, payload: options.payload, timeout: options.timeout, retries: options.retries, backoff: options.backoff, backoffType: options.backoffType, missedRunStrategy: options.missedRunStrategy, replaceExisting: options.replaceExisting, }; await this.tasksPoolsService.scheduleAtCron(task); return; } if (options.fixedRate !== undefined) { const task = { name: options.name, queue: options.queue, period: options.fixedRate, startAfter: options.startAfter, priority: options.priority, payload: options.payload, timeout: options.timeout, retries: options.retries, backoff: options.backoff, backoffType: options.backoffType, missedRunStrategy: options.missedRunStrategy, replaceExisting: options.replaceExisting, }; await this.tasksPoolsService.scheduleAtFixedRate(task); return; } if (options.fixedDelay === undefined) { throw new Error("@ScheduledTask requires one of: cron, fixedRate, fixedDelay"); } const task = { name: options.name, queue: options.queue, period: options.fixedDelay, startAfter: options.startAfter, priority: options.priority, payload: options.payload, timeout: options.timeout, retries: options.retries, backoff: options.backoff, backoffType: options.backoffType, missedRunStrategy: options.missedRunStrategy, replaceExisting: options.replaceExisting, }; await this.tasksPoolsService.scheduleAtFixedDelay(task); } }; ScheduledTasksRegistrar = __decorate([ Injectable(), __metadata("design:paramtypes", [DiscoveryService, MetadataScanner, TasksPoolsService]) ], ScheduledTasksRegistrar); export { ScheduledTasksRegistrar };