redis-smq
Version:
A high-performance, reliable, and scalable message queue for Node.js.
123 lines • 6.99 kB
JavaScript
;
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