32

I'm trying to set up the MongoDB auto reconnection feature via Mongoose. Every way that I have tried to pass the option has had no effect, or at least the reconnected event isn't being emitted.

What I've tried:

mongoose.createConnection("mongodb://localhost:27017/test", { auto_reconnect: true });
mongoose.createConnection("mongodb://localhost:27017/test", { autoReconnect: true });
mongoose.createConnection("mongodb://localhost:27017/test", { server: { auto_reconnect: true } });
mongoose.createConnection("mongodb://localhost:27017/test", { server: { autoReconnect: true } });

If one of these is correct, the reconnected event should be triggered and a message should be logged in the console, however this never happens.

If there is a delay before the reconnection, does anyone know how to configure it?

Thanks in advance

For anyone looking into this, take a look at this and this issue in mongoose repository.

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
gustavohenke
  • 40,997
  • 14
  • 121
  • 129
  • Autoreconnect is enabled by default, so you don't have to enable it yourself. Also, to what do you attach the listener for the `reconnected` event, and are you simulating an actual reconnect situation? – robertklep Apr 26 '13 at 06:32
  • I'm attaching that event to the return of this `createConnection()` call. My events are working normally with the `connected`/`disconnected` events. – gustavohenke Apr 26 '13 at 12:34
  • So how are you testing the reconnects? – robertklep Apr 26 '13 at 14:14
  • stopping/starting the mongodb service after application initialization. – gustavohenke Apr 26 '13 at 19:16
  • 3
    mongoose will only reconnect when you actually use the connection, so try running a query after restarting mongodb. I'm getting reconnected-events that way (although I use `mongoose.connect()` and not `createConnection()`, perhaps that makes a difference) – robertklep Apr 26 '13 at 19:41
  • Well, after some time I've tried what you told without any success :) At least no success using `createConnection()`... – gustavohenke Jun 28 '13 at 13:17
  • It may be helpful to consult [the issue](https://github.com/Automattic/mongoose/issues/5169) I opened about this on the project's Github page. – joeytwiddle Apr 17 '17 at 03:38

11 Answers11

63

I had the same question as you, and robertklep's solution didn't work for me either. I found when MongoDB service is stopped, an error event is triggered, but the connection.readyState is still 1 (connected). That may be why it didn't auto reconnect.

This is what I have now:

  var db = mongoose.connection;

  db.on('connecting', function() {
    console.log('connecting to MongoDB...');
  });

  db.on('error', function(error) {
    console.error('Error in MongoDb connection: ' + error);
    mongoose.disconnect();
  });
  db.on('connected', function() {
    console.log('MongoDB connected!');
  });
  db.once('open', function() {
    console.log('MongoDB connection opened!');
  });
  db.on('reconnected', function () {
    console.log('MongoDB reconnected!');
  });
  db.on('disconnected', function() {
    console.log('MongoDB disconnected!');
    mongoose.connect(dbURI, {server:{auto_reconnect:true}});
  });
  mongoose.connect(dbURI, {server:{auto_reconnect:true}});
Zebra Propulsion Lab
  • 1,736
  • 1
  • 17
  • 28
  • 1
    This is so wrong to reconnect on disconnect. Instead you should monitor error and reconnect from there – pronebird Nov 19 '14 at 11:19
  • 4
    Mongoose doesn't disconnect on error, so I have to explicitly disconnect it in order to reconnect – Zebra Propulsion Lab Nov 20 '14 at 11:41
  • For those interested in reading the **official mongoose `Connection` docs**. http://mongoosejs.com/docs/3.0.x/docs/api.html#connection_Connection – zurfyx Jan 25 '17 at 17:58
  • 1
    Does the `{server:{auto_reconnect:true}}` bit cause mongoose to automatically reconnect whenever you disconnect? For example, if the `error` event is fired and you disconnect, does mongoose reconnect for you? If not, why would you not want to attempt to reconnect, because I think it is probably some trivial communication error? Or does your 'disconnected' event get fired in this case, and that handles reconnection? If so, what is `auto_reconnect` even doing anymore? Haven't you sort of reimplemented it? – Will Brickner May 28 '17 at 21:48
  • This approach threw me various errors and did not work for me – Suhas Jul 23 '18 at 05:44
  • The solution works like a charm for me.`{auto_reconnect:true}` does not work. Now I have multiple connection in my application that is a struggle. – Yuki Feb 10 '21 at 17:30
  • 2
    This no longer works on Mongoose 6 – Kawd Mar 20 '22 at 22:38
  • Outdated/Deprecated - mongodb v3&4 now auto reconnects by default. – Dmitri R117 Jan 23 '23 at 08:46
21

Plucked from http://bites.goodeggs.com/posts/reconnecting-to-mongodb-when-mongoose-connect-fails-at-startup/

This worked for me:

var mongoose = require('mongoose')
var mongoUrl = "mongodb://localhost:27017/test"

var connectWithRetry = function() {
  return mongoose.connect(mongoUrl, function(err) {
    if (err) {
      console.error('Failed to connect to mongo on startup - retrying in 5 sec', err);
      setTimeout(connectWithRetry, 5000);
    }
  });
};
connectWithRetry();
dcodesmith
  • 9,590
  • 4
  • 36
  • 40
Ricky Sahu
  • 23,455
  • 4
  • 42
  • 32
  • Best alternative! – SSG Aug 07 '19 at 18:31
  • Running a replica set with a single node in dev, the app crashes without ever calling the callback function. Both when I kill the mongod service while the app is running, as well as when starting the app without the service running. I'm not sure this arises due to the replica set setup, but it might – NoBullsh1t Apr 19 '22 at 10:22
14

Recently, I investigate the auto-reconnect with MongoDB var Mongoose. There is one issue here, when invoking mongoose.connect within disconnected event handler, it will trigger infinite loop. Why the SIGINT signal is blocked when mongoose auto reconnect.

One work around solution could be that the mongoose.connect() only be called when there is no connection with MongoDB before. The auto_reconnect flag could make mongoose reconnect with MongoDB automatically. Here are code snippets.

var mongoose = require('mongoose');

var isConnectedBefore = false;
var connect = function() {
    mongoose.connect('mongodb://localhost/' + + 'test_dev', {server: { auto_reconnect: true }});
};
connect();

mongoose.connection.on('error', function() {
    console.log('Could not connect to MongoDB');
});

mongoose.connection.on('disconnected', function(){
    console.log('Lost MongoDB connection...');
    if (!isConnectedBefore)
        connect();
});
mongoose.connection.on('connected', function() {
    isConnectedBefore = true;
    console.log('Connection established to MongoDB');
});

mongoose.connection.on('reconnected', function() {
    console.log('Reconnected to MongoDB');
});

// Close the Mongoose connection, when receiving SIGINT
process.on('SIGINT', function() {
    mongoose.connection.close(function () {
        console.log('Force to close the MongoDB conection');
        process.exit(0);
    });
});
zangw
  • 43,869
  • 19
  • 177
  • 214
12

Just for the sake of posterity, as most of these answers are old, you should not need to deal with this issue any more, as it is now baked into the nodejs mongodb driver. To quote kdmon:

...reconnecting is now baked into mongoose and enabled by default. But it might be useful to know that Mongoose by default will only try reconnecting for 30s and then give up. Set the server.reconnectTries option to increase the number of times mongoose will try to reconnect. For example, you can tell mongoose to never stop trying to reconnect like this:

mongoose.connect(uri, { server: { reconnectTries: Number.MAX_VALUE } });

See connection docs and server options defaults for details

joniba
  • 3,339
  • 4
  • 35
  • 49
  • The problem using this flag is it turns mongoose calls into blocking calls, ie they will not return until database is up again. In my case I was calling mongoose from an express application and we prefered to return an error instead of blocking the request. – Ismael Nov 07 '16 at 14:54
  • I wish this were true. It is true if the connection is lost in the middle of runtime. But if the *first* connection attempt fails (because the mongo DB is down), the driver never retries. – joeytwiddle Apr 17 '17 at 03:04
  • 1
    This no longer works on Mongoose 6 – Kawd Mar 20 '22 at 22:37
8

@Clive's answer was excellent. Nonetheless, due to using mongoose with Promise I was getting the following warning after every failed attempt:

(node:18123) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): MongoError: failed to connect to server [localhost:27017] on first connect

