Handling timeouts with Node.js and mongodb

16,384

Solution 1

UPD:
Based on this post, looks like they've deployed fix that will do the same as what we do here. Not sure if this is already within npm (15.10.13). https://github.com/mongodb/node-mongodb-native/issues/1092#ref-commit-2667d13

After some investigation I've managed to understand what is going on there:
Every time you call any method to deal with database (find, update, insert, etc.) it creates cursor, that has own ID and registers itself to EventEmitter of Db for being called back later. In meantime it registers itself to _notReplied object within same CallBackStore.

But once connection is closed, I couldn't locate anything that would iterate through _notReplied cursors and would trigger them with errors or any logic with timers (it still might be somewhere there). So I've managed to write small work around, that does force triggers cursors with error when DB emits close event:

new mongodb.Db('testdb', new mongodb.Server('localhost', 27017, { }), { safe: true }).open(function (err, db) {
  if (!err) {
    db.on('close', function() {
      if (this._callBackStore) {
        for(var key in this._callBackStore._notReplied) {
          this._callHandler(key, null, 'Connection Closed!');
        }
      }
    });

    // ...

  } else {
    console.log(err)
  }
});

I recommend using first approach instead of MongoClient. Reasons are few: for example when you close connection and then call .find it will properly trigger error in callback, while with MongoClient it won't.

If you are using MongoClient:

MongoClient.connect('mongodb://localhost:27017/testdb', function(err, db) {
  if (!err) {
    db.on('close', function() {
      if (this._callBackStore) {
        for(var key in this._callBackStore._notReplied) {
          this._callHandler(key, null, 'Connection Closed!');
        }
      }
    });

    // ...

  } else {
    console.log(err);
  }
});

What this will do? Once connection is closed, it will iterate through All _notReplied cursors and trigger events for them with error Connection Closed!.

Test case:

items.find({ }).toArray(function(err, data) {
  if (!err) {
    console.log('Items found successfully');
  } else {
    console.log(err);
  }
});
db.close();

That will force close database connection and trigger close event that you handle earlier and will make sure that cursor will be closed.

UPD: I've added Issue on GitHub: https://github.com/mongodb/node-mongodb-native/issues/1092 we'll see what they say regarding this.

Solution 2

I had the same problem, and found this page from google. But your choosed answer didn't resolve the problem and it is as same as you, this._callBackStore can't use

but i tried to wrap the Mongo, and it seems work fine

var MongoClient = require('mongodb').MongoClient;

var mongo = {};
mongo.init = function() {
  MongoClient.connect('mongodb://localhost:27017/testdb', function(err, db) {
    if (err) {
      mongo.DB = '';
    } else {
      mongo.DB = db;
    }
    db.on('close', function() {
      mongo.DB = '';
    });
    db.on('reconnect', function() {
      mongo.DB = db;
    });
  }
}
                      
mongo.getdb = function(callback) {
    if (mongo.DB) {
      callback(null, mongo.DB);
    } else {
      callback('can not connect to db', null);
    }
}
module.exports = mongo;

firstly start server and init() it

and then you can require it and use

mongo.getdb(function(err, db) {
  if (err) {
    console.log(err);
  } else {
    db.collection('user').find({'xxx':'xxx'}).toArray(function(err, items) {
      console.log(items);
    });
  }
});
Share:
16,384

Related videos on Youtube

Admin
Author by

Admin

Updated on September 15, 2022

Comments

  • Admin
    Admin over 1 year

    I am currently testing how some code stands up against the following scenario:

    • Node.js application is started and successfully establishes a connection to mongodb
    • After it has successfully setup a connection, the mongodb server dies and all subsequent requests fail

    To do this I have got the following code which makes use of the official driver (found here: https://github.com/mongodb/node-mongodb-native) :

    MongoClient.connect('mongodb://localhost:27017/testdb', function(err, db) {
    app.get('/test', function(req, res) {
        db.collection('users', function (err, collection) {
            console.log(err);
            if (err) {
                // ## POINT 1 ##
                // Handle the error
            }
            else {
                collection.find({ 'username': username }, { timeout: true }).toArray(function(err, items) {
                    console.log(err);
                    if (err) {
                        // ## POINT 2 ##
                        // Handle the error
                    }
                    else {
                        if (items.length > 0) {
                            // Do some stuff with the document that was found
                        }
                        else {
                            // Handle not finding the document
                        }
                    }
                }); 
            }
        });
    });
    

    });

    As the mongodb server is no longer running when the request is being handled, I'd made the assumption that at either the points which I have labelled ## POINT 1 ## or ## POINT 2 ##, it would return an error indicating a timeout; this however, isn't the case.

    I have tried a number of different settings (including one you can see here that explicitly allows the cursor to timeout), however I cannot seem to enable it in any way. In every configuration I've tried Node.js will simply keep waiting for the find() operation to callback and it never does.

    If I start the Node.js application before running mongodb, it catches the error in the connect callback fine, but if the connection dies after that it doesn't seem to handle it in any way.

    Is there a setting I am missing or is there no way to detect connections being terminated after they've been established?

    Edit: just to be clear, the username variable used in the find method is actually declared in my full code, the code I've put in this post is a cut down version to illustrate the structure and error checking.

    • Jesus Ruiz
      Jesus Ruiz over 10 years
      This is a good question: db.collection (point #1 above) will not return an error when the db connection is lost. collection.find (point #2 above) will eventually return an error, but it will be after more than 1 min. I played with the connectionTimeoutMS and the socketTimeoutMS settings in the connection string, but the behavior did not change.