redis-smq
Version:
A simple high-performance Redis message queue for Node.js.
143 lines • 6.59 kB
JavaScript
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