ES6 version (with Promise)

I also added a small timeout between reconnects in this version (completely optional), to prevent your screen (or your logger) being flooded of repeated messages.

import mongoose from 'mongoose';

mongoose.Promise = Promise; // Set mongoose to use ES6 Promises.

const dbURI = 'mongodb://127.0.0.1:27017/myDb';
const reconnectTimeout = 5000; // ms.

function connect() {
  mongoose.connect(dbURI, { auto_reconnect: true })
    .catch(() => {}); // Catch the warning, no further treatment is required
                      // because the Connection events are already doing this
                      // for us.
}

const db = mongoose.connection;

db.on('connecting', () => {
  console.info('Connecting to MongoDB...');
});

db.on('error', (error) => {
  console.error(`MongoDB connection error: ${error}`);
  mongoose.disconnect();
});

db.on('connected', () => {
  console.info('Connected to MongoDB!');
});

db.once('open', () => {
  console.info('MongoDB connection opened!');
});

db.on('reconnected', () => {
  console.info('MongoDB reconnected!');
});

db.on('disconnected', () => {
  console.error(`MongoDB disconnected! Reconnecting in ${reconnectTimeout / 1000}s...`);
  setTimeout(() => connect(), reconnectTimeout);
});

connect();

Further information on Connection events.

Community
  • 1
  • 1
zurfyx
  • 31,043
  • 20
  • 111
  • 145
4

Here is an improvement to Clive's answer that sets a minimum of 5 seconds between connection attempts.

