UNPKG

@vpriem/kafka-broker

Version:

Easily compose and manage your kafka resources in one place

132 lines 4.47 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BatchProducer = exports.isProducerBatch = void 0; const events_1 = __importDefault(require("events")); const isProducerBatch = (record) => typeof record.topicMessages !== 'undefined'; exports.isProducerBatch = isProducerBatch; const toSingleTopicMessage = (topic, messages) => messages.flatMap((message) => ({ topic, message })); const noop = () => { /** */ }; class BatchProducer extends events_1.default { producer; topicMessages = []; timer; hrTime = process.hrtime(); isSending = false; batchSize; batchRequest = Promise.resolve(); lingerMs; config; constructor(producer, batchConfig) { super({ captureRejections: true }); this.producer = producer; const { size, lingerMs, ...config } = batchConfig; this.batchSize = size; this.lingerMs = lingerMs; this.config = config; this.producer.on('producer.connect', /* istanbul ignore next */ () => this.startAutoSendIfNecessary()); this.producer.on('producer.disconnect', () => this.stopAutoSendIfNecessary()); } get length() { return this.topicMessages.length; } elapsedMs() { const [s, ns] = process.hrtime(this.hrTime); return s * 1000 + ns / 1000000; } async sendIfNecessary() { if (this.isSending) return; if (this.topicMessages.length >= this.batchSize) { await this.sendAllBatch(); } else if (this.elapsedMs() >= this.lingerMs) { await this.sendOneBatch(); } } startAutoSendIfNecessary() { if (this.timer) return; this.timer = setInterval(() => { this.sendIfNecessary() .then(noop) .catch( /* istanbul ignore next */ (error) => this.emit('error', error)); }, this.lingerMs); } stopAutoSendIfNecessary() { clearInterval(this.timer); } push(record) { if ((0, exports.isProducerBatch)(record)) { this.topicMessages.push(...(record.topicMessages ?? []).flatMap(({ topic, messages }) => toSingleTopicMessage(topic, messages))); } else { this.topicMessages.push(...toSingleTopicMessage(record.topic, record.messages)); } this.sendIfNecessary() .then(noop) .catch( /* istanbul ignore next */ (error) => this.emit('error', error)); this.startAutoSendIfNecessary(); } createBatch(topicMessages) { const messagesByTopic = {}; topicMessages.forEach(({ topic, message }) => { if (!messagesByTopic[topic]) { messagesByTopic[topic] = { topic, messages: [], }; } messagesByTopic[topic].messages.push(message); }); return { ...this.config, topicMessages: Object.values(messagesByTopic), }; } async sendBatch() { await this.batchRequest; const messages = this.topicMessages.splice(0, this.batchSize); if (!messages.length) return; this.hrTime = process.hrtime(); const batch = this.createBatch(messages); this.emit('batch.start', batch); this.batchRequest = this.producer.sendBatch(batch).catch( /* istanbul ignore next */ (error) => { this.emit('error', error); }); await this.batchRequest; this.batchRequest = Promise.resolve(); } async sendOneBatch() { this.isSending = true; await this.sendBatch(); this.isSending = false; } async sendAllBatch() { this.isSending = true; while (this.topicMessages.length >= this.batchSize) { // eslint-disable-next-line no-await-in-loop await this.sendBatch(); } this.isSending = false; } async flush() { this.isSending = true; while (this.topicMessages.length) { // eslint-disable-next-line no-await-in-loop await this.sendBatch(); } this.isSending = false; } } exports.BatchProducer = BatchProducer; //# sourceMappingURL=BatchProducer.js.map