redis-smq
Version:
A high-performance, reliable, and scalable message queue for Node.js.
170 lines • 7.53 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PurgeQueueJob = void 0;
const background_job_worker_abstract_js_1 = require("../../../abstract/background-job/background-job-worker-abstract.js");
const redis_smq_common_1 = require("redis-smq-common");
const purge_queue_job_manager_js_1 = require("./purge-queue-job-manager.js");
const index_js_1 = require("../../../index.js");
const _delete_message_js_1 = require("../../../../message-manager/_/_delete-message.js");
const index_js_2 = require("../../../../errors/index.js");
const message_browser_factory_js_1 = require("../../../../queue-messages/message-browser-factory.js");
class PurgeQueueJob extends background_job_worker_abstract_js_1.BackgroundJobWorkerAbstract {
constructor() {
super(...arguments);
this.jobManager = null;
}
processNextJob(cb) {
redis_smq_common_1.async.waterfall([
(cb) => {
this.getJobManager().acquireNextJob((err, jobId) => {
if (err)
return cb(err);
if (!jobId) {
this.logger.debug(`No job available.`);
return cb(null);
}
this.logger.debug(`Worker ${this.id} acquired job: ${jobId}`);
cb(null, jobId);
});
},
(jobId, cb) => {
this.executeJob(jobId, (err) => {
if (err) {
this.logger.error(`Job ${jobId} failed:`, err);
}
else {
this.logger.info(`Job ${jobId} completed successfully`);
}
cb(err);
});
},
], cb);
}
executeJob(jobId, cb) {
const jobManager = this.getJobManager();
redis_smq_common_1.async.waterfall([
(next) => {
jobManager.start(jobId, this.id, next);
},
(job, next) => {
this.purgeMessages(job, next);
},
(totalPurged, next) => {
jobManager.complete(jobId, { meta: { purged: totalPurged } }, (err) => next(err));
},
], (err) => {
if (err) {
return jobManager.fail(jobId, err.message, (err) => {
if (!err) {
this.logger.debug(`Successfully marked job ${jobId} as failed`);
}
cb();
});
}
cb();
});
}
goingUp() {
return super.goingUp().concat([
(cb) => {
const redisClient = this.getRedisClient();
this.jobManager = new purge_queue_job_manager_js_1.PurgeQueueJobManager(redisClient, this.logger);
this.jobManager.initialize(cb);
},
]);
}
getJobManager() {
if (!this.jobManager)
throw new redis_smq_common_1.PanicError({
message: 'A PurgeQueueJobManager instance is required.',
});
return this.jobManager;
}
purgeMessages(job, cb) {
const parsedParams = job.payload.queue;
const delay = job.delay || 5000;
const batchSize = job.batchSize || 1000;
let totalPurged = 0;
let initialTotalItems = 0;
let batchCount = 0;
this.logger.debug(`Starting batch purge for queue ${parsedParams.queueParams.name}`);
const purgeBatch = () => {
batchCount += 1;
this.logger.debug(`Processing purge batch ${batchCount} (batch size = ${batchSize}) for queue ${parsedParams.queueParams.name}`);
redis_smq_common_1.async.waterfall([
(next) => {
this.getJobManager().get(job.id, (err, currentJob) => {
if (err) {
return next(err);
}
if (!currentJob ||
currentJob.status === index_js_1.EBackgroundJobStatus.CANCELED) {
return next(new index_js_2.BackgroundJobCanceledError({
metadata: { jobId: job.id },
}));
}
next();
});
},
(_, next) => message_browser_factory_js_1.MessageBrowserFactory.createBrowserForQueue(job.payload.queue.queueParams, job.payload.messageType, this.logger, next),
(messageBrowser, next) => {
messageBrowser.getMessageIds(parsedParams, 1, batchSize, next);
},
(result, next) => {
const { items, totalItems } = result;
if (batchCount === 1) {
initialTotalItems = totalItems;
this.logger.debug(`Queue "${parsedParams.queueParams.name}" has ${initialTotalItems} messages to purge`);
}
if (items.length === 0) {
this.logger.debug(`No messages to purge in this batch for queue ${parsedParams.queueParams.name}`);
return next(null, false);
}
this.logger.debug(`Deleting ${items.length} messages from queue ${parsedParams.queueParams.name}`);
(0, _delete_message_js_1._deleteMessage)(this.getRedisClient(), items, job.id, (err, reply) => {
if (err) {
this.logger.error(`Error deleting messages: ${err.message}`);
return next(err);
}
const successfullyDeleted = (reply === null || reply === void 0 ? void 0 : reply.stats.success) || 0;
totalPurged += successfullyDeleted;
this.logger.debug(`Batch ${batchCount}: Deleted ${successfullyDeleted} messages`);
this.getJobManager().updateProgress(job.id, totalPurged, initialTotalItems);
const shouldContinue = items.length === batchSize;
next(null, shouldContinue);
});
},
], (err, shouldContinue) => {
if (err) {
this.logger.error(`Error in purge batch: ${err.message}`);
return cb(err);
}
if (shouldContinue) {
this.logger.debug(`Got full batch (${batchSize} items), continuing with batch ${batchCount + 1}`);
if (delay)
setTimeout(() => purgeBatch(), delay);
else
purgeBatch();
}
else {
this.logger.info(`Successfully purged ${totalPurged} messages from queue "${parsedParams.queueParams.name}"`);
cb(null, totalPurged);
}
});
};
purgeBatch();
}
work(cb) {
redis_smq_common_1.async.series([
(cb) => {
this.getJobManager().recoverStuckJobs(cb);
},
(cb) => {
this.processNextJob(cb);
},
], (err) => cb(err));
}
}
exports.PurgeQueueJob = PurgeQueueJob;
exports.default = PurgeQueueJob;
//# sourceMappingURL=purge-queue.job.js.map