This implementation was derived from the following requirements. I would like to learn that whether the implementation is clean enough, without memory leak and performance hit.
Requirements
- Always return either ready to be used DB object, or error if not available.
- Reconnect automatically (passive), even in the event of explicit call of
db.close()
occurred. - Unessential implementation of a
express.js
compatible middleware to inject the DB object intorequest
object. This may be ignored while reviewing.
Possible issues
- Possible memory leak on registering
error
andclose
events handler (not sure since my understanding of theonce
interface is that it would immediately unregister the handler after being invoked). - Possible "race condition" on async calls of the
get()
function which would cause unexpected creation of connections.
For the "race condition" one, how do I safely and elegantly solve it? Also, if there're already proper implementations available that fits the requirement and doesn't exhibit any of the concerns and known issues, I'd be appreciated to learn as well.
Module implementation
/*
Mongo helper, exposes access to Mongo DB object as req.mongo, as well as a
get() method for similar effect. Reconnection is wrapped and handled within.
*/
var MongoClient = require('mongodb').MongoClient;
var logger = require('tp-logger')();
module.exports = function() {
// arguments to be forwarded to MongoClient.connect()
var _args = arguments;
// reused db
var _db;
// connected flag
var _connected = false;
// DB error/close events handler
var _onClose = function(err) {
if (err) {
if (_db) _db.close();
logger.error('MongoDB closed on error', err.stack);
} else {
logger.log('MongoDB closed');
}
_connected = false;
};
function _get(callback) {
// already connected
if (_connected) return callback(null, _db);
// reset connection
MongoClient.connect.apply(MongoClient, _args).then(function(db) {
_connected = true;
_db = db;
_db.once('close', _onClose).once('error', _onClose);
return callback(null, _db);
}).catch(function(err) {
_connected = false;
return callback(err);
});
}
return {
// express.js compatible middleware
middleware: function(req, res, next) {
_get(function(err, db) {
if (err) return next(err);
req.mongo = db;
return next();
});
},
get: _get
};
};