kafka-ts
Version:
**KafkaTS** is a Apache Kafka client library for Node.js. It provides both a low-level API for communicating directly with the Apache Kafka cluster and high-level APIs for publishing and subscribing to Kafka topics.
133 lines (132 loc) • 5.41 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Cluster = void 0;
const api_1 = require("./api");
const broker_1 = require("./broker");
const error_1 = require("./utils/error");
const logger_1 = require("./utils/logger");
const shared_1 = require("./utils/shared");
const tracer_1 = require("./utils/tracer");
const trace = (0, tracer_1.createTracer)('Cluster');
class Cluster {
options;
seedBroker;
brokerById = {};
brokerMetadata = {};
constructor(options) {
this.options = options;
}
async connect() {
this.seedBroker = await this.findSeedBroker();
this.brokerById = {};
await this.refreshBrokerMetadata();
}
async disconnect() {
await Promise.all([
this.seedBroker?.disconnect(),
...Object.values(this.brokerById).map((x) => x.disconnect()),
]);
}
ensureConnected = (0, shared_1.shared)(async () => {
if (!this.seedBroker) {
return this.connect();
}
const brokers = [
{
broker: this.seedBroker,
handleError: async (error) => {
logger_1.log.debug(`Failed to connect to seed broker. Reconnecting...`, { reason: error.message });
await this.seedBroker?.disconnect();
this.seedBroker = await this.findSeedBroker();
},
},
...Object.entries(this.brokerById).map(([nodeId, broker]) => ({
broker,
handleError: async (error) => {
logger_1.log.debug(`Failed to connect to broker ${nodeId}. Disconnecting...`, { reason: error.message });
await broker.disconnect();
delete this.brokerById[parseInt(nodeId)];
},
})),
];
await Promise.all(brokers.map(async ({ broker, handleError }) => {
try {
await broker.connect();
}
catch (error) {
await handleError(error);
}
}));
});
setSeedBroker = async (nodeId) => {
const broker = await this.acquireBroker(nodeId);
await this.seedBroker?.disconnect();
this.seedBroker = broker;
};
sendRequest = async (...args) => {
return this.seedBroker.sendRequest(...args);
};
sendRequestToNode = (nodeId) => async (...args) => {
if (!this.brokerById[nodeId]) {
this.brokerById[nodeId] = await this.acquireBroker(nodeId);
}
return this.brokerById[nodeId].sendRequest(...args);
};
async acquireBroker(nodeId) {
if (!(nodeId in this.brokerMetadata))
await this.refreshBrokerMetadata();
if (!(nodeId in this.brokerMetadata))
throw new error_1.ConnectionError(`Broker ${nodeId} is not available`);
const broker = new broker_1.Broker({
clientId: this.options.clientId,
sasl: this.options.sasl,
ssl: this.options.ssl,
requestTimeout: this.options.requestTimeout,
options: this.brokerMetadata[nodeId],
});
await broker.connect();
return broker;
}
async findSeedBroker() {
const randomizedBrokers = this.options.bootstrapServers.toSorted(() => Math.random() - 0.5);
for (const options of randomizedBrokers) {
try {
const broker = new broker_1.Broker({
clientId: this.options.clientId,
sasl: this.options.sasl,
ssl: this.options.ssl,
requestTimeout: this.options.requestTimeout,
options,
});
await broker.connect();
return broker;
}
catch (error) {
logger_1.log.debug(`Failed to connect to seed broker ${options.host}:${options.port}`, {
reason: error.message,
});
}
}
throw new error_1.KafkaTSError('No seed brokers found');
}
async refreshBrokerMetadata() {
const metadata = await this.sendRequest(api_1.API.METADATA, { topics: [] });
this.brokerMetadata = Object.fromEntries(metadata.brokers.map((options) => [options.nodeId, options]));
}
}
exports.Cluster = Cluster;
__decorate([
trace((nodeId) => ({ nodeId, result: `<Broker ${nodeId}>` })),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Number]),
__metadata("design:returntype", Promise)
], Cluster.prototype, "acquireBroker", null);