Node Kakfa receiving BrokerNotAvailable errors - node.js

I'm trying to get started with NodeJS and Kafka. I have one Kafka server running in a docker container with one Zookeeper server.
I can successfully connect a listener to my topic and when my code behaves I see messages passed from Producer to Consumer.
However - more often than not, my code fails with an error. Here are two consecutive attempts to Produce a message into the Kafka topic
2018-01-09T22:14:29.061Z - info: KafkaProducer: client is ready
err: null
data: [object Object]
err: BrokerNotAvailableError: Broker not available
data: undefined
2018-01-09T22:15:03.770Z - info: KafkaProducer: client is ready
The code that I'm using to publish the message is here:
'use strict';
const log = require('./Logger');
const kafka = require('kafka-node');
const Producer = kafka.Producer;
const KeyedMessage = kafka.KeyedMessage;
const Client = kafka.Client;
const Promise = require('bluebird');
const uuid = require('uuid');
const client = new Client('192.168.99.100:2181', 'test', {
sessionTimeout: 300,
spinDelay: 100,
retries: 2
});
const producer = new Producer(client, {requireAcks: 1});
producer.on('ready', () => {
log.info('KafkaProducer: client is ready');
});
producer.on('error', (err) => {
log.error(`KafkaProducer: ${err}`);
});
class KafkaProducer {
constructor() {}
sendRecord(type, userId, sessionId, data, callback) {
if (!userId) {
return callback(new Error('A userId must be provided.'));
}
const event = {
id: uuid.v4(),
timestamp: Date.now(),
userId: userId,
sessionId: sessionId,
type: type,
data: data
};
const buffer = new Buffer.from(JSON.stringify(event));
// Create a new payload
const record = [
{
topic: 'datastore',
messages: buffer,
attributes: 1 /* Use GZip compression for the payload */
}
];
//Send record to Kafka and log result/error
producer.send(record, callback);
}
}
module.exports = KafkaProducer;
Not particularly elegant code, but I'm just trying to get a simple use case working before integrating Kafka into my application.
If it is relevant this are my Dockerfiles for Kafka and Zookeeper
FROM wurstmeister/kafka:1.0.0
ENV KAFKA_ADVERTISED_HOST_NAME service-kafka
ENV KAFKA_PORT 9092
ENV KAFKA_HOST_NAME service-kafka
ENV KAFKA_ZOOKEEPER_CONNECT service-zookeeper:2181
ENV KAFKA_CREATE_TOPICS "auth:1:1,datastore:1:1,transactions:1:1"
and
FROM zookeeper:latest
This comment in a GitHub issue seems to suggest that it is the applications responsibility to catch connection errors and retry the message, but this happens so frequently there is something else wrong that I need some guidance with. Thanks

Related

How to consume data from AWS Kinesis fanout using nodejs #aws-sdk/client-kinesis lib v3?

How to consume data from AWS Kinesis fanout using nodejs #aws-sdk/client-kinesis v3 lib?
I registered a StreamConsumer and got a connection to a shard, but how do I consume data?
const client = new KinesisClient({ region });
//recover a consumer or create a new one
const { Consumers } = await client.send(new ListStreamConsumersCommand({ StreamARN }));
let consumer = Consumers.find((x) => x.ConsumerName == ConsumerName);
if (!consumer) {
const { Consumer } = await client.send(new RegisterStreamConsumerCommand({ StreamARN, ConsumerName }));
consumer = Consumer;
}
//subscribe to shard
const { EventStream } = await client.send(
new SubscribeToShardCommand({
ConsumerARN: consumer.ConsumerARN,
ShardId: shardId,
StartingPosition: { Type: 'TRIM_HORIZON' },
}),
);
Log of Consumers and EventStream looks like this:
Consumers: [
{
ConsumerARN: 'arn:aws:kinesis:ap-south-1:123123123:stream/test-stream/consumer/test-client:234234234',
ConsumerCreationTimestamp: 2022-12-07T04:33:20.000Z,
ConsumerName: 'test-client',
ConsumerStatus: 'ACTIVE'
}
]
EventStream:
{
[Symbol(Symbol.asyncIterator)]: [AsyncGeneratorFunction: [Symbol.asyncIterator]]
}

Google Cloud Function don't publish on PubSub, Timeout exceeded

