Ran Kiselstein
Ran Kiselstein

Reputation: 21

Mongoose Connection is not Restored when DB becomes accessible again

The problem is Mongoose loses its connection in the following scenario:

  1. Node App is started, all seems fine (DB accessible, etc.)
  2. Mongo server process (version 2.6.10) running locally is stopped, then started after 15 seconds, all seems fine (DB accessible, etc.)
  3. Mongo process is stopped.
  4. a request to Express is made, trying to go to DB (using Mongoose) - inaccessible as expected.
  5. Mongo process is started.
  6. The same request is made, DB is inaccessible even though Mongo is up.

If I restart the Node JS app, all works fine.

Why is a failing query (using Mongoose version 4.4.5) to Mongo prevents the connection from being restored when the DB process is up again?

Should we implement a retry mechanism to try and restore the connection until DB becomes accessible?

I've tried the configuration mentioned here but it didn't work.

Any help would be appreciated.

Bellow is a sample code of our connection helper:

'use strict';

const _ = require('lodash');
const mongoose = require('mongoose');
const config = require('../config');
const logger = require('../logger');

const _instance = Symbol('instance');
const _enforcer = Symbol('enforcer');
const _members = Symbol('members');

/**
 * Singleton implementation of ConnectionHelper module
 * This module is responsible to reusing common db connections in the application

 * @type {ConnectionsHelper}
 */
module.exports = class ConnectionsHelper {
  constructor(enforcer) {
    if (enforcer !== _enforcer) {
      throw new Error('invalid singleton instantiation');
    }
    initConnectionFromConfig.call(this);
  }

  /**
   * The single instance
   * @returns {*}
     */
  static get instance() {
    if (!this[_instance]) {
      this[_instance] = new ConnectionsHelper(_enforcer);
    }
    return this[_instance];
  }

  /**
   * method retrieves connection by its name
   * @param connectionKey
   * @returns {*}
   */
  getConnection(connectionKey) {
    return this[_members][connectionKey];
  }

  /**
   * method disconnects all underlying connections
   * @returns {void|MongooseThenable}
   */
  closeConnections() {
    return mongoose.disconnect();
  }
};

function initConnectionFromConfig() {
  this[_members] = {};
  const dbsConnections = config.get('dbsConnections');

  _.forEach(dbsConnections, (connection, connectionName) => {

    const protocol = connection.protocol;
    const repSetPath = connection.mongoPath.join(',');
    const options = connection.options;

    options.server = {auto_reconnect: true, socketOptions: {keepAlive: 1, connectTimeoutMS: 30000 }};
    options.replset = {socketOptions: {keepAlive: 1, connectTimeoutMS: 30000 }};

    this[_members][connectionName] = mongoose.createConnection(protocol + repSetPath, options);

    addConnectionEvents.call(this, connectionName);
  });
}

function initConnectionIfNeeded() {
  this[_members] = {};
  const dbsConnections = config.get('dbsConnections');

  _.forEach(dbsConnections, (connection, connectionName) => {
    const protocol = connection.protocol;
    const repSetPath = connection.mongoPath.join(',');
    const options = connection.options;
    //options.server = {auto_reconnect: true, socketOptions: {keepAlive: 1, connectTimeoutMS: 30000 }};
    //options.replset = {socketOptions: {keepAlive: 1, connectTimeoutMS: 30000 }};

    this[_members][connectionName] = mongoose.createConnection(protocol + repSetPath, options);
    addConnectionEvents.call(this, connectionName);
  });
}

function addConnectionEvents(connectionName) {
  const connection = this[_members][connectionName];

  connection.on('connected', () => {
    logger.debug(`Mongoose connection open`);
  });
  connection.on('error', (err) => {
    logger.debug(`Mongoose connection error: ${err}`);
  });
  connection.on('exception', (err) => {
    logger.debug(`Mongoose connection exception: ${err}`);
  });
  connection.on('disconnected', () => {
    logger.debug(`Mongoose connection disconnected`);
  });
  connection.on('reconnected', () => {
    logger.debug(`Mongoose connection reconnected`);
  });
  connection.on('open', () => {
    logger.debug(`Mongoose connection is open`);
  });
  // If the Node process ends, close the Mongoose connection
  process.on('SIGINT', () => {
    connection.close(() => {
      logger.debug(`Mongoose default connection disconnected through app termination`);
      process.exit(0);
    });
  });
}

Upvotes: 2

Views: 1205

Answers (1)

Marina Dunst
Marina Dunst

Reputation: 870

I had the same problem. What I did is to restart mongoose in here:

connection.on('disconnected', () => {
    logger.debug(`Mongoose connection disconnected`);
    mongoose.connect(path);
  });

And I also use setTimeout() so that it tries every 3 secs.

mongoose.connection.on('disconnected', function () {  
  console.log('Mongoose default connection disconnected'); 
  console.log("Trying to reconnect");

  function connect(){
    mongoose.connect(dbPath, function(err){
      if(err) console.log("Error while trying to reconnect to MongoDB");
    });
  }
  setTimeout(connect, 3000);
});

And I also use ForeverJS.

Upvotes: 2

Related Questions