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.

100 lines (99 loc) 3.25 kB
import log4js from "log4js"; import { TimeUtils } from "./time-utils.js"; import { HashMap, Nil, option } from "scats"; import { MetricsService } from "application-metrics"; const logger = log4js.getLogger("TasksAuxiliaryWorker"); export class TasksAuxiliaryWorker { tasksQueueDao; manageTasksQueueService; workerTimer = null; metricsTimer = null; queuesCounts = HashMap.empty; constructor(tasksQueueDao, manageTasksQueueService) { this.tasksQueueDao = tasksQueueDao; this.manageTasksQueueService = manageTasksQueueService; } start() { const runWorker = () => { this.runAuxiliaryJobs(); }; this.workerTimer = setInterval(() => { try { runWorker(); } catch (e) { logger.warn("Failed to process stalled tasks", e); } }, TimeUtils.second * 30); try { runWorker(); } catch (e) { logger.warn("Failed to process stalled tasks", e); } const runMetrics = () => { this.fetchMetrics(); }; this.metricsTimer = setInterval(() => { try { runMetrics(); } catch (e) { logger.warn("Failed to sync metrics", e); } }, TimeUtils.minute * 2); try { runMetrics(); } catch (e) { logger.warn("Failed to sync metrics", e); } } runAuxiliaryJobs() { try { this.tasksQueueDao .failStalled() .then((res) => { if (res.nonEmpty) { logger.info(`Marked stalled as failed: ${res.mkString(", ")}`); } }) .catch((e) => { logger.warn("Failed to process stalled tasks", e); }); this.tasksQueueDao.resetFailed().catch((e) => { logger.warn("Failed to reset failed tasks", e); }); this.tasksQueueDao.clearFinished().catch((e) => { logger.warn("Failed to clear finished tasks", e); }); } catch (e) { logger.warn("Failed to process stalled tasks", e); } } fetchMetrics() { this.manageTasksQueueService .tasksCount() .then((tasksCounts) => { 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); }); } async stop() { option(this.workerTimer).foreach((t) => clearTimeout(t)); option(this.metricsTimer).foreach((t) => clearTimeout(t)); } }