first thanks for reading this and try to answer it :)
On GCP (Google Cloud Plateform), i've got a database stored on cloud SQL,
2 Cloud functions, the first one is used to make a request on the database (this request can have more that 1000 result) then publish the result on PubSub, and the second one is used to do web scraping thanks to puppeteer.
first cloud functions code:
//[Requirement]
const mysql = require('mysql')
const {SecretManagerServiceClient} = require('#google-cloud/secret-manager')
const ProjectID = process.env.secretID
const SqlPass = `projects/xxx`
const client = new SecretManagerServiceClient()
const {PubSub} = require('#google-cloud/pubsub');
const pubSubClient = new PubSub();
const topicName = "xxxxxx";
//[/Requirement]
exports.LaunchAudit = async () => {
const dbSocketPath = "/cloudsql"
const DB_USER = "xxx"
const DB_PASS = await getSecret()
const DB_NAME = "xxx"
const CLOUD_SQL_CONNECTION_NAME = "xxx"
//[SQL CONNEXION]
let pool = mysql.createPool({
connectionLimit: 1,
socketPath: `${dbSocketPath}/${CLOUD_SQL_CONNECTION_NAME}`,
user: DB_USER,
password: DB_PASS,
connectTimeout: 500,
database: DB_NAME
})
//[/SQL CONNEXION]
//set the request
let sql = `select * from * where *;`
//make the setted request
await pool.query(sql, async (e,results) => {
//if there is an error send it
if(e){
throw e
}
//for each result of the query, log it and publish on PubSub ("Audit-property" topic)
results.forEach(async element => {
console.log(JSON.stringify(element))
await msgPubSub(JSON.stringify(element))
})
})
}
async function msgPubSub(data){
//console.log(data)
const messageBuffer = Buffer.from(data)
try {
const topicPublisher = await pubSubClient.topic(topicName).publish(messageBuffer)
console.log("Message id: " + topicPublisher)
} catch (error) {
console.error(`Error while publishing message: ${error.message}`)
}
}
firstly, when it works, it takes long to publish on PubSub topic the first message, something like 6 minutes, why there is this delay ? And When i do a big request (something like 500+ result) i've got a Timeout error : Total timeout of API google.pubsub.v1.Publisher exceeded 600000 milliseconds before any response was received.
I've tried to publish a batched message, add some memory to the cloud functions, use google-gax, but got the same result.
i'm using nodejs10.
2nd cloud functions message part code:
exports.MainAudit = async message => {
const property = Buffer.from(message.data, 'base64').toString()
const pProperty = JSON.parse(property)
console.log(property)
}
package.json dependencies:
"dependencies": {
"#google-cloud/pubsub": "^2.6.0",
"#google-cloud/secret-manager": "^3.2.0",
"google-gax": "^2.9.2",
"mysql": "^2.18.1",
"node-fetch": "^2.6.1"
}
Log + Timestamp:
As the code is now, you are creating a new instance of a publisher for each message you publish. This is because pubSubClient.topic(topicName) creates an instance for publishing to the topic. Therefore, you are paying the overhead of establishing a connection for each message you send. Instead, you'd want to create that object a single time and reuse it:
const pubSubClient = new PubSub();
const topicName = "xxxxxx";
const topicPublisher = pubSubClient.topic(topicName)
However, this still leaves an inefficiency in your application where you are waiting for each message to publish before starting the next publish due to the use of await on the publish call and the call to msgPubSub. The Pub/Sub client library can batch messages togetherr for more efficient sending, but you'd need to allow multiple calls to publish to be outstanding to take advantage of it. You'd want to await on a Promise.all of the list of promises returned from publishing.

Nodejs Elastic benastalk refused to connect to upsteam/ upsteam prematurely closed

I am getting the following errors when running my application in elastic beanstalk: [error] 3636#0: *295 upstream prematurely closed connection while reading response header from upstream and [error] 3636#0: *295 connect() failed (111: Connection refused) while connecting to upstream Its strange because if I hit those routes independently it works fine. It only appears to error when firing those routes from my vuex action.
The following is the log from the AWS elastic beanstalk.
The following is the network tab when it hits my FFmpeg route:
The following is the generate video action as fired from vuex.
async [GENERATE_VIDEO]({state, rootState, dispatch, commit}){
const username = rootState.user.currentUser.username;
const s3Id = rootState.templates.currentVideo.stock_s3_id;
const type = rootState.dataClay.fileFormat || state.type;
const vid = new Whammy.fromImageArray(state.captures, 30);
vid.lastModifiedDate = new Date();
vid.name = "canvasVideo.webm";
const data = new FormData();
const id = `${username}_${new Date().getTime()}`;
data.append("id", id);
data.append("upload", vid);
const projectId = await dispatch(INSERT_PROJECT);
await dispatch(UPLOAD_TEMP_FILE, data);
const key = await dispatch(CONVERT_FILE_TYPE, { id, username, type, projectId});
const role = rootState.user.currentUser.role;
state.file = `/api/files/${key}`;
let message;
if(role!='banner'){
message =`<p>Your video is ready.</p> Download`;
} else {
message = `<p>Your video is ready. You may download your file from your banner account</p>`;
const resolution = rootState.dataClay.matrix[0];
await dispatch(EXPORT_TO_BANNER, { s3Id, fileUrl: key, extension: `.${type}`, resolution});
}
And here are the api routes called in the actions.
async [UPLOAD_TEMP_FILE]({ commit }, data) {
try {
const response = await axios.post("/api/canvas-editor/upload-temp", data);
return response.data;
} catch (error) {
console.log(error);
}
},
async [CONVERT_FILE_TYPE]({commit}, data) {
try{
const response = await axios.post("/api/canvas-editor/ffmpeg", data);
return response.data;
} catch(error){
console.log(error);
}
}
}
As I said all my routes work and the application runs as expected on localhost however when uploaded to aws I receive unexpected errors.
After some digging I found out that I did not set the ffmpeg path.
Once this was done it worked great.
const ffmpeg = require('fluent-ffmpeg');
const ffmpegPath = require('#ffmpeg-installer/ffmpeg').path;
ffmpeg.setFfmpegPath(ffmpegPath);
module.exports = ffmpeg;

