kafka-consumer-manager
Version:
Kafka consumer manager handles retries, offset issues and retries
80 lines (70 loc) • 3.19 kB
JavaScript
let async = require('async');
module.exports = class KafkaThrottlingManager {
init(messagesInMemoryThreshold, interval, topics, callbackPromise, callbackErrorPromise, kafkaStreamConsumer, logger) {
Object.assign(this, {
logger: logger,
kafkaStreamConsumer: kafkaStreamConsumer,
messagesInMemoryThreshold: messagesInMemoryThreshold,
callbackPromise: callbackPromise,
callbackErrorPromise: callbackErrorPromise,
innerQueues: {}
});
topics.forEach(topic => {
this.innerQueues[topic] = {};
});
this.intervalId = setInterval(function () {
manageQueues(this.kafkaStreamConsumer, this.messagesInMemoryThreshold, this.innerQueues, this.logger);
}.bind(this), interval);
}
handleIncomingMessage(message) {
let partition = message.partition;
let topic = message.topic;
if (!this.innerQueues[topic][partition]) {
this.innerQueues[topic][partition] = generateThrottlingQueueInstance(this.callbackPromise, this.callbackErrorPromise, this.logger);
}
this.innerQueues[topic][partition].push(message, () => {
this.kafkaStreamConsumer.commit(message);
});
}
stop() {
clearInterval(this.intervalId);
}
};
function generateThrottlingQueueInstance(callbackPromise, callbackErrorPromise, logger) {
let queue = async.queue(function (message, commitOffsetCallback) {
return callbackPromise(message).then(() => {
logger.trace(`kafkaThrottlingManager finished handling message: topic: ${message.topic}, partition: ${message.partition}, offset: ${message.offset}`);
commitOffsetCallback(message);
}).catch((err) => {
commitOffsetCallback(message);
logger.error('MessageFunction was rejected', err);
return callbackErrorPromise(message, err);
}).catch((err) => {
logger.error('ErrorMessageFunction invocation was rejected', err);
});
}.bind({logger}), 1);
return queue;
}
function getQueueLengthsByTopic(innerQueues) {
let queueSizesByTopic = {};
Object.keys(innerQueues).forEach(topic => {
let specificTopicQueueSizes = [];
Object.keys(innerQueues[topic]).forEach((key) => {
specificTopicQueueSizes.push(innerQueues[topic][key].length());
});
queueSizesByTopic[topic] = specificTopicQueueSizes;
});
return queueSizesByTopic;
}
function manageQueues(kafkaStreamConsumer, messagesInMemoryThreshold, innerQueues, logger) {
logger.trace('managing queues..');
let lengthsByTopic = getQueueLengthsByTopic(innerQueues);
let totalMessagesInQueues = 0;
Object.keys(lengthsByTopic).forEach(topic => {
totalMessagesInQueues += lengthsByTopic[topic].reduce((a, b) => a + b, 0);
});
logger.trace('Total messages in queues are: ' + totalMessagesInQueues);
let shouldResume = totalMessagesInQueues < messagesInMemoryThreshold;
kafkaStreamConsumer.setThirsty(shouldResume);
shouldResume ? kafkaStreamConsumer.resume() : kafkaStreamConsumer.pause();
}