From 65a1bd61dbd75ee342e1d8cf423ffc2077acb12d Mon Sep 17 00:00:00 2001 From: Guillermo Rauch Date: Fri, 25 Feb 2011 20:37:26 -0800 Subject: [PATCH] Added replica sets support --- lib/mongoose/connection.js | 88 ++++++++++++++++--- .../drivers/node-mongodb-native/connection.js | 31 ++++++- lib/mongoose/index.js | 27 ++++++ test/index.test.js | 38 ++++++++ 4 files changed, 169 insertions(+), 15 deletions(-) diff --git a/lib/mongoose/connection.js b/lib/mongoose/connection.js index 98d14913345..15b9cd0bd71 100644 --- a/lib/mongoose/connection.js +++ b/lib/mongoose/connection.js @@ -118,18 +118,77 @@ Connection.prototype.open = function (host, database, port, callback) { this.emit('opening'); // open connection - this.doOpen(function(err){ + this.doOpen(function (err) { if (err) { - if (typeof callback == 'function') callback(err); + if (typeof callback == 'function') + callback(err); } else { self.onOpen(); - if (typeof callback == 'function') callback(null); + + if (typeof callback == 'function') + callback(null); } }); return this; }; +/** + * Connects to a replica set. + * + * Supply a comma-separted list of mongodb:// URIs. You only need to specify + * the database name and/or auth to one of them. + * + * @param {String} comma-separated mongodb:// URIs + * @param {Function} optional callback + */ + +Connection.prototype.openSet = function (uris, database, callback) { + var uris = uris.split(',') + , self = this; + + if (uris.length < 2) { + if (callback) callback(new Error('Please provide comma-separated URIs')); + return this; + } + + // signal connecting + this.readyState = 2; + this.emit('opening'); + + this.host = []; + this.port = []; + + uris.forEach(function (uri) { + var uri = url.parse(uri); + + self.host.push(uri.hostname); + self.port.push(uri.port || 27017); + + if (!self.name && uri.pathname.replace(/\//g, '')) + self.name = uri.pathname.replace(/\//g, ''); + + if (!self.user && uri.auth) { + var auth = uri.auth.split(':'); + self.user = auth[0]; + self.pass = auth[1]; + } + }); + + // open connection + this.doOpenSet(function (err) { + if (err) { + if (typeof callback == 'function') + callback(err); + } else { + self.onOpen(); + + if (typeof callback == 'function') + callback(null); + } + }); +}; + /** * Called when the connection is opened * @@ -139,21 +198,22 @@ Connection.prototype.open = function (host, database, port, callback) { Connection.prototype.onOpen = function () { var self = this; - var continuation = function(){ - self.readyState = 1; - // avoid having the collection subscribe to our event emitter - // to prevent 0.3 warning - for (var i in self.collections) - self.collections[i].onOpen(); + function open () { + self.readyState = 1; + + // avoid having the collection subscribe to our event emitter + // to prevent 0.3 warning + for (var i in self.collections) + self.collections[i].onOpen(); - self.emit('open'); + self.emit('open'); }; - //do authentication before we continue if a database username and password exist - if(self.user && self.pass) - self.db.authenticate(self.user,self.pass,continuation); + // re-authenticate + if (self.user && self.pass) + self.db.authenticate(self.user, self.pass, open); else - continuation(); + open(); }; /** diff --git a/lib/mongoose/drivers/node-mongodb-native/connection.js b/lib/mongoose/drivers/node-mongodb-native/connection.js index 6a8eaa5cda2..d7036201f99 100644 --- a/lib/mongoose/drivers/node-mongodb-native/connection.js +++ b/lib/mongoose/drivers/node-mongodb-native/connection.js @@ -4,7 +4,9 @@ */ var Connection = require('../../connection') - , mongo = require('../../../../support/node-mongodb-native/lib/mongodb/'); + , mongo = require('../../../../support/node-mongodb-native/lib/mongodb/') + , Server = mongo.Server + , ReplSetServers = mongo.ReplSetServers; /** * Connection for mongodb-native driver @@ -32,10 +34,37 @@ NativeConnection.prototype.__proto__ = Connection.prototype; NativeConnection.prototype.doOpen = function (fn) { if (!this.db) this.db = new mongo.Db(this.name, new mongo.Server(this.host, this.port)); + this.db.open(fn); + return this; }; +/** + * Opens a set connection + * + * @param {Function} callback + * @api private + */ + +NativeConnection.prototype.doOpenSet = function (fn) { + if (!this.db) { + var servers = [] + , ports = this.port; + + this.host.forEach(function (host, i) { + servers.push(new mongo.Server(host, ports[i], {})); + }); + + this.db = new mongo.Db(this.name, new ReplSetServers(servers)); + } + + this.db.open(fn); + + return this; +}; + + /** * Closes the connection * diff --git a/lib/mongoose/index.js b/lib/mongoose/index.js index 1a7a149b26b..017b53fca4d 100644 --- a/lib/mongoose/index.js +++ b/lib/mongoose/index.js @@ -76,6 +76,21 @@ Mongoose.prototype.createConnection = function () { return conn; }; +/** + * Creates a replica set connection + * + * @see {Mongoose#createConnection} + * @api public + */ + +Mongoose.prototype.createSetConnection = function () { + var conn = new Connection(this); + this.connections.push(conn); + if (arguments.length) + conn.openSet.apply(conn, arguments); + return conn; +}; + /** * Connects the default mongoose connection * @@ -88,6 +103,18 @@ Mongoose.prototype.connect = function (){ return this; }; +/** + * Connects the default mongoose connection to a replica set + * + * @see {Mongoose#createConnection} + * @api public + */ + +Mongoose.prototype.connectSet = function (){ + this.connection.openSet.apply(this.connection, arguments); + return this; +}; + /** * Disconnects from all connections. * diff --git a/test/index.test.js b/test/index.test.js index f8f844b64a1..775fd6cacaa 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -148,6 +148,44 @@ module.exports = { should.strictEqual(err, null); mong.connection.close(); }); + }, + + 'test connecting to a replica set': function () { + var uri = process.env.MONGOOSE_SET_TEST_URI; + + if (!uri) { + console.log('\033[31m', '\n', 'You\'re not testing for replica sets!' + , '\n', 'Please set the MONGOOSE_SET_TEST_URI env variable.', '\n' + , 'e.g: `mongodb://localhost:27017/db,mongodb://localhost…`', '\n' + , '\033[39m'); + return; + } + + var mong = new Mongoose(); + + mongoose.connectSet(uri, function (err) { + should.strictEqual(err, null); + + mong.model('Test', new mongoose.Schema({ + test: String + })); + + var Test = mong.model('Test') + , test = new Test(); + + test.test = 'aa'; + test.save(function (err) { + should.strictEqual(err, null); + + Test.findById(test._id, function (err, doc) { + should.strictEqual(err, null); + + doc.test.should.eql('aa'); + + mongoose.connection.close(); + }); + }); + }); } };