redis-smq
Version:
A simple high-performance Redis message queue for Node.js.
216 lines • 10.6 kB
JavaScript
import { async, logger, Runnable, } from 'redis-smq-common';
import { Configuration } from '../../config/index.js';
import { _getConsumerGroups } from '../consumer-groups/_/_get-consumer-groups.js';
import { _getQueueProperties } from '../queue/_/_get-queue-properties.js';
import { _getQueues } from '../queue/_/_get-queues.js';
import { EQueueDeliveryModel, } from '../queue/index.js';
export class QueueConsumerGroupsCache extends Runnable {
redisClient;
eventBus;
producerId;
logger;
consumerGroupsByQueues = {};
constructor(producer, redisClient, eventBus) {
super();
this.redisClient = redisClient;
this.eventBus = eventBus;
this.producerId = producer.getId();
this.logger = logger.getLogger(Configuration.getSetConfig().logger, this.constructor.name.toLowerCase());
this.logger.debug(`QueueConsumerGroupsCache instance created for producer ${this.producerId}`);
}
getLogger() {
return this.logger;
}
goingUp() {
this.logger.debug('QueueConsumerGroupsCache is going up');
return super.goingUp().concat([this.subscribe, this.loadConsumerGroups]);
}
goingDown() {
this.logger.debug('QueueConsumerGroupsCache is going down');
return [this.unsubscribe, this.cleanUpConsumerGroups].concat(super.goingDown());
}
addConsumerGroup = (queue, groupId) => {
const queueKey = `${queue.name}@${queue.ns}`;
this.logger.debug(`Adding consumer group ${groupId} to queue ${queueKey}`);
this.consumerGroupsByQueues[queueKey] =
this.consumerGroupsByQueues[queueKey] || [];
const group = this.consumerGroupsByQueues[queueKey];
if (!group.includes(groupId)) {
this.logger.debug(`Consumer group ${groupId} not found in queue ${queueKey}, adding it`);
group.push(groupId);
}
else {
this.logger.debug(`Consumer group ${groupId} already exists in queue ${queueKey}, skipping`);
}
};
deleteConsumerGroup = (queue, groupId) => {
const queueKey = `${queue.name}@${queue.ns}`;
this.logger.debug(`Deleting consumer group ${groupId} from queue ${queueKey}`);
const groups = this.consumerGroupsByQueues[queueKey];
if (groups && groups.includes(groupId)) {
this.logger.debug(`Found consumer group ${groupId} in queue ${queueKey}, removing it`);
this.consumerGroupsByQueues[queueKey] = groups.filter((i) => i !== groupId);
this.logger.debug(`Queue ${queueKey} now has ${this.consumerGroupsByQueues[queueKey].length} consumer groups`);
}
else {
this.logger.debug(`Consumer group ${groupId} not found in queue ${queueKey}, nothing to delete`);
}
};
addQueue = (queue, properties) => {
const queueKey = `${queue.name}@${queue.ns}`;
this.logger.debug(`Adding queue ${queueKey} with delivery model ${EQueueDeliveryModel[properties.deliveryModel]}`);
if (properties.deliveryModel === EQueueDeliveryModel.PUB_SUB) {
this.logger.debug(`Queue ${queueKey} is PUB_SUB, initializing empty consumer groups array`);
this.consumerGroupsByQueues[queueKey] = [];
}
else {
this.logger.debug(`Queue ${queueKey} is not PUB_SUB, skipping consumer groups initialization`);
}
};
deleteQueue = (queue) => {
const queueKey = `${queue.name}@${queue.ns}`;
this.logger.debug(`Deleting queue ${queueKey} from consumer groups cache`);
if (this.consumerGroupsByQueues[queueKey]) {
this.logger.debug(`Found queue ${queueKey} in cache, removing it`);
delete this.consumerGroupsByQueues[queueKey];
}
else {
this.logger.debug(`Queue ${queueKey} not found in cache, nothing to delete`);
}
};
subscribe = (cb) => {
this.logger.debug('Subscribing to queue events');
const instance = this.eventBus.getInstance();
if (instance instanceof Error) {
this.logger.error('Failed to get event bus instance', instance);
cb(instance);
}
else {
this.logger.debug('Successfully got event bus instance, registering event handlers');
instance.on('queue.queueCreated', this.addQueue);
instance.on('queue.queueDeleted', this.deleteQueue);
instance.on('queue.consumerGroupCreated', this.addConsumerGroup);
instance.on('queue.consumerGroupDeleted', this.deleteConsumerGroup);
this.logger.info('Successfully subscribed to all queue events');
cb();
}
};
unsubscribe = (cb) => {
this.logger.debug('Unsubscribing from queue events');
const instance = this.eventBus.getInstance();
if (instance instanceof Error) {
this.logger.error('Failed to get event bus instance', instance);
cb(instance);
}
else {
this.logger.debug('Successfully got event bus instance, removing event handlers');
instance.removeListener('queue.queueCreated', this.addQueue);
instance.removeListener('queue.queueDeleted', this.deleteQueue);
instance.removeListener('queue.consumerGroupCreated', this.addConsumerGroup);
instance.removeListener('queue.consumerGroupDeleted', this.deleteConsumerGroup);
this.logger.info('Successfully unsubscribed from all queue events');
cb();
}
};
loadConsumerGroups = (cb) => {
this.logger.debug('Loading consumer groups for all queues');
const redisClient = this.redisClient.getInstance();
if (redisClient instanceof Error) {
this.logger.error('Failed to get Redis client instance', redisClient);
cb(redisClient);
}
else {
this.logger.debug('Successfully got Redis client instance, fetching queues');
async.waterfall([
(cb) => {
this.logger.debug('Getting all queues');
_getQueues(redisClient, cb);
},
(queues, cb) => {
this.logger.info(`Found ${queues.length} queues, processing each queue`);
async.eachOf(queues, (queue, index, done) => {
const queueKey = `${queue.name}@${queue.ns}`;
this.logger.debug(`Processing queue ${queueKey} (${index + 1}/${queues.length})`);
async.waterfall([
(cb) => {
this.logger.debug(`Getting properties for queue ${queueKey}`);
_getQueueProperties(redisClient, queue, cb);
},
(properties, cb) => {
this.logger.debug(`Queue ${queueKey} has delivery model ${EQueueDeliveryModel[properties.deliveryModel]}`);
if (properties.deliveryModel === EQueueDeliveryModel.PUB_SUB) {
this.logger.debug(`Queue ${queueKey} is PUB_SUB, getting consumer groups`);
_getConsumerGroups(redisClient, queue, (err, reply) => {
if (err) {
this.logger.error(`Failed to get consumer groups for queue ${queueKey}`, err);
cb(err);
}
else {
const groupCount = reply?.length || 0;
this.logger.debug(`Found ${groupCount} consumer groups for queue ${queueKey}`);
this.consumerGroupsByQueues[queueKey] = reply ?? [];
cb();
}
});
}
else {
this.logger.debug(`Queue ${queueKey} is not PUB_SUB, skipping consumer groups`);
cb();
}
},
], (err) => {
if (err) {
this.logger.error(`Error processing queue ${queueKey}`, err);
}
else {
this.logger.debug(`Successfully processed queue ${queueKey}`);
}
done(err);
});
}, (err) => {
if (err) {
this.logger.error('Failed to process all queues', err);
}
else {
this.logger.info('Successfully loaded consumer groups for all queues');
}
cb(err);
});
},
], (err) => {
if (err) {
this.logger.error('Failed to load consumer groups', err);
}
else {
this.logger.info('Consumer groups cache initialized successfully');
}
cb(err);
});
}
};
cleanUpConsumerGroups = (cb) => {
this.logger.debug('Cleaning up consumer groups cache');
const groupCount = Object.keys(this.consumerGroupsByQueues).length;
this.consumerGroupsByQueues = {};
this.logger.info(`Cleared ${groupCount} entries from consumer groups cache`);
cb();
};
getConsumerGroups(queue) {
const key = `${queue.name}@${queue.ns}`;
this.logger.debug(`Getting consumer groups for queue ${key}`);
if (this.consumerGroupsByQueues[key]) {
const groupCount = this.consumerGroupsByQueues[key].length;
this.logger.debug(`Found queue ${key} in cache with ${groupCount} consumer groups`);
return {
exists: true,
consumerGroups: this.consumerGroupsByQueues[key],
};
}
this.logger.debug(`Queue ${key} not found in cache, returning empty result`);
return {
exists: false,
consumerGroups: [],
};
}
}
//# sourceMappingURL=queue-consumer-groups-cache.js.map