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.

83 lines (82 loc) 3.08 kB
import log4js from "log4js"; import { TimeUtils } from "./time-utils.js"; import { HashMap, Nil, option } from "scats"; import { MetricsService } from "application-metrics"; import { SystemClock } from "./clock.js"; const logger = log4js.getLogger("TasksAuxiliaryWorker"); export class TasksAuxiliaryWorker { tasksQueueDao; manageTasksQueueService; clock; workerTimer = null; metricsTimer = null; queuesCounts = HashMap.empty; constructor(tasksQueueDao, manageTasksQueueService, clock = new SystemClock()) { this.tasksQueueDao = tasksQueueDao; this.manageTasksQueueService = manageTasksQueueService; this.clock = clock; } start() { this.workerTimer = setInterval(() => { void this.runMaintenanceOnce(); }, TimeUtils.second * 30); void this.runMaintenanceOnce(); this.metricsTimer = setInterval(() => { void this.syncMetricsOnce(); }, TimeUtils.minute * 2); void this.syncMetricsOnce(); } async runMaintenanceOnce() { try { const failStalledPromise = this.tasksQueueDao .failStalled(this.clock.now()) .then((res) => { if (res.nonEmpty) { logger.info(`Processed stalled tasks: ${res.mkString(", ")}`); } }) .catch((e) => { logger.warn("Failed to process stalled tasks", e); }); const resetFailedPromise = this.tasksQueueDao.resetFailed().catch((e) => { logger.warn("Failed to reset failed tasks", e); }); const clearFinishedPromise = this.tasksQueueDao .clearFinished(undefined, this.clock.now()) .catch((e) => { logger.warn("Failed to clear finished tasks", e); }); await Promise.all([ failStalledPromise, resetFailedPromise, clearFinishedPromise, ]); } catch (e) { logger.warn("Failed to process stalled tasks", e); } } async syncMetricsOnce() { try { const tasksCounts = await this.manageTasksQueueService.tasksCount(); this.queuesCounts = tasksCounts.groupBy((c) => c.queueName); tasksCounts.foreach((c) => { MetricsService.gauge(`tasks_queue_${c.queueName}_${c.status}`.replace(/[^a-zA-Z0-9_:]/g, "_"), () => { return this.queuesCounts .get(c.queueName) .getOrElseValue(Nil) .find((x) => c.status === x.status) .map((c) => c.count) .getOrElseValue(0); }); }); } catch (e) { logger.warn("Failed to sync metrics", e); } } stop() { option(this.workerTimer).foreach((t) => clearInterval(t)); option(this.metricsTimer).foreach((t) => clearInterval(t)); } }