UNPKG

redis-smq

Version:

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

166 lines 7.9 kB
import { async, Runnable, } from 'redis-smq-common'; import { _getConsumerGroups } from '../consumer-groups/_/_get-consumer-groups.js'; import { _getQueueProperties } from '../queue-manager/_/_get-queue-properties.js'; import { _getQueues } from '../queue-manager/_/_get-queues.js'; import { EQueueDeliveryModel, } from '../queue-manager/index.js'; import { withSharedPoolConnection } from '../common/redis/redis-connection-pool/with-shared-pool-connection.js'; import { InternalEventBus } from '../event-bus/internal-event-bus.js'; export class PubSubTargetResolver extends Runnable { internalEventBus; producerId; logger; pubSubTargets = {}; constructor(producer, logger) { super(); this.producerId = producer.getId(); this.logger = logger.createLogger(this.constructor.name); this.internalEventBus = InternalEventBus.getInstance(); this.logger.debug(`PubSubTargetResolver instance created for producer ${this.producerId}`); } getQueueKey(queue) { return `${queue.name}@${queue.ns}`; } goingUp() { return super .goingUp() .concat([this.subscribeToEvents, this.loadAndCacheInitialTargets]); } goingDown() { return [this.unsubscribeFromEvents, this.clearCache].concat(super.goingDown()); } onConsumerGroupCreated = (queue, groupId) => { const queueKey = this.getQueueKey(queue); this.logger.debug(`Handling 'consumerGroupCreated' event for group [${groupId}] on queue [${queueKey}]`); if (this.pubSubTargets[queueKey]) { const targets = this.pubSubTargets[queueKey]; if (!targets.includes(groupId)) { targets.push(groupId); this.logger.debug(`Added group [${groupId}] to cache for queue [${queueKey}].`); } } }; onConsumerGroupDeleted = (queue, groupId) => { const queueKey = this.getQueueKey(queue); this.logger.debug(`Handling 'consumerGroupDeleted' event for group [${groupId}] on queue [${queueKey}]`); if (this.pubSubTargets[queueKey]) { const targets = this.pubSubTargets[queueKey]; const index = targets.indexOf(groupId); if (index > -1) { targets.splice(index, 1); this.logger.debug(`Removed group [${groupId}] from cache for queue [${queueKey}].`); } } }; onQueueCreated = (queue, properties) => { const queueKey = this.getQueueKey(queue); this.logger.debug(`Handling 'queueCreated' event for queue [${queueKey}] with delivery model [${EQueueDeliveryModel[properties.deliveryModel]}]`); if (properties.deliveryModel === EQueueDeliveryModel.PUB_SUB) { this.pubSubTargets[queueKey] = this.pubSubTargets[queueKey] ?? []; this.logger.debug(`Added PUB/SUB queue [${queueKey}] to cache.`); } }; onQueueDeleted = (queue) => { const queueKey = this.getQueueKey(queue); this.logger.debug(`Handling 'queueDeleted' event for queue [${queueKey}]`); if (this.pubSubTargets[queueKey]) { delete this.pubSubTargets[queueKey]; this.logger.debug(`Removed queue [${queueKey}] from cache.`); } }; subscribeToEvents = (cb) => { this.logger.debug('Subscribing to queue and consumer group events...'); this.internalEventBus.on('queue.queueCreated', this.onQueueCreated); this.internalEventBus.on('queue.queueDeleted', this.onQueueDeleted); this.internalEventBus.on('queue.consumerGroupCreated', this.onConsumerGroupCreated); this.internalEventBus.on('queue.consumerGroupDeleted', this.onConsumerGroupDeleted); this.logger.debug('Successfully subscribed to events.'); cb(); }; unsubscribeFromEvents = (cb) => { this.logger.debug('Unsubscribing from events...'); this.internalEventBus.removeListener('queue.queueCreated', this.onQueueCreated); this.internalEventBus.removeListener('queue.queueDeleted', this.onQueueDeleted); this.internalEventBus.removeListener('queue.consumerGroupCreated', this.onConsumerGroupCreated); this.internalEventBus.removeListener('queue.consumerGroupDeleted', this.onConsumerGroupDeleted); this.logger.debug('Successfully unsubscribed from all events.'); cb(); }; loadAndCacheInitialTargets = (cb) => { this.logger.debug('Loading and caching initial PUB/SUB targets...'); withSharedPoolConnection((redisClient, cb) => { async.waterfall([ (cb) => { _getQueues(redisClient, cb); }, (queues, cb) => { this.logger.debug(`Found [${queues.length}] queues to process.`); async.eachOf(queues, (queue, index, done) => { const queueKey = this.getQueueKey(queue); this.logger.debug(`Processing queue [${queueKey}] (${index + 1}/${queues.length})`); async.waterfall([ (cb) => { _getQueueProperties(redisClient, queue, cb); }, (properties, cb) => { if (properties.deliveryModel === EQueueDeliveryModel.PUB_SUB) { _getConsumerGroups(redisClient, queue, (err, reply) => { if (err) return cb(err); const targets = reply ?? []; this.pubSubTargets[queueKey] = targets; this.logger.debug(`Cached [${targets.length}] targets for PUB/SUB queue [${queueKey}].`); cb(); }); } else { cb(); } }, ], (err) => { if (err) { this.logger.error(`Error processing queue [${queueKey}]. Skipping.`, err); } done(); }); }, () => { this.logger.debug('Finished initial loading of PUB/SUB targets.'); cb(); }); }, ], (err) => { if (err) { this.logger.error('Failed to complete initial target loading.', err); } else { this.logger.debug('Initial target cache is ready.'); } cb(err); }); }, cb); }; clearCache = (cb) => { this.logger.debug('Clearing PUB/SUB target cache...'); const count = Object.keys(this.pubSubTargets).length; this.pubSubTargets = {}; this.logger.debug(`Cleared [${count}] entries from cache.`); cb(); }; resolveTargets(queue) { const queueKey = this.getQueueKey(queue); this.logger.debug(`Resolving targets for queue [${queueKey}]`); if (this.pubSubTargets[queueKey]) { const targets = this.pubSubTargets[queueKey]; this.logger.debug(`Found queue [${queueKey}] in cache with [${targets.length}] targets.`); return { isPubSub: true, targets, }; } this.logger.debug(`Queue [${queueKey}] not found in cache.`); return { isPubSub: false, targets: [], }; } } //# sourceMappingURL=pub-sub-target-resolver.js.map