Setting Heartbeat when connecting to RabbitMQ via Amqplib - node.js

When connecting to RabbitMQ via amqplib, the value of the Heartbeat parameter cannot be set.
I do this:
// NPM Modules
const amqp = require('amqplib/callback_api');
const withAutoRecovery = require('amqplib-auto-recovery');
// Application configuration
const config = settings.getConfig();
withAutoRecovery(amqp, {
onError: (err) => { console.log(err.message); },
isErrorUnrecoverable: (err) => { console.log(err) }
}).connect(config.rabbitmq, (err, connection) => {
// If there are connection errors
if (err) {
console.error(this.conn_str_amqp);
console.error('[AMQP] Connection error:', err.message);
} else if (connection) {
// Connection Error Event
connection.on('error', (err) => {
if (err.message !== 'Connection closing') {
console.error('[AMQP] Closing connection', err.message);
}
});
// Error event when you close the connection
connection.on('close', () => {
console.error('[AMQP] Closing connection');
});
// Hint to user
console.log('[AMQP] Connected');
}
});
Here config.rabbitmq is:
{
protocol: 'amqp',
hostname: 'localhost',
port: 5672,
username: 'guest',
password: 'guest',
locale: 'en_US',
frameMax: 0x1000,
heartbeat: 1800,
vhost: '/',
}
I expect to get the Heartbeat = 1800s parameter value, in the UI Managment window the default value Heartbeat = 60s is displayed.
Screenshot:
I tried to pass another object instead config.rabbitmq line (https://www.rabbitmq.com/uri-spec.html and https://www.rabbitmq.com/uri-query-parameters.html) in the format:
function getRabbitMQConnectionString() {
const rabbitmq = config.rabbitmq;
return `${rabbitmq.protocol}://${rabbitmq.username}:${rabbitmq.password}#${rabbitmq.hostname}:${rabbitmq.port}${encodeURIComponent(rabbitmq.vhost)}?heartbeat=${rabbitmq.heartbeat}`;
}
Thus, too, it turned out similar to the above effect.
Tell me, please, how to correctly set the Heartbeat parameter through the Node.js client application on amqplib?

For some reason I set up the heartbeat to 30 s and it worked.

From RabbitMQ documentation, they suggest not to set the heartbeats high, because it can disable them.
However, I don't know what is shown as 60s heartbeats.
https://www.rabbitmq.com/heartbeats.html#disabling

Reason
The reason of getting heartbeat 60s in Management UI is because the RabbitMQ server have default heartbeat 60s.
Explanation
And during connection creation the minimum of both client(amqplib) and server(RabbitMQ) heartbeat value is taken.
Solution
So, to increate the heartbeat value from amqplib, we need to have more or equal value set in RabbitMQ configurations
Methods to Set heartbeat value for RabbitMQ Server
How to change RabbitMQ Heartbeat without restart
https://www.rabbitmq.com/configure.html#:~:text=Max%20value%3A%20536870912-,heartbeat,-Value%20representing%20the
Reference for more detail
https://www.rabbitmq.com/heartbeats.html
https://www.rabbitmq.com/configure.html

Related

Is it normal for app to still receiving request while MongoDB is closing it's connection?

The app trying to use MongoDB connection that has already closed so I got error :
So I use Kubernetes GKE for my app and I only have one way to disconnect to MongoDB that is triggered by SIGTERM or SIGINT.
Stack that I used
NodeJS 16.19.0, Mongoose 5.8.1, GKE as infra
this is how I close the connection :
const gracefulExit = async (args) => {
console.log('RECEIVED GRACEFUL EXIT', args);
await agenda.stop();
await mongoose.connection.close();
console.info('Mongoose disconnected on app termination');
process.exit(0);
};
process
.on('SIGINT', gracefulExit)
.on('SIGTERM', gracefulExit);
the await process takes seconds to finish (4-7s) so I think that is why it still receive some requests.
So is it normal for app to still receiving request while MongoDB is closing it's connection? or should I just leave the connection and not close it? if not then what can I do to fix this? Thanks!
WHAT I TRIED?
Reconnect to MongoDB if it's disconnected, I know it's strange because the app will go down anyway, but the error still persists
try {
if (!filter) {
throw 'GET ONE: No filter supplied';
}
if(mongoose.connection.readyState !== 1) {
console.log("reconnecting...", mongoose.connection.readyState)
await connectDbAsync()
console.log("reconnected", mongoose.connection.readyState)
}
const result = await this.model.findOne(filter, fields, options);
return result;
} catch (e) {
console.error('GET ONE ERROR:', e);
throw e
}
Trying to change parameters in the connection such as smaller heartbeatMS
const options = {
useUnifiedTopology: !isLocal,
useNewUrlParser: true,
useFindAndModify: false,
useCreateIndex: true,
poolSize: 10,
serverSelectionTimeoutMS: 5000, // set the server selection timeout
heartbeatFrequencyMS: 5000, // set the heartbeat frequency
connectTimeoutMS: 10000, // set the connection timeout
};
What I Expect
No request to the app when the closing the MongoDB connection, or to avoid "Cannot use a session that has ended" error.

MongoDB/Mongoose unhandled error on disconnect - why isn't it firing on('error') event?

I'm connecting to a MongoDB database using the mongoose library.
After running mongoose.connect() I'm creating a mongoose.connection.on('error') handler.
After the app starts, I stop the MongoDB service to simulate a 'lost-connection event'. The script outputs:
events.js:196
throw er; // Unhandled 'error' event
...
and my app crashes.
The on('error') handler never executes. Why not? Isn't that the point of this event handler: to handle errors that the MongoDB connection throws, to avoid the app crashing?
Expected behavior: if the MongoDB connection is lost, the app attempts to reconnect indefinitely.
What am I doing wrong?
Here's my full connection code:
function dbConnect() {
// Establish MongoDB connection
logger.debug(`Connecting to MongoDB database ${process.env.DB_SERVER}:${process.env.DB_PORT} database ${process.env.DB_DATABASE}...`)
// Set up connection string
const db_uri = `mongodb://${process.env.DB_USER}:${process.env.DB_PASS}#${process.env.DB_SERVER}:${process.env.DB_PORT}/${process.env.DB_AUTH_DATABASE}`
// Initiate connection:
mongoose.connect(db_uri, {
dbName: process.env.DB_DATABASE, // Connect to the specified database
useNewUrlParser: true, // Use new settings
useUnifiedTopology: true,
useCreateIndex: true,
autoIndex: process.env.DB_AUTO_INDEX, // Autoindex
reconnectTries: Number.MAX_VALUE, // Keep retrying forever (thanks https://stackoverflow.com/a/39684734/1502289 and https://stackoverflow.com/a/41923785/1502289)
reconnectInterval: 5000, // Time to wait between reconnection attempts
})
.then(() => {
logger.debug("MongoDB successful connection")
})
.catch((err) => {
logger.debug("MongoDB connection error", err)
})
const db = mongoose.connection
// Set up database event handlers:
db.on('error', function(err) { logger.error("Unable to connect to database. Error: " + err) } )
db.once('open', function() {
logger.debug('Mongoose database connection established.')
// Load common properties from database:
// ... [snip]
})
db.on('disconnected', function() {
logger.error('MongoDB disconnected. Attempting to reconnect...')
})
db.on('reconnected', function() { logger.debug('Mongoose reconnected.')})
}

When mongodb server is down how to catch the error while running mongoose query

I am using mongoose for connecting node.js with mongoDB, now i wrote below query
var trans = new transmodel({method: method, trans_id: r});
trans.save(function(err) {
if (err) {
console.error("Razor_pay_webhook Error 4 err: " + err);
res.write('statusCode: 200');
res.end();
} else {
res.write('statusCode: 400');
res.end();
}
});
I thought when my mongodb cluster will be down then i will get 'err' while executing above mongoose query, but when i ran above query while my mongo cluster was down nothing happened(No err was called). Can anyone please tell me how can i catch the error if my mongodb server is down inside my query. Also for reconnecting again with my cluster i have set below parameters but my node server is not trying to reconnect again with my mongodb server i don't know what's going wrong.
var mongoose = require('mongoose');
var config = require('./config/database.js');
var DB_URL = config.db.url;
mongoose.connection.on("connected", function(ref) {
console.log("Connected to " + " DB!");
});
mongoose.connection.on("error", function(err) {
console.error('Failed to connect to DB ' + ' on startup ', err);
if (err) {
return next(err);
}
});
mongoose.connection.on('disconnected', function(err) {
console.log('Mongoose default connection to DB :' + ' disconnected');
if (err) {
return next(err);
}
});
var gracefulExit = function() {
mongoose.connection.close(function () {
console.log('Mongoose default connection with DB :' + ' is disconnected through app termination');
process.exit(0);
});
}
process.on('SIGINT', gracefulExit).on('SIGTERM', gracefulExit);
exports.con_close = function () {
console.log('Mongoose connection disconnected');
mongoose.connection.close();
}
var options = {
server: {
socketOptions: {
keepAlive: 1000,
connectTimeoutMS: 30000
}
},
replset: {
rs_name: 'replicaset',
auto_reconnect:true,
socketOptions: {
keepAlive: 1000, // doubt about it
connectTimeoutMS: 30000
}
},
user: 'root',
pass: 'G3saGT2Y',
auth: {
authdb: 'admin'
}
}
mongoose.connect(DB_URL, options, function(err) {
console.log('ho rha hai');
if (err) {
console.log('error connection to mongo server!');
console.log(err);
}
});
You are using mongoose, it emits events (the EventEmitter pattern) when the database is down and when the database is reconnecting and up again.
from mongoose code found here we can see that the library db connection - connection.js
has the following events that are emitted:
* #param {Mongoose} base a mongoose instance
* #inherits NodeJS EventEmitter
http://nodejs.org/api/events.html#events_class_events_eventemitter
* #event connecting: Emitted when connection.{open,openSet}() is executed on this connection.
#event connected: Emitted when this connection successfully connects to the db. May be emitted multiple times in reconnected scenarios.
#event open: Emitted after we connected and onOpen is executed on all of this connections models.
#event disconnecting: Emitted when connection.close() was executed.
#event disconnected: Emitted after getting disconnected from the db.
#event close: Emitted after we disconnected and onClose executed on all of this connections models.
#event reconnected: Emitted after we connected and subsequently disconnected, followed by successfully another successfull connection.
#event error: Emitted when an error occurs on this connection.
#event fullsetup: Emitted in a replica-set scenario, when primary and at
least one seconaries specified in the connection string are connected.
#event all: Emitted in a replica-set scenario, when all nodes specified in the connection string are connected.
When the database is down you will receive two events:
1. disconnected
2. error (the error that driver encountered)
When the database is up again you will receive the reconnect event.
So you don't need to try catch the error rather you should listen to these events.
More helpful information about connection failures and reconnecting can be found here.
This article explain how to use and configure the autoReconnect and the bufferMaxEntries according to your settings.

Connecting to redis using kue always creates a connection to localhost

I can't connect to Redis using kue, I've followed this article, practically I'm creating the connection by using the kue redis client, and the connection code is this:
var kue = require('kue'),
redis = require('kue/node_modules/redis');
app.redisClient = redis.createClient('6379', 'remoteip',{});
app.redisClient.on('error', function (err) {
console.log('Redis error encountered', err);
});
app.redisClient.on('end', function() {
console.log('Redis connection closed');
});
kue.redis.createClient = function() {
console.log('client ------------------',app.redisClient);
return app.redisClient;
};
and it seems like Kue is trying to connect to a local Redis (which I don't have installed), because I'm getting this exception:
Error: Redis connection to 127.0.0.1:6379 failed - connect
ECONNREFUSED
I've read this post and it seems like the issue has been resolved in version 0.8 and I'm using 0.8.11 :/, finally I also wanted to override the client using a different client instance by using redis nodejs without any luck, because I'm getting the same error.
any help will be more than appreciated.
thanks!
I used a different way to setup the connection and it works so far this is what I've done:
app.redisClient = redis.createClient(config.redisPort, config.redisUrl,{});
app.redisClient.on('connect', function () {
console.info('successful connection to redis server');
});
app.redisClient.on('error', function (err) {
console.log('Redis error encountered', err);
});
app.redisClient.on('end', function() {
console.log('Redis connection closed');
});
kue.app.listen(config.kuePort);
app.jobs = kue.createQueue({
redis: {
createClientFactory: function(){
return app.redisClient;
}
}
});

Why NodeJS KeepAlive does not seem to work as expected?

Quoted from TCP keepalive HowTo:
In order to understand what TCP keepalive (which we will just call
keepalive) does, you need do nothing more than read the name: keep TCP
alive. This means that you will be able to check your connected socket
(also known as TCP sockets), and determine whether the connection is
still up and running or if it has broken.
So why is the following code not throwing something when the internet connection is broken?
var tls = require('tls');
var socket = tls.connect(443, "google.com", function connected() {
console.log('connected');
});
socket.setNoDelay(true);
socket.setKeepAlive(true, 0);
socket.setTimeout(0, function(){
console.log('timeout');
});
socket.on('data', function(data) {
console.log(data);
});
socket.on('close', function() {
console.error("close");
});
socket.on('error', function(err) {
console.error("error", err);
});
Tested on MacOS/Debian, with NodeJS v0.10.17
Quoting man 7 tcp:
tcp_keepalive_time (integer; default: 7200; since Linux 2.2)
The number of seconds a connection needs to be idle before TCP begins sending out keep-alive probes. Keep-alives are only sent when the SO_KEEPALIVE socket option is enabled. The default value is 7200 seconds (2 hours). An idle connection is terminated after approximately an additional 11 minutes (9 probes an interval of 75 seconds apart) when keep-alive is enabled.
So after ~10 minutes (on MacOS 10.8) node emitted an error:
error { [Error: read ETIMEDOUT] code: 'ETIMEDOUT', errno: 'ETIMEDOUT', syscall: 'read' }
https://www.npmjs.com/package/net-keepalive
Here is a module which lets you configure TCP_KEEPINTVL and TCP_KEEPCNT per-socket basis.
Provides high-level access to socket options like TCP_KEEPIDLE,
TCP_KEEPINTVL, TCP_KEEPCNT
var Net = require('net')
, NetKeepAlive = require('net-keepalive')
;
// Create a TCP Server
var srv = Net.createServer(function(s){>
console.log('Connected %j', s.address())
// Doesn't matter what it does
s.pipe(s)
});
// Start on some port
srv.listen(1337, function(){
console.log('Listening on %j', srv.address())
});
// Connect to that server
var s = Net.createConnection({port:1337}, function(){
console.log('Connected to %j', s.address())
//IMPORTANT: KeepAlive must be enabled for this to work
s.setKeepAlive(true, 1000)
// Set TCP_KEEPINTVL for this specific socket
NetKeepAlive.setKeepAliveInterval(s, 1000)
// and TCP_KEEPCNT
NetKeepAlive.setKeepAliveProbes(s, 1)
});

Resources