var db = mongoose.connection;
var lastReconnectAttempt; //saves the timestamp of the last reconnect attempt
db.on('error', function(error) {
    console.error('Error in MongoDb connection: ' + error);
    mongoose.disconnect();
});
db.on('disconnected', function() {
    console.log('MongoDB disconnected!');
    var now = new Date().getTime();
    // check if the last reconnection attempt was too early
    if (lastReconnectAttempt && now-lastReconnectAttempt<5000) {
        // if it does, delay the next attempt
        var delay = 5000-(now-lastReconnectAttempt);
        console.log('reconnecting to MongoDB in ' + delay + "mills");
        setTimeout(function() {
            console.log('reconnecting to MongoDB');
            lastReconnectAttempt=new Date().getTime();
            mongoose.connect(dbURI, {server:{auto_reconnect:true}});
        },delay);
    }
    else {
        console.log('reconnecting to MongoDB');
        lastReconnectAttempt=now;
        mongoose.connect(dbURI, {server:{auto_reconnect:true}});
    }

});
Gil SH
  • 3,789
  • 1
  • 27
  • 25
  • 2
    Why do you use `auto_reconnect: true`, shouldn't it be `false`, if you reconnecting manually why do you need to set `auto_reconnect` to `true`? – Aviel Fedida Apr 03 '16 at 11:27
  • 1
    `auto_reconnect: true` only works as expected if the first connect succeeds. For some reason if the first connect failed, the driver never tried again. Hence all these workarounds you find here. That said, I think you can leave the option out, since it defaults to true anyway. – joeytwiddle Apr 17 '17 at 03:00
  • Something like this is what I ended up using, except I moved the retry into a standalone function so it could be called initially as well if the initial connect fails. – PeterM Sep 26 '18 at 03:21
3

Make sure mongoose is also the only way you're connecting to Mongo. In my case, I am using connect-mongo to store sessions in Express, but it does not have auto_reconnect set to true by default, as of v0.4.0.

mattwad
  • 1,743
  • 17
  • 13
2

Based on @zangw answer, I've finished with this database init function for my app

const mongoose = require('mongoose')
const RETRY_TIMEOUT = 3000

module.exports = function initDB () {
  mongoose.Promise = global.Promise
  const options = {
    autoReconnect: true,
    useMongoClient: true,
    keepAlive: 30000,
    reconnectInterval: RETRY_TIMEOUT,
    reconnectTries: 10000
  }

  let isConnectedBefore = false

  const connect = function () {
    return mongoose.connect(process.env.MONGODB_URL, options)
      .catch(err => console.error('Mongoose connect(...) failed with err: ', err))
  }

  connect()

  mongoose.connection.on('error', function () {
    console.error('Could not connect to MongoDB')
  })

  mongoose.connection.on('disconnected', function () {
    console.error('Lost MongoDB connection...')
    if (!isConnectedBefore) {
      setTimeout(() => connect(), RETRY_TIMEOUT)
    }
  })
  mongoose.connection.on('connected', function () {
    isConnectedBefore = true
    console.info('Connection established to MongoDB')
  })

  mongoose.connection.on('reconnected', function () {
    console.info('Reconnected to MongoDB')
  })

  // Close the Mongoose connection, when receiving SIGINT
  process.on('SIGINT', function () {
    mongoose.connection.close(function () {
      console.warn('Force to close the MongoDB connection after SIGINT')
      process.exit(0)
    })
  })
}

There is a few differences: I've added some options to prevent connection closing issue - no reconnect after 30 auto retries, just MongoError: Topology was destroyed for any operation and no reconnect; also I've added .catch after connect to prevent unhandled promise rejection):

Maksim Nesterenko
  • 5,661
  • 11
  • 57
  • 91
2

To have multiple retries without request blocking while retrying I had to set bufferMaxEntries: 0:

const dbUri = 'mongodb://localhost/some_db';
const dbOptions = {
    useMongoClient: true,
    autoReconnect: true,
    reconnectTries: Number.MAX_VALUE,
    bufferMaxEntries: 0
};

mongoose.connect(dbUri, dbOptions).catch(err => process.exit(1));
jonasnas
  • 3,540
  • 1
  • 23
  • 32
1

After reading the docs, I'm pretty sure you have the options wrong. The connection options string should look like:

mongoose.connect("mongodb://localhost:27017/db", {
    socketOptions: {
      // This option is on by default, but why not set it explicitly
      autoReconnect: true
    },
    // This options is 1 second by default, its possible the ha
    // takes longer than 30 seconds to recover.
    reconnectInterval: 5000,
    // This options is 30 by default, why not make it 60
    reconnectTries: 60
  })

Check this page out: http://mongoosejs.com/docs/api.html

Dr.Knowitall
  • 10,080
  • 23
  • 82
  • 133
  • 1
    The options have changed in the course of time. The question has been created 4 years ago. – kentor Nov 30 '17 at 09:26
0

2023 Update:

Unified Topology is now the standard for the latest mongodb drivers. Connections are treated differently. You can listen for disconnect events using mongoose, but it's not necessary for reconnecting.

To spy on the reconnect attempts you can use the mongoose.connection.client (MongoDB v3 or v4 Client) object:

client.on('serverHeartbeatFailed', (e) => {})

This will show you the reconnect attempts ongoing. Defaults to every 10s.

You can still use the mongoose disconnected or error events to add your own custom reporting.

Dmitri R117
  • 2,502
  • 23
  • 20