Skip to content

Commit c5d60fc

Browse files
author
Thomas Reggi
authored
feat: directConnection adds unify behavior for replica set discovery
Adds `directConnection` option to unify behavior around configuration for replica set discovery. Migrated mongodb/specifications tests from commit "e56f5eceed7729f8b9b43a4a1f76c7e5840db49f". Skips SDAM tests for legacy topology / behavior that we do not intend to introduce to the legacy topology types. Users should switch to the unified topology. NODE-2452
1 parent 60e31b0 commit c5d60fc

File tree

81 files changed

+1608
-76
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+1608
-76
lines changed

lib/core/sdam/topology.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -813,10 +813,16 @@ function parseStringSeedlist(seedlist) {
813813
}
814814

815815
function topologyTypeFromSeedlist(seedlist, options) {
816+
if (options.directConnection) {
817+
return TopologyType.Single;
818+
}
819+
816820
const replicaSet = options.replicaSet || options.setName || options.rs_name;
817-
if (seedlist.length === 1 && !replicaSet) return TopologyType.Single;
818-
if (replicaSet) return TopologyType.ReplicaSetNoPrimary;
819-
return TopologyType.Unknown;
821+
if (replicaSet == null) {
822+
return TopologyType.Unknown;
823+
}
824+
825+
return TopologyType.ReplicaSetNoPrimary;
820826
}
821827

