queue-manager-pro
Version:
A flexible, TypeScript-first queue/task manager with pluggable backends ,dynamic persistence storage and event hooks.
78 lines • 3.1 kB
JavaScript
import { randomUUID } from 'crypto';
export class BaseQueueRepository {
logger;
emitEvent;
MAX_RETRIES;
MAX_PROCESSING_TIME;
id;
dequeueLock = false;
constructor(maxRetries, maxProcessingTime) {
this.MAX_RETRIES = maxRetries;
this.MAX_PROCESSING_TIME = maxProcessingTime;
this.id = randomUUID();
}
// same for in-memory and file repositories, others will be overridden
async updateTask(id, obj) {
const tasks = await this.loadTasks();
const task = tasks.find(t => t.id === id);
if (task) {
Object.assign(task, obj);
task.updatedAt = new Date();
await this.saveTasks(tasks);
return task;
}
return undefined;
}
sortTasksToDequeue(taskA, taskB) {
return taskB.priority - taskA.priority || new Date(taskA.createdAt).getTime() - new Date(taskB.createdAt).getTime();
}
// same for in-memory and file repositories, others will be overridden
async dequeue() {
if (this.dequeueLock)
return null;
this.dequeueLock = true;
try {
const tasks = await this.loadTasks();
const taskToProcess = [...tasks].sort(this.sortTasksToDequeue).find(t => t.status === 'pending');
if (taskToProcess) {
taskToProcess.status = 'processing';
await this.saveTasks(tasks);
return taskToProcess;
}
else {
await this.checkAndHandleStuckTasks(tasks);
return null;
}
}
finally {
this.dequeueLock = false;
}
}
async checkAndHandleStuckTasks(tasks) {
const now = Date.now();
for (const task of tasks) {
if (task.status !== 'processing')
continue;
const elapsed = now - new Date(task.updatedAt).getTime();
this.logger?.info(`Checking task ${task.id} status: elapsed time ${elapsed / 1000}s`);
const maxProcessingTime = task.maxProcessingTime ?? this.MAX_PROCESSING_TIME;
if (elapsed > maxProcessingTime) {
this.emitEvent?.('taskStuck', task);
this.logger?.warn(`Task ${task.id} is stuck`);
const maxRetries = task.maxRetries ?? this.MAX_RETRIES;
if (task?.retryCount < maxRetries) {
this.logger?.warn(`Retrying task ${task.id} (${task.retryCount + 1}/${maxRetries})`);
await this.updateTask(task.id, { retryCount: task.retryCount + 1, status: 'pending' });
this.emitEvent?.('taskRetried', task);
}
else {
const error = `Task ${task.id} failed after ${maxRetries} retries`;
this.emitEvent?.('taskFailed', task, new Error(error));
this.logger?.error(error);
await this.updateTask(task.id, { status: 'failed' });
}
}
}
}
}
//# sourceMappingURL=base.repository.js.map