@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
JavaScript
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 };