UNPKG

redis-smq

Version:

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

143 lines 6.59 kB
import { async } from 'redis-smq-common'; 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 { _getMessage } from '../../../message/_/_get-message.js'; import { EMessageProperty, EMessagePropertyStatus, } from '../../../message/index.js'; import { consumerQueues } from '../../consumer-queues.js'; import { ConsumerError } from '../../errors/index.js'; import { EMessageUnknowledgmentAction, EMessageUnknowledgmentDeadLetterReason, EMessageUnknowledgmentReason, } from './types/index.js'; function getMessageUnknowledgementAction(message, unacknowledgedReason) { if (unacknowledgedReason === EMessageUnknowledgmentReason.TTL_EXPIRED || message.getSetExpired()) { return { action: EMessageUnknowledgmentAction.DEAD_LETTER, deadLetterReason: EMessageUnknowledgmentDeadLetterReason.TTL_EXPIRED, }; } if (message.isPeriodic()) { return { action: EMessageUnknowledgmentAction.DEAD_LETTER, deadLetterReason: EMessageUnknowledgmentDeadLetterReason.PERIODIC_MESSAGE, }; } if (message.hasRetryThresholdExceeded()) { return { action: EMessageUnknowledgmentAction.DEAD_LETTER, deadLetterReason: EMessageUnknowledgmentDeadLetterReason.RETRY_THRESHOLD_EXCEEDED, }; } const delay = message.producibleMessage.getRetryDelay(); return delay ? { action: EMessageUnknowledgmentAction.DELAY } : { action: EMessageUnknowledgmentAction.REQUEUE }; } function unknowledgeProcessingQueueMessage(redisClient, consumerId, queue, unknowledgmentReason, keys, args, unacknowledgementStatus, done) { args.push(JSON.stringify(queue), consumerId); const { keyConsumerQueues } = redisKeys.getConsumerKeys(consumerId); const { keyQueueProcessing } = redisKeys.getQueueConsumerKeys(queue, consumerId); const { keyQueueDL, keyQueueProcessingQueues, keyQueueConsumers, keyQueueProperties, keyQueueDelayed, keyQueueRequeued, } = redisKeys.getQueueKeys(queue, null); keys.push(keyQueueProcessing, keyQueueDelayed, keyQueueRequeued, keyQueueDL, keyQueueProcessingQueues, keyQueueConsumers, keyConsumerQueues, keyQueueProperties); processingQueue.fetchProcessingQueueMessage(redisClient, keyQueueProcessing, (err, message) => { if (err) return done(err); if (!message) { keys.push(''); args.push('', '', '', unknowledgmentReason, ''); return done(); } const messageId = message.getId(); const { keyMessage } = redisKeys.getMessageKeys(messageId); keys.push(keyMessage); args.push(messageId); const unknowledgementAction = getMessageUnknowledgementAction(message, unknowledgmentReason); const { action } = unknowledgementAction; const messageStatus = action === EMessageUnknowledgmentAction.DEAD_LETTER ? EMessagePropertyStatus.DEAD_LETTERED : action === EMessageUnknowledgmentAction.REQUEUE ? EMessagePropertyStatus.UNACK_REQUEUING : EMessagePropertyStatus.UNACK_DELAYING; args.push(action, action === EMessageUnknowledgmentAction.DEAD_LETTER ? unknowledgementAction.deadLetterReason : '', unknowledgmentReason, messageStatus); unacknowledgementStatus[messageId] = unknowledgementAction; done(); }); } export const processingQueue = { unknowledgeMessage(redisClient, consumerId, queues, logger, unknowledgmentReason, cb) { const { store, expire, queueSize } = Configuration.getSetConfig().messages.store.deadLettered; const keys = []; const args = [ EMessageUnknowledgmentAction.DELAY, EMessageUnknowledgmentAction.REQUEUE, EMessageUnknowledgmentAction.DEAD_LETTER, Number(store), expire, queueSize * -1, EMessageProperty.STATUS, EMessageUnknowledgmentReason.OFFLINE_CONSUMER, EMessageUnknowledgmentReason.OFFLINE_MESSAGE_HANDLER, ]; const messageHandlingStatus = {}; async.waterfall([ (next) => { if (queues === null) { consumerQueues.getConsumerQueues(redisClient, consumerId, next); } else { next(null, queues); } }, (queueParams, next) => { if (!queueParams.length) { return next(); } async.eachOf(queueParams, (queue, _, done) => { unknowledgeProcessingQueueMessage(redisClient, consumerId, queue, unknowledgmentReason, keys, args, messageHandlingStatus, done); }, next); }, ], (err) => { if (err) { return cb(err); } if (!keys.length) { return cb(); } redisClient.runScript(ELuaScriptName.HANDLE_PROCESSING_QUEUE, keys, args, (err, reply) => { if (err) { return cb(err); } if (reply !== 'OK') { return cb(new ConsumerError(reply ? String(reply) : undefined)); } Object.keys(messageHandlingStatus).forEach((messageId) => { const action = messageHandlingStatus[messageId].action === EMessageUnknowledgmentAction.DEAD_LETTER ? 'dead-lettered' : 'unacknowledged'; logger.debug(`Message ID ${messageId} has been ${action}.`); }); cb(null, messageHandlingStatus); }); }); }, fetchProcessingQueueMessage(redisClient, keyQueueProcessing, cb) { redisClient.lrange(keyQueueProcessing, 0, 0, (err, range) => { if (err) { return cb(err); } if (range && range.length) { _getMessage(redisClient, range[0], cb); } else { cb(); } }); }, getQueueProcessingQueues(redisClient, queue, cb) { const { keyQueueProcessingQueues } = redisKeys.getQueueKeys(queue, null); redisClient.hgetall(keyQueueProcessingQueues, cb); }, }; //# sourceMappingURL=processing-queue.js.map