-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
No retries are made after "failed to connect on first connect" #5169
Comments
can confirm. |
i'm guessing this is a Can you also report this issue there? Here's a full repro script: const mongoose = require('mongoose');
const co = require('co');
mongoose.Promise = global.Promise;
const GITHUB_ISSUE = `gh-5169`;
exec()
.catch(error => {
console.error(`Error: ${ error }\n${ error.stack }`);
});
function exec() {
return co(function*() {
const db = mongoose.createConnection(`mongodb://localhost:27017/${ GITHUB_ISSUE }`);
db.on('error', (error) => {
console.error(`in an error ${ error }\n${ error.stack }`);
})
});
} |
Thanks for the repro. I have looked into mongodb-core. It is the intended behaviour of the driver:
So I suspect we won't get any different behaviour from the driver. I actually think that behaviour is reasonable for a low-level driver. It will help developers who accidentally try to connect to the wrong host or the wrong port. But if we want to do something more developer-friendly in mongoose, we could consider:
If I remember correctly, when I used the workaround and finally connected after a few failed attempts, queries already queued by the app did get executed as desired. (But this is worth testing again.) |
i think this is a decent idea actually, i'll label this a feature request |
No, failing fast on initial connection is a pretty consistent behavior across MongoDB drivers and there isn't much benefit to mongoose supporting it. |
This recent post from the Strongloop Loopback mongodb connector may be relevant. Their My interest is container orchestration, where "container startup order" can often be set and expected but "order of service availability" cannot. An orchestration tool might confirm that the mongo container is "up" even though the mongo service isn't available yet. So, if my mongo container takes 1s to start but 5s for the service to become available, and my app container takes 1s to start and 1s for the service to be available, the app service will outrun the mongo service, causing a first connection failure as originally described. The Docker Compose documentation has this to say:
So there's a definite gap here in the context of container orchestration, but both of these stances appear to be valid:
|
Sure, there's a gap, but then the responsibility falls to you to decide whether to retry if initial connection fails. All mongoose tells you is that it failed. If you make the questionable decision to use docker compose in production (or in any context for that matter), it's up to you to handle retrying initial connection failures. |
Stance 2, it is, then. |
For anyone wanting auto-reconnection when first connect fails, this is how I handle it: function createConnection (dbURL, options) {
var db = mongoose.createConnection(dbURL, options);
db.on('error', function (err) {
// If first connect fails because mongod is down, try again later.
// This is only needed for first connect, not for runtime reconnects.
// See: https://github.com/Automattic/mongoose/issues/5169
if (err.message && err.message.match(/failed to connect to server .* on first connect/)) {
console.log(new Date(), String(err));
// Wait for a bit, then try to connect again
setTimeout(function () {
console.log("Retrying first connect...");
db.openUri(dbURL).catch(() => {});
// Why the empty catch?
// Well, errors thrown by db.open() will also be passed to .on('error'),
// so we can handle them there, no need to log anything in the catch here.
// But we still need this empty catch to avoid unhandled rejections.
}, 20 * 1000);
} else {
// Some other error occurred. Log it.
console.error(new Date(), String(err));
}
});
db.once('open', function () {
console.log("Connection to db established.");
});
return db;
}
// Use it like
var db = createConnection('mongodb://...', options);
var User = db.model('User', userSchema); For mongoose < 4.11 use Edit 2019/09/02: There is also a shorter solution using |
Hi @vkarpov15 when this occurs also log an Unhandled Rejection from mongodb-core library.
This is easy to reproduce, try to connect with a non available MongoDb server.
Some way to handle it? |
@jorgearanda what version of mongoose are you using and can you provide some code samples? |
@vkarpov15 I think that's a message for @jorgecuesta ? |
Woops, my mistake. Silly github autocomplete, yep, that was for @jorgecuesta |
I am seeing the exact same thing as @jorgecuesta, I was using 4.11.5 and but see the same with 5.0.0-rc2. I am using createConnection as some of the models are using different databases on the same mongo instance. It happens when starting the server while mongo is down:
The models call a connection function with the name of the database they are using (below). As long as mongo is running when the server starts, bringing the database up and down re-connects just fine (logging the errors). I just realized reading this issue that the driver handles the initial connection differently, which is kind of annoying. I am going to try @joeytwiddle's workaround, but I suspect this unhandled exception will still occur?
|
@raythree in the above case you should be fine because you have let conn;
for (let i = 0; i < numRetries; ++i) {
try {
conn = await mongoose.createConnection(uri);
break;
} catch (error) {}
} |
@vkarpov15 Sorry for delay to answer you, we are using 4.11.14 and 4.13.4 |
@jorgecuesta do |
@vkarpov15 connect() return connection instance right? const connection = mongoose.connect(uri || undefined, {useMongoClient: true});
connection.once('error', (e) => {
console.error(e, 'mongoose connection error.');
});
connection.once('connected', () => {
console.log('mongoose connected');
}); |
In 5.0.0 we changed it so |
@pranshuchittora can you show your MongoDB config file? Make sure the port is 27017. Also, try using 127.0.0.1 instead of localhost |
Try to connect with ip instead of localhost that will fix your problem.
|
If first attempt to connect fails it always throws error. mongoose@5.3.16 async function waitForMongoDBStart(uri: string, timeoutMs: number) {
return new Promise( async (resolve, reject) => {
let endTime = Date.now() + timeoutMs;
while (true) {
let connectionError: Error;
function errorHandler(err: Error) {
connectionError = err;
}
mongoose.connection.once("error", errorHandler);
await mongoose.connect(uri, {
connectTimeoutMS: 5000, // This timeout applies only after connected & connection lost
useNewUrlParser: true,
useFindAndModify: false
});
// Time for error event propagation
await wait(0);
if ( ! connectionError) {
mongoose.connection.removeListener("error", errorHandler);
return resolve(); // All OK, connected
}
if (connectionError.name !== "MongoNetworkError") {
return reject(`Unable to connect mongoDB. Details: ${connectionError}`);
}
if (Date.now() > endTime) {
return reject(`Unable to connect mongoBD in ${timeoutMs} ms. Details: ${connectionError}`);
}
}
});
} |
@vkarpov15 This is very non-obvious behaviour. From https://mongoosejs.com/docs/connections.html:
Documentation says that it should work with initial connection, but it does not. With |
If it's consistent behaviour across MongoDB drivers, it would be nice to have some option like |
Current behavior is opposite to this one described by vkarpov15 -
connectImeoutMS matters only after first connection (on first connection it
is expected that DB is started and accepting connection, otherwise immedia
error is thrown)
sob., 22 gru 2018 o 13:03 Dmitry Kirilyuk <notifications@github.com>
napisał(a):
… If it's consistent behaviour across MongoDB drivers, it would be nice to
have some option like reconnectOnInitialFail which will be false by
default
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#5169 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABY-TjgeI2UqVca050y5YY3zi6w7nMkfks5u7h-vgaJpZM4M-1ur>
.
--
*Wojciech Fiderek*
Mobile: +48 516 661 428
|
If I recall correctly, connectTimeoutMS matters on initial connection depending on network conditions and os. When does connectTimeoutMS matter after initial connection? |
In my case immediate error was thrown when I was not able to connect on first try to localhost mongod instance on Windows 10 and Alpine Linux (version unknown). Mongod instance just not yet started. |
@fider @Jokero I dug in further, and I'm right,
const net = require('net');
const server = net.createServer();
server.listen(27055); You'll see that const assert = require('assert');
const mongoose = require('mongoose');
mongoose.set('debug', true);
const { Schema } = mongoose;
run().then(() => console.log('done')).catch(error => console.error(error.stack));
async function run() {
await mongoose.connect('mongodb://localhost:27055', {
useNewUrlParser: true,
connectTimeoutMS: 1000,
socketTimeoutMS: 25000
});
}
|
sometimes due to network restrictions, MongoDB url's gets blocked, try changing your network/internet source. |
When making an initial connection, (at least) three things can happen:
So the timeout is not always used. It's only used if there is no clear success or failure in the given time. This is common in networking, and also when talking to an uncooperative person. Your request can get two types of rejection: "NO!" and ..... |
Thanks for the solid explanation @joeytwiddle 👍 |
How should one handle this behavior with replica set connections using |
@vkarpov15 yes I am! Thanks for the tip. I will run some tests after disabling unified topology and report back my findings. |
Hi. I have the exact same problem in my project. have you found any soloution for it?! |
@fdmxfarhan please open a separate issue and follow the issue template. |
By default, mongoose throws an Error if the first connect fails, which crashes node.
So to reproduce this bug, you will need the following code in your app, to catch the error and prevent the crash:
Now we can reproduce this bug as follows:
[MongoError: failed to connect to server [localhost:27017] on first connect [MongoError: connect ECONNREFUSED 127.0.0.1:27017]]
Expected behaviour: Since
autoreconnect
defaults to true, I would expect mongoose to establish a connection soon after the MongoDB is accessible again.Note: If the first connect succeeds, but the connection to MongoDB is lost during runtime, then autoreconnect works fine, as expected. The problem is the inconsistency if MongoDB is not available when the app starts up.
(If this is the desired behaviour, and developers are recommended to handle this situation by not catching the error, and letting node crash, then I can accept that, but it is worth making it clear.)
node v4.4.1, mongoose@4.9.4, mongodb@2.2.19, mongodb-core@2.1.4
The text was updated successfully, but these errors were encountered: