Skip to content

Commit 96f9b22

Browse files
committed
NODE-764 Better error messages when replicaset param not set for MongoClient.connect
1 parent 3e72bd8 commit 96f9b22

File tree

3 files changed

+242
-4
lines changed

3 files changed

+242
-4
lines changed

lib/mongo_client.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ var parse = require('./url_parser')
77
, Define = require('./metadata')
88
, ReadPreference = require('./read_preference')
99
, Logger = require('mongodb-core').Logger
10+
, MongoError = require('mongodb-core').MongoError
1011
, Db = require('./db')
1112
, dns = require('dns')
12-
, f = require('util').format
13+
, f = require('util').format
1314
, shallowClone = require('./utils').shallowClone;
1415

1516
/**
@@ -277,13 +278,27 @@ var connect = function(url, options, callback) {
277278
throw new Error("connection string must contain at least one seed host");
278279
}
279280

281+
function connectCallback(err, db) {
282+
if(err && err.message == 'no mongos proxies found in seed list') {
283+
if(logger.isWarn()) {
284+
logger.warn(f('seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name'));
285+
}
286+
287+
// Return a more specific error message for MongoClient.connect
288+
return callback(new MongoError('seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name'));
289+
}
290+
291+
// Return the error and db instance
292+
callback(err, db);
293+
}
294+
280295
// Do we have a replicaset then skip discovery and go straight to connectivity
281296
if(_finalOptions.replicaSet || _finalOptions.rs_name) {
282-
return createReplicaset(_finalOptions, connectHandler(_finalOptions, callback));
297+
return createReplicaset(_finalOptions, connectHandler(_finalOptions, connectCallback));
283298
} else if(object.servers.length > 1) {
284-
return createMongos(_finalOptions, connectHandler(_finalOptions, callback));
299+
return createMongos(_finalOptions, connectHandler(_finalOptions, connectCallback));
285300
} else {
286-
return createServer(_finalOptions, connectHandler(_finalOptions, callback));
301+
return createServer(_finalOptions, connectHandler(_finalOptions, connectCallback));
287302
}
288303
}
289304

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
"use strict"
2+
3+
exports['Should correctly print warning when non mongos proxy passed in seed list'] = {
4+
metadata: {
5+
requires: {
6+
generators: true,
7+
topology: "single"
8+
}
9+
},
10+
11+
test: function(configuration, test) {
12+
var MongoClient = configuration.require.MongoClient,
13+
ObjectId = configuration.require.ObjectId,
14+
ReadPreference = configuration.require.ReadPreference,
15+
Logger = configuration.require.Logger,
16+
Long = configuration.require.Long,
17+
Code = configuration.require.Code,
18+
co = require('co'),
19+
mockupdb = require('../mock');
20+
21+
// Contain mock server
22+
var mongos1 = null;
23+
var mongos2 = null;
24+
var running = true;
25+
// Current index for the ismaster
26+
var currentStep = 0;
27+
// Primary stop responding
28+
var stopRespondingPrimary = false;
29+
var port = null;
30+
31+
// Extend the object
32+
var extend = function(template, fields) {
33+
for(var name in template) fields[name] = template[name];
34+
return fields;
35+
}
36+
37+
// Default message fields
38+
var defaultFields = {
39+
"ismaster" : true,
40+
"msg" : "isdbgrid",
41+
"maxBsonObjectSize" : 16777216,
42+
"maxMessageSizeBytes" : 48000000,
43+
"maxWriteBatchSize" : 1000,
44+
"localTime" : new Date(),
45+
"maxWireVersion" : 3,
46+
"minWireVersion" : 0,
47+
"ok" : 1
48+
}
49+
50+
// Default message fields
51+
var defaultRSFields = {
52+
"setName": "rs", "setVersion": 1, "electionId": new ObjectId(),
53+
"maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000,
54+
"maxWriteBatchSize" : 1000, "localTime" : new Date(), "maxWireVersion" : 4,
55+
"minWireVersion" : 0, "ok" : 1, "hosts": ["localhost:32000", "localhost:32001", "localhost:32002"], "arbiters": ["localhost:32002"]
56+
}
57+
58+
// Primary server states
59+
var serverIsMaster = [extend(defaultFields, {}), extend(defaultRSFields, {})];
60+
61+
// Boot the mock
62+
co(function*() {
63+
mongos1 = yield mockupdb.createServer(52000, 'localhost');
64+
mongos2 = yield mockupdb.createServer(52001, 'localhost');
65+
66+
// Mongos
67+
co(function*() {
68+
while(running) {
69+
var request = yield mongos1.receive();
70+
71+
// Get the document
72+
var doc = request.document;
73+
if(doc.ismaster) {
74+
request.reply(serverIsMaster[0]);
75+
} else if(doc.insert) {
76+
request.reply({ok:1, n:doc.documents, lastOp: new Date()});
77+
}
78+
}
79+
});
80+
81+
// Mongos
82+
co(function*() {
83+
while(running) {
84+
var request = yield mongos2.receive();
85+
86+
// Get the document
87+
var doc = request.document;
88+
if(doc.ismaster) {
89+
request.reply(serverIsMaster[1]);
90+
} else if(doc.insert) {
91+
request.reply({ok:1, n:doc.documents, lastOp: new Date()});
92+
}
93+
}
94+
});
95+
96+
var logger = Logger.currentLogger();
97+
Logger.setCurrentLogger(function(msg, state) {
98+
test.equal('warn', state.type);
99+
test.equal('expected mongos proxy, but found replicaset member mongod for server localhost:52001', state.message);
100+
});
101+
102+
MongoClient.connect('mongodb://localhost:52000,localhost:52001/test', function(err, db) {
103+
Logger.setCurrentLogger(logger);
104+
test.equal(null, err);
105+
106+
running = false;
107+
db.close();
108+
mongos1.destroy();
109+
mongos2.destroy();
110+
test.done();
111+
});
112+
});
113+
}
114+
}
115+
116+
exports['Should correctly print warning and error when no mongos proxies in seed list'] = {
117+
metadata: {
118+
requires: {
119+
generators: true,
120+
topology: "single"
121+
}
122+
},
123+
124+
test: function(configuration, test) {
125+
var MongoClient = configuration.require.MongoClient,
126+
ObjectId = configuration.require.ObjectId,
127+
ReadPreference = configuration.require.ReadPreference,
128+
Logger = configuration.require.Logger,
129+
Long = configuration.require.Long,
130+
Code = configuration.require.Code,
131+
co = require('co'),
132+
mockupdb = require('../mock');
133+
134+
// Contain mock server
135+
var mongos1 = null;
136+
var mongos2 = null;
137+
var running = true;
138+
// Current index for the ismaster
139+
var currentStep = 0;
140+
// Primary stop responding
141+
var stopRespondingPrimary = false;
142+
var port = null;
143+
144+
// Extend the object
145+
var extend = function(template, fields) {
146+
for(var name in template) fields[name] = template[name];
147+
return fields;
148+
}
149+
150+
// Default message fields
151+
var defaultRSFields = {
152+
"setName": "rs", "setVersion": 1, "electionId": new ObjectId(),
153+
"maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000,
154+
"maxWriteBatchSize" : 1000, "localTime" : new Date(), "maxWireVersion" : 4,
155+
"minWireVersion" : 0, "ok" : 1, "hosts": ["localhost:32000", "localhost:32001", "localhost:32002"], "arbiters": ["localhost:32002"]
156+
}
157+
158+
// Primary server states
159+
var serverIsMaster = [extend(defaultRSFields, {}), extend(defaultRSFields, {})];
160+
161+
// Boot the mock
162+
co(function*() {
163+
mongos1 = yield mockupdb.createServer(52000, 'localhost');
164+
mongos2 = yield mockupdb.createServer(52001, 'localhost');
165+
166+
// Mongos
167+
co(function*() {
168+
while(running) {
169+
var request = yield mongos1.receive();
170+
171+
// Get the document
172+
var doc = request.document;
173+
if(doc.ismaster) {
174+
request.reply(serverIsMaster[0]);
175+
} else if(doc.insert) {
176+
request.reply({ok:1, n:doc.documents, lastOp: new Date()});
177+
}
178+
}
179+
});
180+
181+
// Mongos
182+
co(function*() {
183+
while(running) {
184+
var request = yield mongos2.receive();
185+
186+
// Get the document
187+
var doc = request.document;
188+
if(doc.ismaster) {
189+
request.reply(serverIsMaster[1]);
190+
} else if(doc.insert) {
191+
request.reply({ok:1, n:doc.documents, lastOp: new Date()});
192+
}
193+
}
194+
});
195+
196+
var warnings = [];
197+
198+
var logger = Logger.currentLogger();
199+
Logger.setCurrentLogger(function(msg, state) {
200+
test.equal('warn', state.type);
201+
warnings.push(state);
202+
});
203+
204+
MongoClient.connect('mongodb://localhost:52000,localhost:52001/test', function(err, db) {
205+
Logger.setCurrentLogger(logger);
206+
207+
// Assert all warnings
208+
test.equal('expected mongos proxy, but found replicaset member mongod for server localhost:52000', warnings[0].message);
209+
test.equal('expected mongos proxy, but found replicaset member mongod for server localhost:52001', warnings[1].message);
210+
test.equal('no mongos proxies found in seed list, did you mean to connect to a replicaset', warnings[2].message);
211+
test.equal('seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name', warnings[3].message);
212+
// Assert error
213+
test.equal('seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name', err.message);
214+
215+
running = false;
216+
mongos1.destroy();
217+
mongos2.destroy();
218+
test.done();
219+
});
220+
});
221+
}
222+
}

test/runner.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ try {
345345
testFiles.push('/test/functional/operation_generators_example_tests.js');
346346
// Mock tests
347347
testFiles.push('/test/functional/command_write_concern_tests.js');
348+
testFiles.push('/test/functional/replicaset_mock_tests.js');
348349
} catch(err) {}
349350

350351
// Add all the tests to run

0 commit comments

Comments
 (0)