Writing a unit test case in node.js using mocha to mock a azure service bus queue to recive messages

I have written a unit test case,but it is giving error.
Please find the code below
index.js
const { ServiceBusClient, ReceiveMode } = require("#azure/service-bus");
module.exports = async function (context, myTimer) {
// Define connection string and related Service Bus entity names here
const connectionString = process.env['serviceBusConnectionString'];
const queueName = process.env['serviceBusQueueName'];
const sbClient = ServiceBusClient.createFromConnectionString(connectionString);
const queueClient = sbClient.createQueueClient(queueName);
//const receiver = queueClient.createReceiver(ReceiveMode.receiveAndDelete);
const receiver = queueClient.createReceiver(ReceiveMode.peekLock);
const messages = await receiver.receiveMessages(1);
try {
let payloads = [];
messages.forEach((msg) => {
payloads.push(msg.body);
})
await queueClient.close();
} catch (err) {
context.log('Queue message status settle: abandon');
await messages[0].abandon();
console.log('Error ', err);
} finally {
await sbClient.close();
context.done();
}
};
This is the unit test file and I am getting error.Please let me know why I am getting this errorenter image description here
indexTest.js:
beforeEach(() => {
const sbClientStub = {
createQueueClient: sinon.stub().returnsThis(),
createReceiver: sinon.stub().returnsThis(),
receiveMessages:sinon.stub(),
close: sinon.stub(),
};
sinon.stub(ServiceBusClient, 'createFromConnectionString').callsFake(() => sbClientStub);
const ctx = {};
// const actual = await pushToQueue(message, ctx);
// sinon.assert.match(actual, 2);
sinon.assert.calledWithExactly(ServiceBusClient.createFromConnectionString, undefined);
sinon.assert.calledWithExactly(sbClientStub.createQueueClient, undefined);
sinon.assert.calledOnce(sbClientStub.createReceiver, undefined );
//sinon.assert.calledWithExactly(sbClientStub.send.firstCall, { body: 'a' });
//sinon.assert.calledWithExactly(sbClientStub.send.secondCall, { body: 'b' });
sinon.assert.calledTwice(sbClientStub.close);
});
You should replace every sinon.stub() with sinon.spy(). The stub will prevent calling the original implementation of methods, but spies will do. They basically have the same APIs.
In order to call the original methods of #azure/service-bus, make sure the resources of #azure/service-bus are ready such as environment variables, service account, queue and so on.
If you do this, the unit tests are no longer isolated. In fact, they are no longer unit tests, but integration tests, or e2e tests.

Is there any way to use confluent schema registry with kafka-node module?

I have implemented Avro schema in node.js with schema being sent with the message payload. And it is working fine. I am looking if there is any way I can use schema-registry with Kafka-node module. I have explored but was not successful in finding any.
And sending schema in each message increase the message size? Does it affect the performance compared to using schema registry?
Any help in this will be appreciated.
You can use "avro-schema-registry" module.
It is working for me. I am also new to Kafka, just trying it out.
const kafka = require('kafka-node');
const avroSchemaRegistry = require('avro-schema-registry');
/* Configuration */
const kafkaTopic = 'newkafkatopic';//'kafka.test';
const host = 'localhost:9092';
const schemaRegistry = 'http://localhost:8081';
const Consumer = kafka.Consumer;
const Client = kafka.KafkaClient;
const registry = avroSchemaRegistry(schemaRegistry);
var client = new Client(host);
var topics = [{
topic: kafkaTopic
}];
var options = {
autoCommit: false,
fetchMaxWaitMs: 1000,
fetchMaxBytes: 1024 * 1024,
encoding: 'buffer'
};
var consumer = new Consumer(client, topics, options);
consumer.on('message', function(rawMessage) {
console.log("Raw Message", rawMessage);
registry.decode(rawMessage.value)
.then((msg) => {
console.log(msg)
})
.catch(err=>console.log(err))
});
consumer.on('error', (e) => {
console.log(e.message)
consumer.close();
})

Resources