Open Crispy1975 opened 3 years ago
Hi,
In our company wa are using the consumer(non-following mode) and work fine to handle back-pressure (with the async module or with out it).
consumerOnReady(consumer) {
logger.debug(`[Consumer] - consumerEvents - consumer ready.`);
logger.info(`[Consumer] - consumerEvents - Subscribing to Topic: '${kafka.topic.split(",")}'`);
consumer.subscribe(kafka.topic.split(","));
consumer.consume(this.maxPollRecords, this.onData);
this.start(consumer);
consumerInstance = consumer;
}
onDataCallback(consumer, err, msg) {
try {
if (err) {
if (err.message === failuresMessage.CONSUMER_NOT_CONNECTED) process.exit(1);
else logger.error(`[Consumer] - onDataCallback - error: ${err}`);
}
if (msg && msg.length) {
logger.debug(`[Consumer] - onDataCallback - poll returns: ${msg.length} Records`);
msg.forEach(record => this.notifyStartProcessing(record));
this.q.push(msg);
}
if (this.q.length() > queue.asyncMaxQueueSize) {
consumer.pause(consumer.assignments());
this.paused = true;
logger.debug(`[Consumer] - onDataCallback - consumer paused`);
} else {
if (this.paused) {
logger.debug(`[Consumer] - onDataCallback - consumer resumed`);
this.paused = false;
consumer.resume(consumer.assignments());
}
consumer.consume(this.maxPollRecords, this.onData);
}
} catch (error) {
logger.error(`[Consumer] - onDataCallback - handle error: ${error}`);
}
}
queueOnDrain(consumer) {
logger.debug(`[Consumer] - consumerEvents - queue drain`);
logger.debug(`[Consumer] - consumerEvents - is paused ${this.paused}`);
if (this.paused) {
this.paused = false;
consumer.resume(consumer.assignments());
logger.debug(`[Consumer] - consumerEvents - consumer resumed`);
}
consumer.consume(this.maxPollRecords, this.onData);
}
/**
* Kafka consumser event listner, push on the queue all event received from kafka
* The queue is used to handle backpressure, if the length of the queue is greatter than the asyncMaxQueueSize the consumer is paused,
* will be resumed only when the queue is drained (all events are processed successfully or errored).
*/
consumerEvents() {
const consumer = new rdKafka.KafkaConsumer(ConsumerConfig.globalConfig(this), ConsumerConfig.topicConfig());
consumer.setDefaultConsumeTimeout(kafka.consumerDefaultTimeout);
consumer.connect({ timeout: kafka.connectionTimeout }, (err) => this.consumerOnConnect(err));
consumer.on('ready', () => this.consumerOnReady(consumer));
consumer.on('warning', warn => logger.warn(`[Consumer] - consumerEvents - warning ${JSON.stringify(warn)}`));
consumer.on('event.log', log => logger.warn(`[Consumer] - consumerEvents - event.log ${JSON.stringify(log)}`)); //logging debug messages, if debug is enabled
consumer.on('event.error', err => logger.error(`[Consumer] - consumerEvents - event.error ${JSON.stringify(err)}`)); //logging all errors
consumer.on('disconnected', (arg) => logger.info(`[Consumer] - consumerEvents - consumer disconnected: ${JSON.stringify(arg)}`));
this.onData = (err, msg) => this.onDataCallback(consumer, err, msg);
this.q.drain(() => this.queueOnDrain(consumer));
this.q.error((err, task) => logger.error(`[Consumer] - consumerEvents - async queue error: ${err}`, { task, stackTrace: err.stack }));
}
Thanks for the example @syahiaoui - I suspect I have a leak somewhere else after the consumer code... I will dig into that.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
@syahiaoui I think if you start using streams api instead of the traditional consumer, you don't need to handle this back pressure manually. Please correct me if I am wrong.
@sathyarajagopal The consumer's stream API extends the native class of Readable, but in the past, there was a problem of not stopping reading messages when the internal buffer has reached the threshold of highWaterMark
.
(I don't know if this has been fixed)
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
@syahiaoui do you have the full code to the great example you provided of the consumer(non-following mode)? Thanks again for this, it's very elegant!
@syahiaoui I was able to get it working, thanks, it's a very clear solution for our problem!
Hi. I've been using
node-rdkafka
for a little while and overall I am very pleased with how it works, great lib! I do have a question however that I am not 100% on the answer to. I have a consumer process that needs to be able to handle back-pressure effectively as I am doing ETL into a slower database cluster, so I don't want the consumer to be overwhelmed and go OOM. However, I am getting OOM issues periodically... I was looking through the library code on the JS side and saw this comment: https://github.com/Blizzard/node-rdkafka/blob/master/lib/kafka-consumer.js#L382This suggests that using the
consume()
method is going to result in OOMs in situations like mine. I have setlibrdkafka
settings such asqueued.min.messages
so it only grabs a smaller number of messages in the background. I've also built an internal queue in my process with a pause/resume mechanism to allow for more control. For the most part previous OOMs have much reduced, however as mentioned I still do get them. Below is a skeleton version of what I have running, comments on any issues or reasons for the OOM would be greatly appreciated.The consumer settings are as follows:
I am considering switching from the style listed above to making use of the callback on the
consume()
method but before that it would be interesting to see if there is something obvious I am doing (or not) with the above code.