UNPKG

redis-smq

Version:

A simple high-performance Redis message queue for Node.js.

259 lines 13.2 kB
import { async, CallbackEmptyReplyError, logger, withEventBus, withRedisClient, } from 'redis-smq-common'; import { EventBus } from '../../common/index.js'; import { RedisClient } from '../../common/redis-client/redis-client.js'; import { ELuaScriptName } from '../../common/redis-client/scripts/scripts.js'; import { redisKeys } from '../../common/redis-keys/redis-keys.js'; import { Configuration } from '../../config/index.js'; import { _deleteQueue } from './_/_delete-queue.js'; import { _getQueueConsumerIds } from './_/_get-queue-consumer-ids.js'; import { _getQueueConsumers } from './_/_get-queue-consumers.js'; import { _getQueueProperties } from './_/_get-queue-properties.js'; import { _getQueues } from './_/_get-queues.js'; import { _parseQueueParams } from './_/_parse-queue-params.js'; import { _queueExists } from './_/_queue-exists.js'; import { QueueQueueExistsError } from './errors/index.js'; import { EQueueDeliveryModel, EQueueProperty, EQueueType, } from './types/index.js'; export class Queue { redisClient; eventBus; logger; constructor() { this.logger = logger.getLogger(Configuration.getSetConfig().logger, this.constructor.name.toLowerCase()); this.logger.debug('Initializing Queue instance'); this.eventBus = new EventBus(); this.eventBus.on('error', (err) => { this.logger.error(`EventBus error: ${err.message}`, err); }); this.logger.debug('EventBus initialized'); this.redisClient = new RedisClient(); this.redisClient.on('error', (err) => { this.logger.error(`Redis client error: ${err.message}`, err); }); this.logger.debug('Redis client initialized'); this.logger.info('Queue instance initialized successfully'); } save(queue, queueType, deliveryModel, cb) { const queueDesc = typeof queue === 'string' ? queue : `${queue.name}@${queue.ns}`; this.logger.debug(`Saving queue: ${queueDesc}, type: ${EQueueType[queueType]}, delivery model: ${EQueueDeliveryModel[deliveryModel]}`); const queueParams = _parseQueueParams(queue); if (queueParams instanceof Error) { this.logger.error(`Error parsing queue parameters for ${queueDesc}: ${queueParams.message}`, queueParams); return cb(queueParams); } const queueName = `${queueParams.name}@${queueParams.ns}`; this.logger.debug(`Parsed queue parameters: ${queueName}`); withRedisClient(this.redisClient, (client, cb) => { async.withCallback((cb) => { const { keyQueueProperties } = redisKeys.getQueueKeys(queueParams, null); const { keyNamespaces, keyQueues } = redisKeys.getMainKeys(); const { keyNamespaceQueues } = redisKeys.getNamespaceKeys(queueParams.ns); const queueParamsStr = JSON.stringify(queueParams); this.logger.debug(`Executing CREATE_QUEUE script for ${queueName} with keys: [${keyNamespaces}, ${keyNamespaceQueues}, ${keyQueues}, ${keyQueueProperties}]`); client.runScript(ELuaScriptName.CREATE_QUEUE, [ keyNamespaces, keyNamespaceQueues, keyQueues, keyQueueProperties, ], [ queueParams.ns, queueParamsStr, EQueueProperty.QUEUE_TYPE, queueType, EQueueProperty.DELIVERY_MODEL, deliveryModel, ], cb); }, (reply, cb) => { if (reply !== 'OK') { const error = new QueueQueueExistsError(); this.logger.error(`Queue ${queueName} already exists`, error); return cb(error); } this.logger.debug(`Queue ${queueName} created successfully, retrieving properties`); async.withCallback((cb) => this.getProperties(queueParams, cb), (properties, cb) => { this.logger.debug(`Retrieved properties for queue ${queueName}, emitting queue.queueCreated event`); withEventBus(this.eventBus, (eventBus, cb) => { this.logger.debug(`Emitting queue.queueCreated event for ${queueName}`); eventBus.emit('queue.queueCreated', queueParams, properties); this.logger.info(`Queue ${queueName} successfully created with type=${EQueueType[queueType]}, deliveryModel=${EQueueDeliveryModel[deliveryModel]}`); cb(null, { queue: queueParams, properties }); }, cb); }, cb); }, cb); }, cb); } exists(queue, cb) { const queueDesc = typeof queue === 'string' ? queue : `${queue.name}@${queue.ns}`; this.logger.debug(`Checking if queue exists: ${queueDesc}`); const queueParams = _parseQueueParams(queue); if (queueParams instanceof Error) { this.logger.error(`Error parsing queue parameters for ${queueDesc}: ${queueParams.message}`, queueParams); cb(queueParams); } else { const queueName = `${queueParams.name}@${queueParams.ns}`; this.logger.debug(`Parsed queue parameters: ${queueName}`); withRedisClient(this.redisClient, (client, cb) => { this.logger.debug(`Checking if queue ${queueName} exists in Redis`); _queueExists(client, queueParams, (err, exists) => { if (err) { this.logger.error(`Error checking if queue ${queueName} exists: ${err.message}`, err); cb(err); } else { this.logger.debug(`Queue ${queueName} exists: ${exists}`); cb(null, exists); } }); }, cb); } } delete(queue, cb) { const queueDesc = typeof queue === 'string' ? queue : `${queue.name}@${queue.ns}`; this.logger.debug(`Deleting queue: ${queueDesc}`); const queueParams = _parseQueueParams(queue); if (queueParams instanceof Error) { this.logger.error(`Error parsing queue parameters for ${queueDesc}: ${queueParams.message}`, queueParams); return cb(queueParams); } const queueName = `${queueParams.name}@${queueParams.ns}`; this.logger.debug(`Parsed queue parameters: ${queueName}`); withRedisClient(this.redisClient, (client, cb) => { this.logger.debug(`Executing delete queue operation for ${queueName}`); _deleteQueue(client, queueParams, undefined, (err, multi) => { if (err) { this.logger.error(`Error preparing delete operation for queue ${queueName}: ${err.message}`, err); return cb(err); } if (!multi) { const error = new CallbackEmptyReplyError(); this.logger.error(`Multi command is empty for deleting queue ${queueName}`, error); return cb(error); } this.logger.debug(`Executing multi command to delete queue ${queueName}`); multi.exec((err) => { if (err) { this.logger.error(`Error executing multi command for deleting queue ${queueName}: ${err.message}`, err); return cb(err); } this.logger.debug(`Queue ${queueName} deleted from Redis, emitting queue.queueDeleted event`); withEventBus(this.eventBus, (eventBus, cb) => { this.logger.debug(`Emitting queue.queueDeleted event for ${queueName}`); eventBus.emit('queue.queueDeleted', queueParams); this.logger.info(`Queue ${queueName} successfully deleted`); cb(); }, cb); }); }); }, cb); } getProperties(queue, cb) { const queueDesc = typeof queue === 'string' ? queue : `${queue.name}@${queue.ns}`; this.logger.debug(`Getting properties for queue: ${queueDesc}`); const queueParams = _parseQueueParams(queue); if (queueParams instanceof Error) { this.logger.error(`Error parsing queue parameters for ${queueDesc}: ${queueParams.message}`, queueParams); cb(queueParams); } else { const queueName = `${queueParams.name}@${queueParams.ns}`; this.logger.debug(`Parsed queue parameters: ${queueName}`); withRedisClient(this.redisClient, (client, cb) => { this.logger.debug(`Retrieving properties for queue ${queueName}`); _getQueueProperties(client, queueParams, (err, properties) => { if (err) { this.logger.error(`Error getting properties for queue ${queueName}: ${err.message}`, err); cb(err); } else { if (properties) { this.logger.debug(`Retrieved properties for queue ${queueName}: type=${EQueueType[properties.queueType]}, deliveryModel=${EQueueDeliveryModel[properties.deliveryModel]}`); } else { this.logger.debug(`No properties found for queue ${queueName}`); } cb(null, properties); } }); }, cb); } } getQueues(cb) { this.logger.debug('Getting all queues'); withRedisClient(this.redisClient, (client, cb) => { this.logger.debug('Retrieving all queues from Redis'); _getQueues(client, (err, queues) => { if (err) { this.logger.error(`Error getting queues: ${err.message}`, err); cb(err); } else { const queueCount = queues?.length || 0; this.logger.debug(`Retrieved ${queueCount} queues`); cb(null, queues); } }); }, cb); } getConsumers(queue, cb) { const queueDesc = typeof queue === 'string' ? queue : `${queue.name}@${queue.ns}`; this.logger.debug(`Getting consumers for queue: ${queueDesc}`); const queueParams = _parseQueueParams(queue); if (queueParams instanceof Error) { this.logger.error(`Error parsing queue parameters for ${queueDesc}: ${queueParams.message}`, queueParams); return cb(queueParams); } const queueName = `${queueParams.name}@${queueParams.ns}`; this.logger.debug(`Parsed queue parameters: ${queueName}`); withRedisClient(this.redisClient, (client, cb) => _getQueueConsumers(client, queueParams, cb), cb); } getConsumerIds(queue, cb) { const queueDesc = typeof queue === 'string' ? queue : `${queue.name}@${queue.ns}`; this.logger.debug(`Getting consumer Ids for queue: ${queueDesc}`); const queueParams = _parseQueueParams(queue); if (queueParams instanceof Error) { this.logger.error(`Error parsing queue parameters for ${queueDesc}: ${queueParams.message}`, queueParams); return cb(queueParams); } const queueName = `${queueParams.name}@${queueParams.ns}`; this.logger.debug(`Parsed queue parameters: ${queueName}`); withRedisClient(this.redisClient, (client, cb) => _getQueueConsumerIds(client, queueParams, cb), cb); } shutdown = (cb) => { this.logger.info('Shutting down Queue instance'); async.series([ (next) => { this.logger.debug('Shutting down Redis client'); this.redisClient.shutdown((err) => { if (err) { this.logger.error(`Error shutting down Redis client: ${err.message}`, err); } else { this.logger.debug('Redis client shutdown successful'); } next(err); }); }, (next) => { this.logger.debug('Shutting down EventBus'); this.eventBus.shutdown((err) => { if (err) { this.logger.error(`Error shutting down EventBus: ${err.message}`, err); } else { this.logger.debug('EventBus shutdown successful'); } next(err); }); }, ], (err) => { if (err) { this.logger.error(`Error during Queue shutdown: ${err.message}`, err); } else { this.logger.info('Queue instance shutdown completed successfully'); } cb(err); }); }; } //# sourceMappingURL=queue.js.map