dt-common-device
Version:
A secure and robust device management library for IoT applications
157 lines (156 loc) • 6.16 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.QueueManager = void 0;
const redis_1 = require("../../db/redis");
const config_1 = require("../../config/config");
class QueueManager {
/**
* Get or create a BullMQ queue
*/
static async getOrCreateQueue(queueKey, queues) {
if (queues.has(queueKey)) {
return queues.get(queueKey);
}
const { Queue } = await Promise.resolve().then(() => __importStar(require("bullmq")));
const queue = new Queue(queueKey, {
connection: (0, redis_1.getRedisClient)(),
});
queues.set(queueKey, queue);
return queue;
}
/**
* Get or create a BullMQ worker
*/
static async getOrCreateWorker(queueKey, workers, queues, processor) {
if (workers.has(queueKey)) {
return workers.get(queueKey);
}
const { Worker } = await Promise.resolve().then(() => __importStar(require("bullmq")));
const worker = new Worker(queueKey, processor, {
connection: (0, redis_1.getRedisClient)(),
concurrency: 1, // Process one job at a time for FIFO ordering
});
// Setup event listeners
worker.on("completed", async (job) => {
const propertyId = job.data.body?.propertyId || "unknown";
(0, config_1.getConfig)().LOGGER.info(`Job ${job.id} completed for property ${propertyId}`);
// Check if queue is empty and cleanup worker
await QueueManager.checkAndCleanupWorker(queueKey, workers, queues);
});
worker.on("failed", async (job, err) => {
const propertyId = job?.data?.body?.propertyId || "unknown";
(0, config_1.getConfig)().LOGGER.error(`Job ${job?.id} failed for property ${propertyId}:`, err.message);
// Check if queue is empty and cleanup worker
await QueueManager.checkAndCleanupWorker(queueKey, workers, queues);
});
worker.on("error", (err) => {
(0, config_1.getConfig)().LOGGER.error("Worker error:", err.message);
});
workers.set(queueKey, worker);
return worker;
}
/**
* Check if queue is empty and cleanup worker if no jobs are pending
*/
static async checkAndCleanupWorker(queueKey, workers, queues) {
try {
const queue = queues.get(queueKey);
if (!queue)
return;
// Check if queue has any waiting, active, or delayed jobs
const [waiting, active, delayed] = await Promise.all([
queue.getWaiting(),
queue.getActive(),
queue.getDelayed(),
]);
const totalJobs = waiting.length + active.length + delayed.length;
// If no jobs are pending, cleanup the worker
if (totalJobs === 0) {
const worker = workers.get(queueKey);
if (worker) {
await worker.close();
workers.delete(queueKey);
// Extract propertyId from queueKey (format: propertyId_copilot)
const propertyId = queueKey.replace("_copilot", "");
(0, config_1.getConfig)().LOGGER.info(`Worker cleaned up for property ${propertyId} - no pending jobs`);
}
}
}
catch (error) {
(0, config_1.getConfig)().LOGGER.error(`Error during worker cleanup for ${queueKey}:`, error.message);
}
}
/**
* Close all workers and queues
*/
static async closeAllWorkersAndQueues(workers, queues) {
try {
// Close all workers and queues
await Promise.all([
...Array.from(workers.values()).map((worker) => worker.close()),
...Array.from(queues.values()).map((queue) => queue.close()),
]);
workers.clear();
queues.clear();
(0, config_1.getConfig)().LOGGER.info("All workers and queues closed successfully");
}
catch (error) {
(0, config_1.getConfig)().LOGGER.error("Error closing workers and queues:", error.message);
throw error;
}
}
/**
* Generate queue name for a property
*/
static generateQueueName(propertyId) {
return `${propertyId}_copilot`;
}
/**
* Extract property ID from job ID
*/
static extractPropertyIdFromJobId(jobId) {
return jobId.split("-")[0];
}
/**
* Generate unique job ID
*/
static generateJobId(propertyId) {
return `${propertyId}-${Date.now()}-${Math.random()
.toString(36)
.substr(2, 9)}`;
}
}
exports.QueueManager = QueueManager;