queue-manager-pro
Version:
A flexible, TypeScript-first queue/task manager with pluggable backends ,dynamic persistence storage and event hooks.
99 lines • 3.62 kB
JavaScript
import { TaskMaxRetriesExceededError, TaskProcessingTimeoutError } from '../util/errors.js';
export class QueueWorker {
queueManager;
workerActive = false;
workerPromise;
logger;
constructor(queueManager, logger) {
this.queueManager = queueManager;
this.logger = logger;
}
async startWorker(concurrency = 1) {
this.log('info', `Starting ${concurrency} workers`);
this.workerActive = true;
const workers = [];
for (let i = 0; i < concurrency; i++) {
workers.push(this.queueWorker());
}
this.workerPromise = Promise.all(workers);
}
async stopWorker() {
this.log('info', 'Worker stopping...');
this.workerActive = false;
await this.workerPromise;
this.log('info', 'Worker stopped');
}
async processTaskWithTimeout(task) {
let timeoutId;
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(() => reject(new TaskProcessingTimeoutError()), task.maxProcessingTime + 1000);
});
try {
const promise = this.processTask(task);
return await Promise.race([promise, timeoutPromise]);
}
catch (err) {
if (err instanceof TaskProcessingTimeoutError) {
if (task.retryCount <= task.maxRetries) {
task.retryCount++;
this.queueManager.emit('taskRetried', task);
clearTimeout(timeoutId);
this.log('error', `${task.id}:`, err);
return await this.processTaskWithTimeout(task);
}
else {
this.log('error', `Task ${task.id} failed:`, err);
throw new TaskMaxRetriesExceededError(task.id, err.message);
}
}
else {
throw err; // Re-throw unexpected errors
}
}
finally {
clearTimeout(timeoutId);
}
}
async queueWorker() {
while (this.workerActive) {
const task = await this.queueManager['repository'].dequeue();
if (task) {
try {
await this.processTaskWithTimeout(task);
}
catch (err) {
await this.handleTaskError(task, err);
}
}
else {
await new Promise(res => setTimeout(res, this.queueManager['delay']));
}
}
}
async processTask(task) {
this.queueManager.emit('taskStarted', task);
const handler = this.queueManager['registry'].get(task.handler);
await handler.fn(task.payload);
await this.queueManager['repository'].updateTask(task.id, { status: 'done' });
this.queueManager.emit('taskCompleted', task);
}
async handleTaskError(task, err) {
this.log('error', `Task ${task.id} failed:`, err);
const log = err?.message?.toString() || 'Unknown error';
await this.queueManager['repository'].updateTask(task.id, { status: 'failed', log });
const emitError = err instanceof Error ? err : new Error(String(err));
if (this.queueManager.crashOnWorkerError) {
this.workerActive = false;
throw emitError;
}
else {
this.queueManager.emit('taskFailed', task, emitError);
}
}
log(level, ...args) {
if (this.logger?.[level]) {
this.logger[level](...args);
}
}
}
//# sourceMappingURL=queueWorker.js.map