822828
function randomSelection(array) {

lib/core/sdam/topology_description.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ class TopologyDescription {
161161
}
162162

163163
if (topologyType === TopologyType.Unknown) {
164-
if (serverType === ServerType.Standalone) {
164+
if (serverType === ServerType.Standalone && this.servers.size !== 1) {
165165
serverDescriptions.delete(address);
166166
} else {
167167
topologyType = topologyTypeForServerType(serverType);
@@ -274,8 +274,22 @@ class TopologyDescription {
274274
}
275275

276276
function topologyTypeForServerType(serverType) {
277-
if (serverType === ServerType.Mongos) return TopologyType.Sharded;
278-
if (serverType === ServerType.RSPrimary) return TopologyType.ReplicaSetWithPrimary;
277+
if (serverType === ServerType.Standalone) {
278+
return TopologyType.Single;
279+
}
280+
281+
if (serverType === ServerType.Mongos) {
282+
return TopologyType.Sharded;
283+
}
284+
285+
if (serverType === ServerType.RSPrimary) {
286+
return TopologyType.ReplicaSetWithPrimary;
287+
}
288+
289+
if (serverType === ServerType.RSGhost || serverType === ServerType.Unknown) {
290+
return TopologyType.Unknown;
291+
}
292+
279293
return TopologyType.ReplicaSetNoPrimary;
280294
}
281295

lib/core/uri_parser.js

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ function matchesParentDomain(srvAddress, parentDomain) {
3737
function parseSrvConnectionString(uri, options, callback) {
3838
const result = URL.parse(uri, true);
3939

40+
if (options.directConnection || options.directconnection) {
41+
return callback(new MongoParseError('directConnection not supported with SRV URI'));
42+
}
43+
4044
if (result.hostname.split('.').length < 3) {
4145
return callback(new MongoParseError('URI does not have hostname, domain name and tld'));
4246
}
@@ -215,7 +219,8 @@ const CASE_TRANSLATION = {
215219
tlscertificatekeyfile: 'tlsCertificateKeyFile',
216220
tlscertificatekeyfilepassword: 'tlsCertificateKeyFilePassword',
217221
wtimeout: 'wTimeoutMS',
218-
j: 'journal'
222+
j: 'journal',
223+
directconnection: 'directConnection'
219224
};
220225

221226
/**
@@ -565,10 +570,6 @@ function parseConnectionString(uri, options, callback) {
565570
return callback(new MongoParseError('Invalid protocol provided'));
566571
}
567572

568-
if (protocol === PROTOCOL_MONGODB_SRV) {
569-
return parseSrvConnectionString(uri, options, callback);
570-
}
571-
572573
const dbAndQuery = cap[4].split('?');
573574
const db = dbAndQuery.length > 0 ? dbAndQuery[0] : null;
574575
const query = dbAndQuery.length > 1 ? dbAndQuery[1] : null;
@@ -581,6 +582,11 @@ function parseConnectionString(uri, options, callback) {
581582
}
582583

583584
parsedOptions = Object.assign({}, parsedOptions, options);
585+
586+
if (protocol === PROTOCOL_MONGODB_SRV) {
587+
return parseSrvConnectionString(uri, parsedOptions, callback);
588+
}
589+
584590
const auth = { username: null, password: null, db: db && db !== '' ? qs.unescape(db) : null };
585591
if (parsedOptions.auth) {
586592
// maintain support for legacy options passed into `MongoClient`
@@ -674,6 +680,22 @@ function parseConnectionString(uri, options, callback) {
674680
return callback(new MongoParseError('No hostname or hostnames provided in connection string'));
675681
}
676682

683+
const directConnection = !!parsedOptions.directConnection;
684+
if (directConnection && hosts.length !== 1) {
685+
// If the option is set to true, the driver MUST validate that there is exactly one host given
686+
// in the host list in the URI, and fail client creation otherwise.
687+
return callback(new MongoParseError('directConnection option requires exactly one host'));
688+
}
689+
690+
// NOTE: this behavior will go away in v4.0, we will always auto discover there
691+
if (
692+
parsedOptions.directConnection == null &&
693+
hosts.length === 1 &&
694+
parsedOptions.replicaSet == null
695+
) {
696+
parsedOptions.directConnection = true;
697+
}
698+
677699
const result = {
678700
hosts: hosts,
679701
auth: auth.db || auth.username ? auth : null,

lib/mongo_client.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ const validOptions = require('./operations/connect').validOptions;
145145
* @param {boolean} [options.useUnifiedTopology] Enables the new unified topology layer
146146
* @param {AutoEncrypter~AutoEncryptionOptions} [options.autoEncryption] Optionally enable client side auto encryption
147147
* @param {DriverInfoOptions} [options.driverInfo] Allows a wrapping driver to amend the client metadata generated by the driver to include information about the wrapping driver
148+
* @param {boolean} [options.directConnection=false] Enable directConnection
148149
* @param {MongoClient~connectCallback} [callback] The command result callback
149150
* @return {MongoClient} a MongoClient instance
150151
*/
@@ -406,6 +407,7 @@ MongoClient.prototype.isConnected = function(options) {
406407
* @param {number} [options.numberOfRetries=5] The number of retries for a tailable cursor
407408
* @param {boolean} [options.auto_reconnect=true] Enable auto reconnecting for single server instances
408409
* @param {number} [options.minSize] If present, the connection pool will be initialized with minSize connections, and will never dip below minSize connections
410+
* @param {boolean} [options.directConnection=false] Enable directConnection
409411
* @param {MongoClient~connectCallback} [callback] The command result callback
410412
* @return {Promise<MongoClient>} returns Promise if no callback passed
411413
*/

lib/operations/connect.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ const validOptionNames = [
153153
'tlsCertificateKeyFilePassword',
154154
'minHeartbeatFrequencyMS',
155155
'heartbeatFrequencyMS',
156-
'waitQueueTimeoutMS'
156+
'waitQueueTimeoutMS',
157+
'directConnection'
157158
];
158159

159160
const ignoreOptionNames = ['native_parser'];

test/functional/core/replset_state.test.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,21 @@ describe('ReplicaSet state', function() {
1212

1313
fs.readdirSync(path)
1414
.filter(x => x.indexOf('.json') !== -1)
15-
.filter(x => !x.includes('repeated'))
1615
.forEach(x => {
17-
var testData = require(f('%s/%s', path, x));
16+
const testData = require(f('%s/%s', path, x));
17+
const description = testData.description;
18+
it(description, function(done) {
19+
if (
20+
description.match(/Repeated ismaster response must be processed/) ||
21+
description.match(/Primary mismatched me is not removed/) ||
22+
description.match(/replicaSet URI option causes starting topology to be RSNP/) ||
23+
description.match(/Discover secondary with directConnection URI option/) ||
24+
description.match(/Discover ghost with directConnection URI option/)
25+
) {
26+
this.skip();
27+
return;
28+
}
1829

19-
it(testData.description, function(done) {
2030
executeEntry(testData, done);
2131
});
2232
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"uri": "mongodb+srv://test3.test.build.10gen.cc/?directConnection=false",
3+
"seeds": [
4+
"localhost.test.build.10gen.cc:27017"
5+
],
6+
"hosts": [
7+
"localhost:27017",
8+
"localhost:27018",
9+
"localhost:27019"
10+
],
11+
"options": {
12+
"ssl": true
13+
}
14+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
uri: "mongodb+srv://test3.test.build.10gen.cc/?directConnection=false"
2+
seeds:
3+
- localhost.test.build.10gen.cc:27017
4+
hosts:
5+
- localhost:27017
6+
- localhost:27018
7+
- localhost:27019
8+
options:
9+
ssl: true
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"uri": "mongodb+srv://test3.test.build.10gen.cc/?directConnection=true",
3+
"seeds": [],
4+
"hosts": [],
5+
"error": true,
6+
"comment": "Should fail because directConnection=true is incompatible with SRV URIs."
7+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
uri: "mongodb+srv://test3.test.build.10gen.cc/?directConnection=true"
2+
seeds: []
3+
hosts: []
4+
error: true
5+
comment: Should fail because directConnection=true is incompatible with SRV URIs.

0 commit comments

Comments
 (0)