UNPKG

redis-smq

Version:

A high-performance, reliable, and scalable message queue for Node.js.

123 lines 6.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ReapConsumersWorker = void 0; const redis_smq_common_1 = require("redis-smq-common"); const _get_queue_consumer_ids_js_1 = require("../../../../queue-manager/_/_get-queue-consumer-ids.js"); const message_unacknowledger_js_1 = require("../../consume-message/message-unacknowledger.js"); const index_js_1 = require("../../consume-message/types/index.js"); const with_shared_pool_connection_js_1 = require("../../../../common/redis/redis-connection-pool/with-shared-pool-connection.js"); const _delete_ephemeral_consumer_group_js_1 = require("../../_/_delete-ephemeral-consumer-group.js"); const queue_worker_abstract_js_1 = require("../queue-worker-abstract.js"); const _is_consumer_alive_js_1 = require("../../../_/_is-consumer-alive.js"); const consumer_js_1 = require("../../../consumer.js"); const _unsubscribe_consumer_js_1 = require("../../_/_unsubscribe-consumer.js"); class ReapConsumersWorker extends queue_worker_abstract_js_1.QueueWorkerAbstract { constructor() { super(...arguments); this.work = (cb) => { (0, with_shared_pool_connection_js_1.withSharedPoolConnection)((redisClient, connCb) => { const queue = this.queueParsedParams.queueParams; const queueRef = `${queue.name}@${queue.ns}`; (0, _get_queue_consumer_ids_js_1._getQueueConsumerIds)(redisClient, queue, (err, consumerIds) => { if (err) { this.logger.error(`Failed to get consumer IDs for ${queueRef}: ${err.message}`); return connCb(err); } const consumers = (consumerIds || []).filter((i) => i !== this.consumerId); if (consumers.length === 0) { return connCb(); } this.logger.debug(`Found ${consumers.length} consumers for queue ${queueRef}`); redis_smq_common_1.async.eachOf(consumers, (consumerId, index, done) => { this.logger.debug(`[${index + 1}/${consumers.length}] Checking heartbeat for consumer ${consumerId}`); (0, _is_consumer_alive_js_1._isConsumerAlive)(redisClient, consumerId, (err, alive) => { if (err) { this.logger.error(`Heartbeat check failed for ${consumerId}: ${err.message}`); return done(err); } if (alive) { this.logger.debug(`Consumer ${consumerId} is alive`); return done(); } this.logger.info(`Consumer ${consumerId} is offline, starting recovery`); const unacknowledger = this.createMessageUnacknowledger(consumerId); this.recoverConsumer(consumerId, unacknowledger, (recoveryErr) => { if (recoveryErr) { this.logger.error(`Recovery failed for consumer ${consumerId}: ${recoveryErr.message}`); } else { this.logger.info(`Successfully recovered consumer ${consumerId}`); } done(); }); }); }, connCb); }); }, cb); }; } createMessageUnacknowledger(consumerId) { const unacknowledger = new message_unacknowledger_js_1.MessageUnacknowledger(consumerId, this.queueParsedParams, this.logger, consumer_js_1.Consumer.getDefaultOptions()); unacknowledger.on('messageUnacknowledger.error', (err) => { this.logger.error(`MessageUnacknowledger error for consumer ${consumerId}:`, err); }); unacknowledger.on('messageUnacknowledger.messagesUnacknowledged', (status) => { const messageCount = Object.keys(status).length; if (messageCount > 0) { this.logger.info(`Unacknowledged ${messageCount} messages for consumer ${consumerId}`); } }); return unacknowledger; } recoverConsumer(consumerId, unacknowledger, cb) { const queue = this.queueParsedParams.queueParams; const queueRef = `${queue.name}@${queue.ns}`; redis_smq_common_1.async.series([ (done) => { this.logger.debug(`Starting unacknowledger for consumer ${consumerId}`); unacknowledger.run(done); }, (done) => { this.logger.debug(`Unacknowledging messages for consumer ${consumerId} on queue ${queueRef}`); unacknowledger.unacknowledgeProcessingQueue(index_js_1.EMessageUnacknowledgementCause.OFFLINE_CONSUMER, (err, status) => { if (err) { this.logger.error(`Failed to unacknowledge messages for consumer ${consumerId}: ${err.message}`); return done(); } const count = status ? Object.keys(status).length : 0; if (count > 0) { this.logger.info(`Unacknowledged ${count} messages for consumer ${consumerId}`); } done(); }); }, (done) => { this.logger.debug(`Cleaning up ephemeral groups for consumer ${consumerId}`); (0, _delete_ephemeral_consumer_group_js_1._deleteEphemeralConsumerGroup)(queue, consumerId, null, (err) => { if (err) { this.logger.warn(`Failed to delete ephemeral group for ${queueRef}, consumer ${consumerId}: ${err.message}`); } else { this.logger.debug(`Ephemeral group deleted for ${queueRef}, consumer ${consumerId}`); } done(); }); }, (done) => { (0, _unsubscribe_consumer_js_1._unsubscribeConsumer)(consumerId, this.queueParsedParams, (err) => { if (err) { const queue = this.queueParsedParams.queueParams; this.logger.error(`Failed to unsubscribe consumer ${consumerId} from queue ${queue.name}@${queue.ns}: ${err.message}`); } done(err); }); }, ], (err) => { this.logger.debug(`Shutting down unacknowledger for consumer ${consumerId}`); unacknowledger.shutdown(() => cb(err)); }); } } exports.ReapConsumersWorker = ReapConsumersWorker; exports.default = ReapConsumersWorker; //# sourceMappingURL=reap-consumers.worker.js.map