Node and Mongoose - Not reconnecting if mongod was not running when first tried to connect - mongodb

Im using docker-composer and Im finding issues with execution order of services. The main issue happens when my express app tries to connect to mongod but this is not yet ready.
The issue can be reproduced easily by running first the nodejs application but not mongod (manually forcing this case).
My app uses mongoose and try to establish connection to mongod. Because mongod is not up and running, the app throws an error about it.
$ nodemon server/app.js
24 Apr 21:42:05 - [nodemon] v1.7.0
24 Apr 21:42:05 - [nodemon] to restart at any time, enter `rs`
24 Apr 21:42:05 - [nodemon] watching: *.*
24 Apr 21:42:05 - [nodemon] starting `node server/app.js`
Listening on port 8000
disconnected
connection error: { [MongoError: connect ECONNREFUSED] name: 'MongoError', message: 'connect ECONNREFUSED' }
Starting mongod later seems to reconnect.
24 Apr 21:51:28 - [nodemon] v1.7.0
24 Apr 21:51:28 - [nodemon] to restart at any time, enter `rs`
24 Apr 21:51:28 - [nodemon] watching: *.*
24 Apr 21:51:28 - [nodemon] starting `node server/app.js`
Listening on port 8000
disconnected
connection error: { [MongoError: connect ECONNREFUSED] name: 'MongoError', message: 'connect ECONNREFUSED' }
connected
reconnected
Despite of that, operations that require access to mongo will not come through... neither error is shown
This is the code to connect to mongo using mongoose:
// Starting mongo
mongoose.connect(config.database, {
server:{
auto_reconnect:true,
reconnectTries: 10,
reconnectInterval: 5000,
}
});
// Listening for connection
var mongo = {};
var db = mongoose.connection;
db.on('connected', console.error.bind(console, 'connected'));
db.on('error', console.error.bind(console, 'connection error:'));
db.on('close', console.error.bind(console, 'connection close.'));
db.once('open', function() {
console.log("We are alive");
});
db.on('reconnected', function(){
console.error('reconnected');
});
db.on('disconnected', console.error.bind(console, 'disconnected'));
And here is the route that will try to get data from mongo but fail.
router.post('/auth', function(req, res){
User.findOne({name: req.body.name})
.then(function(user){
if(!user)
{
res.status(401).send({ success: false, message: 'Authentication failed. User not found.' });
}
...
How can I recover from running nodejs before mongo is ready?.

In my case, I created separate function only for mongoose connect method:
const connect = () => {
mongoose.connect('mongodb://localhost:27017/myapp', {
useNewUrlParser: true,
reconnectTries: Number.MAX_VALUE,
reconnectInterval: 500,
poolSize: 10,
});
};
I'm calling it at the same start. I also added Event Handler for error event:
mongoose.connection.on('error', (e) => {
console.log('[MongoDB] Something went super wrong!', e);
setTimeout(() => {
connect();
}, 10000);
});
If mongoose fails to connect because MongoDB is not running, error event handler is fired and setTimeout schedules "custom" reconnect.
Hope it helps.

How long does it take before mongod is ready? Because it seems like this is an edge case issue, where mongod might take a couple of seconds to get ready; and when mongoose is connected it serves requests as expected. Just trying to understand why the slight delay (probably a only a few seconds) is necessary to resolve?
But here is a solution anyway:
You could set up an express middleware to check if mongoose is ready and throw an error if not:
app.use(function(req,res,next){
if (mongoose.Connection.STATES.connected === mongoose.connection.readyState){
next();
} else {
res.status(503).send({success:false, message: 'DB not ready' });
}
});
This should go before you inject your router.

I had the same issue with Mongoose 5+. I was able to get this working by creating a retry function using set timeout.
const mongoose = require('mongoose');
const {
MONGO_USERNAME,
MONGO_PASSWORD,
MONGO_HOSTNAME,
MONGO_PORT,
MONGO_DB,
MONGO_DEBUG,
MONGO_RECONNECT_TRIES,
MONGO_RECONNECT_INTERVAL,
MONGO_TIMEOUT_MS,
} = process.env;
if (MONGO_DEBUG) {
console.log(`********* MongoDB DEBUG MODE *********`);
mongoose.set('debug', true);
}
const DB_OPTIONS = {
useNewUrlParser: true,
reconnectTries: MONGO_RECONNECT_TRIES,
reconnectInterval: MONGO_RECONNECT_INTERVAL,
connectTimeoutMS: MONGO_TIMEOUT_MS,
};
const DB_URL = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}#${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;
// Initialize conenction retry counter
let reconnectTriesAlready = 1;
// Connect to database with timeout and retry
const connectWithRetry = () => {
mongoose.connect(DB_URL, DB_OPTIONS).then(() => {
// Connected successfully
console.log('********* MongoDB connected successfully *********');
// Reset retry counter
reconnectTriesAlready = 1;
}).catch(err => {
// Connection failed
console.error(`********* ERROR: MongoDB connection failed ${err} *********`)
// Compare retries made already to maximum retry count
if (reconnectTriesAlready <= DB_OPTIONS.reconnectTries) {
// Increment retry counter
reconnectTriesAlready = reconnectTriesAlready + 1;
// Reconnect retries made already has not exceeded maximum retry count
console.log(`********* MongoDB connection retry after ${MONGO_RECONNECT_INTERVAL / 1000} seconds *********`)
// Connection retry
setTimeout(connectWithRetry, MONGO_RECONNECT_INTERVAL)
} else {
// Reconnect retries made already has exceeded maximum retry count
console.error(`********* ERROR: MongoDB maximum connection retry attempts have been made already ${DB_OPTIONS.reconnectTries} stopping *********`)
}
})
}
connectWithRetry();

Related

MongoServerError: bad auth : Authentication failed. despite all solutions

Hi all so I went through the MERN Stack tutorial: https://www.mongodb.com/languages/mern-stack-tutorial.
Am not able to connect successfully to MongoDB.
Server is running on port: 8080
MongoServerError: bad auth : Authentication failed.
at Connection.onMessage (/Users/username/Documents/Projects/projectname/server/node_modules/mongodb/lib/cmap/connection.js:227:30)
at MessageStream.<anonymous> (/Users/username/Documents/Projects/projectname/server/node_modules/mongodb/lib/cmap/connection.js:60:60)
at MessageStream.emit (node:events:513:28)
at processIncomingData (/Users/username/Documents/Projects/projectname/server/node_modules/mongodb/lib/cmap/message_stream.js:125:16)
at MessageStream._write (/Users/username/Documents/Projects/projectname/server/node_modules/mongodb/lib/cmap/message_stream.js:33:9)
at writeOrBuffer (node:internal/streams/writable:392:12)
at _write (node:internal/streams/writable:333:10)
at Writable.write (node:internal/streams/writable:337:10)
at TLSSocket.ondata (node:internal/streams/readable:766:22)
at TLSSocket.emit (node:events:513:28) {
ok: 0,
code: 8000,
codeName: 'AtlasError',
connectionGeneration: 0,
[Symbol(errorLabels)]: Set(2) { 'HandshakeError', 'ResetPool' }
The 'config.env' file contains this:
ATLAS_URI=mongodb+srv://correctusername:correctpassword#atlascluster.zgubjgl.mongodb.net/employees?retryWrites=true&w=majority
PORT=8080
The 'conn.js' contains:
const { MongoClient } = require("mongodb");
const Db = process.env.ATLAS_URI;
const client = new MongoClient(Db, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
var _db;
module.exports = {
connectToServer: function (callback) {
client.connect(function (err, db) {
// Verify we got a good "db" object
if (db)
{
_db = db.db("employees");
console.log("Successfully connected to MongoDB.");
}
return callback(err);
});
},
getDb: function () {
return _db;
},
};
Solutions attempted:
Ensured that the username and password do not have '<>' enclosing them.
Created a new user in 'Database Access' in MongoDB itself with 'readWriteAnyDatabase#admin' access to mirror the ending of the ATLAS_URI connection string, same error.
Removed 'employees' in connection string and tried again, same error.
Tried another database name in both 'config.env' and 'conn.js' files, same error.
Ensured that my 'Network Access' in MongoDB itself is '0.0.0.0/0 (includes your current IP address)'.
Ensured that cluster name matches.
All of the solutions above are things I tried based off what I have researched on Stack. Is the problem that I don't have an employees database set up on MongoDB itself? Or is it a security setting on my Mac that is causing an issue?
Please help - feeling very downtrodden that I have reached this obstacle as I have a really good platform idea to start a business based on.

Problem connecting to Mongodb Atlas - Error: "querySrv ECONNREFUSED"

I have created a free cluster for mongodb atlas. Under that cluster I have created a database and a collection to test things out. As I was using node js driver 4.0 or above, this is the connection string I got:
mongodb+srv://myName:myPass#cluster0.2sjva.mongodb.net/myDb?retryWrites=true&w=majority
This is my backend code:
const { MongoClient } = require("mongodb");
// Connection URI
const uri =
"mongodb+srv://myName:myPass#cluster0.2sjva.mongodb.net/myDb?retryWrites=true&w=majority";
// Create a new MongoClient
const client = new MongoClient(uri);
async function run() {
try {
// Connect the client to the server
await client.connect();
// Establish and verify connection
await client.db("myDb").command({ ping: 1 });
console.log("Connected successfully to server");
} finally {
// Ensures that the client will close when you finish/error
await client.close();
}
}
run().catch(console.dir);
But when I run this code, I get this following error:
Error: querySrv ECONNREFUSED _mongodb._tcp.cluster0.2sjva.mongodb.net
at QueryReqWrap.onresolve [as oncomplete] (dns.js:210:19) {
errno: undefined,
code: 'ECONNREFUSED',
syscall: 'querySrv',
hostname: '_mongodb._tcp.cluster0.2sjva.mongodb.net'
}
Here is some information I can provide:
I have whitelisted my current Ip address, After this error I used 0.0.0.0. But the problem persists.
Os: Arch linux
I have also tried to use this connection string for older driver version: mongodb://... and that worked.
My question: why connection string for node js driver version > 4.0 doesn't work in my case?
Thank you for reading!

Sails application fails to connect localhost mongo db

My sails application not able to connect to locally running mongo db instance
local.js is like below
module.exports.connections = {
sailsMongoDBServer: {
adapter: 'sails-mongo',
host: '127.0.0.1',
port: 27017,
database: 'dp-manager-db'
}
}
Error :
2019-03-06T12:32:01.081Z - error:
{ message:
'The hook `orm` is taking too long to load.\nMake sure it is triggering its `initialize()` callback, or else set `sails.config.orm._hookTimeout to a higher value (currently 20000)',
stack:
'Error: The hook `orm` is taking too long to load.\nMake sure it is triggering its `initialize()` callback, or else set `sails.config.orm._hookTimeout to a higher value (currently 20000)\n at Timeout.tooLong [as _onTimeout] (E:\\workspace_DpForm\\Eclipse\\dpm-app\\node_modules\\sails\\lib\\app\\private\\loadHooks.js:85:21)\n at ontimeout (timers.js:436:11)\n at tryOnTimeout (timers.js:300:5)\n at listOnTimeout (timers.js:263:5)\n at Timer.processTimers (timers.js:223:10)',
code: 'E_HOOK_TIMEOUT' }
2019-03-06T12:32:03.521Z - error: A hook (`orm`) failed to load!
E:\workspace_DpForm\Eclipse\dpm-app\node_modules\mongodb\lib\mongo_client.js:224
throw err

Reconnection to the failed mongo server

I'm connecting to the mongo with reconnect options on the startup and using created db over the whole app.
var options = {
"server": {
"auto_reconnect": true,
"poolSize": 10,
"socketOptions": {
"keepAlive": 1
}
},
"db": {
"numberOfRetries": 60,
"retryMiliSeconds": 5000
}
};
MongoClient.connect(dbName, options).then(useDb).catch(errorHandler)
When I restart mongo server, driver reconnect successful. If I stop server and start it after a 30 second I get MongoError "topology was destroyed" on every operation. This 30 second seems to me is a default value for numberOfRetries = 5 and my given option doesn't have effect. Am I doing something wrong? How can I manage reconnection for a long time?
According to this answer, in order to fix this error, you should increase connection timeout in the options:
var options = {
"server": {
"auto_reconnect": true,
"poolSize": 10,
"socketOptions": {
"keepAlive": 1,
"connectTimeoutMS": 30000 // increased connection timeout
}
},
"db": {
"numberOfRetries": 60,
"retryMiliSeconds": 5000
}
};

Why is the database empty, even after a successful import?

I attempted to import a Json file today on my macbook, but I got an error:
Davids-MacBook-Pro:server Droberts$ node import.js
/filepath/A_to_B.json
development
mongodb://localhost/somefoldername
{ db: { safe: true } }
A
B
/Users/Droberts/Desktop/folderA/folderB/node_modules/mongoose/node_modules/mongodb/lib/server.js:236
process.nextTick(function() { throw err; })
^
Error: connect ECONNREFUSED
at exports._errnoException (util.js:746:11)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1010:19)
However, after running mongod in a new tab, I was able to rerun the command successfully:
Davids-MacBook-Pro:server Droberts$ node import.js
/filepath/A_to_B.json
development
mongodb://localhost/somefoldername
{ db: { safe: true } }
A
B
adding data to mongodb
finished import
However, the most startling thing is, my database remains empty!:
Davids-MacBook-Pro:server Droberts$ mongo
MongoDB shell version: 3.2.1
connecting to: test
Server has startup warnings:
2016-01-15T17:50:09.158-0500 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2016-01-15T17:50:09.158-0500 I CONTROL [initandlisten]
2016-01-15T17:50:09.158-0500 I CONTROL [initandlisten]
2016-01-15T17:50:09.158-0500 I CONTROL [initandlisten] ** WARNING: soft rlimits too low. Number of files is 256, should be at least 1000
> show dbs
somefoldername 0.000GB
local 0.000GB
What on earth is going on?!!! Here is the source for node.js:
'use strict';
// Set default node environment to development
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
console.log(process.env.NODE_ENV);
var mongoose = require('mongoose');
var config = require('./config/environment');
var CityPair = require('./api/citypair/citypair.model');
console.log(config.mongo.uri)
console.log(config.mongo.options)
// Connect to database
mongoose.connect(config.mongo.uri, config.mongo.options);
var fs = require('fs');
var file = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
console.log(file.startCity.name)
console.log(file.endCity.name)
//CityPair.find({}).remove(
CityPair.find({
$or: [{startCity : file.startCity, endCity : file.endCity},
{startCity : file.endCity, endCity : file.startCity}]
}).remove(
function() {
console.log('adding data to mongodb');
CityPair.create(file, function(err){
console.log('finished import');
process.exit();
});
}
);