Node.js Mongodb Driver reuse connection and reconnect to cluster if connection lost

Hello, I’m new to using the node.js mongodb driver. In the past I’ve used mongoose for all mongodb related projects. I took M220JS and had a lot of unanswered questions. What is holding me up now is understanding the connection.

The docs point out how to connect and offer some examples here

But the example seems to be written for a one-off use-case rather than a real world application. For instance the example shows closing the connection after the request but in a real application, you would want to re-use the existing connection and take advantage of the connection pool. With that in mind, I have the following mongo.js

require('dotenv').config();
const { MongoClient } = require('mongodb');
const config = require('../../config/index');

const username = encodeURIComponent(config.mongo_db1.user);
const password = encodeURIComponent(config.mongo_db1.pass);
const dbHost = config.mongo_db1.host;
const authMechanism = 'DEFAULT';
const qString = `retryWrites=true&w=majority&authMechanism=${authMechanism}`;

const uri = `mongodb+srv://${username}:${password}@${dbHost}/?${qString}`;

const mongoOptions = {
  poolSize: 100,
  wtimeout: 2500,
  useNewUrlParser: true,
  useUnifiedTopology: true,
};

const client = new MongoClient(uri, mongoOptions);

let _db;

client.on('serverClosed', (event) => {
  // eslint-disable-next-line no-console
  console.log('received serverClosed');
  // eslint-disable-next-line no-console
  console.log(JSON.stringify(event, null, 2));

  // should i call mongoDBConnection() here if connection lost while app running?
});

const mongoDBConnection = async (app) => {
  try {
    if (client.isConnected()) {
      _db = client.db(config.mongo_db1.dbName);
      return client.db(config.mongo_db1.dbName);
    }

    await client.connect();
    if (app) app.use(passport.initialize());
    _db = client.db(config.mongo_db1.dbName);
    return client.db(config.mongo_db1.dbName);
  } catch (error) {
    return Promise.reject(error);
  }
};

const dbObj = () => _db;

module.exports = {
  mongoDBConnection,
  dbObj,
};

In my application, I call mongoDBConnection() in my app.js and it connects as expected on app startup. When it’s time to make a request to the db, I have 2 options with the above code. The first is to call mongoDBConnection() again and let the drivers client.isConnected() tell me if I should reconnect. The code in another file looks something like this:

const db = await mongoDB.mongoDBConnection();
const result = await db.collection('image').find({}).toArray(); 
console.log('what is result', result);

The second option is a little cleaner to use throughout the app because I can call the .dbObj() at the top of the file:

const db = mongoDB.dbObj();
const result = await db.collection('image').find({}).toArray();
console.log('what is result', result);

The problem with the cleaner option, is it doesn’t check if there is a connection issue. It uses whatever was assigned to _db. So if connection was lost, I don’t know what happens. The docs point out some options for handling reconnect here, but it’s only for a single server instance:

|`autoReconnect`|boolean|true|optional Enable autoReconnect for single server instances|

What do we do for reconnection to clusters? All I came up with so far from the docs is to listen for a disconnect

client.on('serverClosed', (event) => {
  // eslint-disable-next-line no-console
  console.log('received serverClosed');
  // eslint-disable-next-line no-console
  console.log(JSON.stringify(event, null, 2));

  // should i call mongoDBConnection() here if connection lost while app running?
});

I don’t know if calling mongoDBConnection() in the code above would make any since because I don’t think (not sure) it would update the already imported instance of dbObj() with the updated instance of the connection. Are there drivers to help test fail scenarios with mongodb?

In a nutshell, my questions are:

  • How do you re-use the connection in node.js with the driver? Is what I’m doing appropriate?
  • How do you handle re-connect if there is a connection issue with the node.js cluster?
  • Are there any tools I can use with node.js and the driver to simulate cluster issues like lost connection, slow response times, etc?

I tested loosing a connection to a cluster while operating on mongoDB.dbObj() after the original connection is made. I disconnected my network so it couldn’t reach mongodb atlas cluster, then made a query. The query waited for me to restore the connection, and then proceeded. I didn’t have to call client.connect again…awesome.

if i waited the >=30 seconds before restoring the connection, the server would timeout with getaddrinfo ENOTFOUND <cluster url>. If I try the request again after restoring the connection, the request worked…I didn’t have to restart the app server or call client.connect.

This makes me think what I’m doing is appropriate but I don’t have enough experience with the native driver to know what other things I should be testing to confirm this setup is good for production.

Another problem with the above is the timeout is not consistent. 30 seconds is the most consistent but I’ve had it wait for over a minute and not timeout. I’m not sure how to duplicate it consistently. I thought the 30 seconds came from connectimeoutMS here but setting it like below has no affect:

const mongoOptions = {
  poolSize: 100,
  wtimeout: 2500,
  connectTimeoutMS: 10000,
  useNewUrlParser: true,
  useUnifiedTopology: true,
};

At this point, i’m not sure if this is a bug in the 3.6 node.js driver or if I’m doing something wrong with my connection setup.

1 Like