UNPKG

redis-smq

Version:

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

166 lines 6.98 kB
import { CallbackEmptyReplyError, PanicError, Runnable, } from 'redis-smq-common'; import { redisKeys } from '../../../common/redis/redis-keys/redis-keys.js'; import { _hasRateLimitExceeded } from '../../../queue-rate-limit/_/_has-rate-limit-exceeded.js'; import { _getQueueProperties } from '../../../queue-manager/_/_get-queue-properties.js'; import { EQueueType, } from '../../../index.js'; import { eventPublisher } from './event-publisher.js'; import { ERedisConnectionAcquisitionMode } from '../../../common/redis/redis-connection-pool/types/connection-pool.js'; import { RedisConnectionPool } from '../../../common/redis/redis-connection-pool/redis-connection-pool.js'; export class DequeueMessage extends Runnable { consumerContext; config; logger; queue; keyQueues; keyQueueConsumers; keyConsumerQueues; keyQueueProcessingQueues; keyQueueProcessing; keyQueuePending; keyQueuePriorityPending; keyQueueProperties; blockUntilMessageReceived; autoCloseRedisConnection; redisClient = null; queueRateLimit = null; queueType = null; constructor(consumerContext, queue, blockUntilMessageReceived = true, autoCloseRedisConnection = true) { super(); this.consumerContext = consumerContext; this.logger = consumerContext.logger.createLogger(this.constructor.name); this.config = consumerContext.config; this.queue = queue; this.blockUntilMessageReceived = blockUntilMessageReceived; this.autoCloseRedisConnection = autoCloseRedisConnection; eventPublisher(this); const { keyConsumerQueues } = redisKeys.getConsumerKeys(this.consumerContext.consumerId); const { keyQueueProcessing } = redisKeys.getQueueConsumerKeys(this.queue.queueParams, this.consumerContext.consumerId); const { keyQueues } = redisKeys.getMainKeys(); const { keyQueueProcessingQueues, keyQueuePending, keyQueuePriority, keyQueueConsumers, keyQueueProperties, } = redisKeys.getQueueKeys(this.queue.queueParams.ns, this.queue.queueParams.name, this.queue.groupId); this.keyQueueProperties = keyQueueProperties; this.keyQueuePriorityPending = keyQueuePriority; this.keyQueuePending = keyQueuePending; this.keyQueueProcessing = keyQueueProcessing; this.keyQueues = keyQueues; this.keyQueueConsumers = keyQueueConsumers; this.keyConsumerQueues = keyConsumerQueues; this.keyQueueProcessingQueues = keyQueueProcessingQueues; } runDequeue(redisClient) { if (this.queueRateLimit) { _hasRateLimitExceeded(redisClient, this.queue.queueParams, this.queueRateLimit, (err, isExceeded) => { if (err) this.handleError(err); else if (isExceeded) { this.emit('consumer.dequeueMessage.nextMessage'); } else { this.performDequeue(redisClient); } }); } else { this.performDequeue(redisClient); } } performDequeue(redisClient) { if (this.isPriorityQueuingEnabled()) { redisClient.zpoplpush(this.keyQueuePriorityPending, this.keyQueueProcessing, this.handleMessage); } else if (this.blockUntilMessageReceived && !this.queueRateLimit) { redisClient.brpoplpush(this.keyQueuePending, this.keyQueueProcessing, 0, this.handleMessage); } else { redisClient.rpoplpush(this.keyQueuePending, this.keyQueueProcessing, this.handleMessage); } } getRedisClient() { if (!this.redisClient) return new PanicError({ message: 'A RedisClient instance is required.' }); return this.redisClient; } handleError(err) { if (!this.isOperational()) return; this.logger.error(`DequeueMessage error: ${err.message}`, err); this.emit('consumer.dequeueMessage.error', err, this.consumerContext.consumerId, this.queue); super.handleError(err); } goingUp() { return super.goingUp().concat([ (cb) => { const redisConnectionAcquisitionMode = this.blockUntilMessageReceived ? ERedisConnectionAcquisitionMode.EXCLUSIVE : ERedisConnectionAcquisitionMode.SHARED; RedisConnectionPool.getInstance().acquire(redisConnectionAcquisitionMode, (err, redisClient) => { if (err) return cb(err); if (!redisClient) return cb(new CallbackEmptyReplyError()); this.redisClient = redisClient; cb(); }); }, (cb) => { const redisClient = this.getRedisClient(); if (redisClient instanceof Error) return cb(redisClient); _getQueueProperties(redisClient, this.queue.queueParams, (err, queueProperties) => { if (err) return cb(err); if (!queueProperties) return cb(new CallbackEmptyReplyError()); this.queueType = queueProperties.queueType; this.queueRateLimit = queueProperties.rateLimit ?? null; cb(); }); }, ]); } goingDown() { return [ (cb) => { if (this.redisClient) { if (this.autoCloseRedisConnection) { RedisConnectionPool.getInstance().destroy(this.redisClient, () => { this.redisClient = null; cb(); }); } else { RedisConnectionPool.getInstance().release(this.redisClient); this.redisClient = null; cb(); } } else cb(); }, ].concat(super.goingDown()); } isPriorityQueuingEnabled() { return this.queueType === EQueueType.PRIORITY_QUEUE; } handleMessage = (err, messageId) => { if (err) { this.handleError(err); } else if (typeof messageId === 'string') { this.emit('consumer.dequeueMessage.messageReceived', messageId, this.queue, this.consumerContext.consumerId); } else { this.emit('consumer.dequeueMessage.nextMessage'); } }; dequeue() { if (!this.isOperational()) { return; } const redisClient = this.getRedisClient(); if (redisClient instanceof Error) { return this.handleError(redisClient); } this.runDequeue(redisClient); } } //# sourceMappingURL=dequeue-message.js.map