redis-smq
Version:
A simple high-performance Redis message queue for Node.js.
245 lines • 10.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Producer = void 0;
const redis_smq_common_1 = require("redis-smq-common");
const redis_client_js_1 = require("../../common/redis-client/redis-client.js");
const scripts_js_1 = require("../../common/redis-client/scripts/scripts.js");
const redis_keys_js_1 = require("../../common/redis-keys/redis-keys.js");
const index_js_1 = require("../../config/index.js");
const index_js_2 = require("../event-bus/index.js");
const _get_exchange_queues_js_1 = require("../exchange/_/_get-exchange-queues.js");
const index_js_3 = require("../exchange/index.js");
const index_js_4 = require("../message/index.js");
const message_envelope_js_1 = require("../message/message-envelope.js");
const index_js_5 = require("../queue/index.js");
const _schedule_message_js_1 = require("./_/_schedule-message.js");
const index_js_6 = require("./errors/index.js");
const event_bus_publisher_js_1 = require("./event-bus-publisher.js");
const queue_consumer_groups_cache_js_1 = require("./queue-consumer-groups-cache.js");
class Producer extends redis_smq_common_1.Runnable {
constructor() {
super();
this.queueConsumerGroupsHandler = null;
this.initQueueConsumerGroupsHandler = (cb) => {
this.queueConsumerGroupsHandler = new queue_consumer_groups_cache_js_1.QueueConsumerGroupsCache(this, this.redisClient, this.eventBus, this.logger);
this.queueConsumerGroupsHandler.run((err) => cb(err));
};
this.shutDownQueueConsumerGroupsHandler = (cb) => {
if (this.queueConsumerGroupsHandler) {
this.queueConsumerGroupsHandler.shutdown(() => {
this.queueConsumerGroupsHandler = null;
cb();
});
}
else
cb();
};
this.initRedisClient = (cb) => this.redisClient.getSetInstance((err, client) => {
if (err)
cb(err);
else if (!client)
cb(new redis_smq_common_1.CallbackEmptyReplyError());
else {
client.on('error', (err) => this.handleError(err));
cb();
}
});
this.redisClient = new redis_client_js_1.RedisClient();
this.redisClient.on('error', (err) => this.handleError(err));
this.eventBus = new index_js_2.EventBus();
this.eventBus.on('error', (err) => this.handleError(err));
this.logger = redis_smq_common_1.logger.getLogger(index_js_1.Configuration.getSetConfig().logger, `producer:${this.id}`);
if (index_js_1.Configuration.getSetConfig().eventBus.enabled) {
(0, event_bus_publisher_js_1.eventBusPublisher)(this, this.eventBus, this.logger);
}
}
getLogger() {
return this.logger;
}
goingUp() {
return super.goingUp().concat([
this.redisClient.init,
this.eventBus.init,
(cb) => {
this.emit('producer.goingUp', this.id);
cb();
},
this.initRedisClient,
this.initQueueConsumerGroupsHandler,
]);
}
up(cb) {
super.up(() => {
this.emit('producer.up', this.id);
cb(null, true);
});
}
goingDown() {
this.emit('producer.goingDown', this.id);
return [
this.shutDownQueueConsumerGroupsHandler,
this.redisClient.shutdown,
].concat(super.goingDown());
}
down(cb) {
super.down(() => {
this.emit('producer.down', this.id);
setTimeout(() => this.eventBus.shutdown(() => cb(null, true)), 1000);
});
}
getQueueConsumerGroupsHandler() {
if (!this.queueConsumerGroupsHandler)
throw new redis_smq_common_1.PanicError(`Expected an instance of QueueConsumerGroupsHandler`);
return this.queueConsumerGroupsHandler;
}
enqueue(redisClient, message, cb) {
var _a;
const messageState = message.getMessageState();
messageState.setPublishedAt(Date.now());
const messageId = message.getId();
const keys = redis_keys_js_1.redisKeys.getQueueKeys(message.getDestinationQueue(), message.getConsumerGroupId());
const { keyMessage } = redis_keys_js_1.redisKeys.getMessageKeys(messageId);
const scriptArgs = [
index_js_5.EQueueProperty.QUEUE_TYPE,
index_js_5.EQueueProperty.MESSAGES_COUNT,
index_js_5.EQueueType.PRIORITY_QUEUE,
index_js_5.EQueueType.LIFO_QUEUE,
index_js_5.EQueueType.FIFO_QUEUE,
(_a = message.producibleMessage.getPriority()) !== null && _a !== void 0 ? _a : '',
messageId,
index_js_4.EMessageProperty.STATUS,
index_js_4.EMessagePropertyStatus.PENDING,
index_js_4.EMessageProperty.STATE,
JSON.stringify(messageState),
index_js_4.EMessageProperty.MESSAGE,
JSON.stringify(message.toJSON()),
];
redisClient.runScript(scripts_js_1.ELuaScriptName.PUBLISH_MESSAGE, [
keys.keyQueueProperties,
keys.keyQueuePriorityPending,
keys.keyQueuePending,
keys.keyQueueMessages,
keyMessage,
], scriptArgs, (err, reply) => {
if (err)
return cb(err);
switch (reply) {
case 'OK':
return cb();
case 'QUEUE_NOT_FOUND':
return cb(new index_js_6.ProducerQueueNotFoundError());
case 'MESSAGE_PRIORITY_REQUIRED':
return cb(new index_js_6.ProducerMessagePriorityRequiredError());
case 'PRIORITY_QUEUING_NOT_ENABLED':
return cb(new index_js_6.ProducerPriorityQueuingNotEnabledError());
case 'UNKNOWN_QUEUE_TYPE':
return cb(new index_js_6.ProducerUnknownQueueTypeError());
default:
return cb(new index_js_6.ProducerError());
}
});
}
produceMessageItem(redisClient, message, queue, cb) {
const messageId = message
.setDestinationQueue(queue)
.getMessageState()
.getId();
const handleResult = (err) => {
if (err) {
cb(err);
}
else {
const action = message.isSchedulable() ? 'scheduled' : 'published';
this.logger.info(`Message (ID ${messageId}) has been ${action}.`);
if (!message.isSchedulable()) {
this.emit('producer.messagePublished', messageId, { queueParams: queue, groupId: message.getConsumerGroupId() }, this.id);
}
cb(null, messageId);
}
};
if (message.isSchedulable()) {
(0, _schedule_message_js_1._scheduleMessage)(redisClient, message, handleResult);
}
else {
this.enqueue(redisClient, message, handleResult);
}
}
produceMessage(redisClient, message, queue, cb) {
const { exists, consumerGroups } = this.getQueueConsumerGroupsHandler().getConsumerGroups(queue);
if (exists) {
if (!consumerGroups.length) {
cb(new index_js_6.ProducerQueueMissingConsumerGroupsError());
}
const ids = [];
redis_smq_common_1.async.eachOf(consumerGroups, (group, _, done) => {
const msg = new message_envelope_js_1.MessageEnvelope(message).setConsumerGroupId(group);
this.produceMessageItem(redisClient, msg, queue, (err, reply) => {
if (err)
done(err);
else {
ids.push(String(reply));
done();
}
});
}, (err) => {
if (err)
cb(err);
else
cb(null, ids);
});
}
else {
const msg = new message_envelope_js_1.MessageEnvelope(message);
this.produceMessageItem(redisClient, msg, queue, (err, reply) => {
if (err)
cb(err);
else
cb(null, [String(reply)]);
});
}
}
produce(msg, cb) {
if (!this.isUp()) {
return cb(new index_js_6.ProducerInstanceNotRunningError());
}
const redisClient = this.redisClient.getInstance();
if (redisClient instanceof Error) {
return cb(redisClient);
}
const exchangeParams = msg.getExchange();
if (!exchangeParams) {
return cb(new index_js_6.ProducerMessageExchangeRequiredError());
}
if (exchangeParams.type === index_js_3.EExchangeType.DIRECT) {
const queue = exchangeParams.params;
return this.produceMessage(redisClient, msg, queue, cb);
}
(0, _get_exchange_queues_js_1._getExchangeQueues)(redisClient, exchangeParams, (err, queues) => {
if (err) {
return cb(err);
}
if (!(queues === null || queues === void 0 ? void 0 : queues.length)) {
return cb(new index_js_6.ProducerExchangeNoMatchedQueueError());
}
const messages = [];
redis_smq_common_1.async.eachOf(queues, (queue, index, done) => {
this.produceMessage(redisClient, msg, queue, (err, reply) => {
if (err) {
return done(err);
}
if (reply) {
messages.push(...reply);
}
done();
});
}, (err) => {
if (err) {
return cb(err);
}
cb(null, messages);
});
});
}
}
exports.Producer = Producer;
//# sourceMappingURL=producer.js.map