-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: directConnection adds unify behavior for replica set discovery #2349
Changes from 8 commits
c80b42f
ae14f1c
0a50a18
29bb331
67a5dda
a67f83c
ad68403
e14501c
4b5dedc
c1ee288
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -143,6 +143,7 @@ const { connect, validOptions } = require('./operations/connect'); | |
* @param {boolean} [options.useNewUrlParser=true] Determines whether or not to use the new url parser. Enables the new, spec-compliant, url parser shipped in the core driver. This url parser fixes a number of problems with the original parser, and aims to outright replace that parser in the near future. Defaults to true, and must be explicitly set to false to use the legacy url parser. | ||
* @param {DriverInfoOptions} [options.driverInfo] Allows a wrapping driver to amend the client metadata generated by the driver to include information about the wrapping driver | ||
* @param {AutoEncrypter~AutoEncryptionOptions} [options.autoEncryption] Optionally enable client side auto encryption | ||
* @param {boolean} [options.directConnection=false] Enable directConnection | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we improve this doc string? "Indicates that a client should directly connect to a node without attempting to discover its topology type" or something like that |
||
* @returns {MongoClient} a MongoClient instance | ||
*/ | ||
function MongoClient(url, options) { | ||
|
@@ -388,6 +389,7 @@ MongoClient.prototype.isConnected = function(options) { | |
* @param {number} [options.numberOfRetries=5] The number of retries for a tailable cursor | ||
* @param {boolean} [options.auto_reconnect=true] Enable auto reconnecting for single server instances | ||
* @param {number} [options.minSize] If present, the connection pool will be initialized with minSize connections, and will never dip below minSize connections | ||
* @param {boolean} [options.directConnection=false] Enable directConnection | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same comment |
||
* @param {MongoClient~connectCallback} [callback] The command result callback | ||
* @returns {Promise<MongoClient>} returns Promise if no callback passed | ||
*/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,6 +101,10 @@ class Topology extends EventEmitter { | |
* @param {number} [options.localThresholdMS=15] The size of the latency window for selecting among multiple suitable servers | ||
* @param {number} [options.serverSelectionTimeoutMS=30000] How long to block for server selection before throwing an error | ||
* @param {number} [options.heartbeatFrequencyMS=10000] The frequency with which topology updates are scheduled | ||
* @param {boolean} [options.directConnection] will set topology to Single | ||
* @param {string} [options.replicaSet] will set topology to ReplicaSetNoPrimary | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
* @param {string} [options.setName] will set topology to ReplicaSetNoPrimary | ||
* @param {string} [options.rs_name] will set topology to ReplicaSetNoPrimary | ||
*/ | ||
constructor(seedlist, options) { | ||
super(); | ||
|
@@ -136,7 +140,7 @@ class Topology extends EventEmitter { | |
} | ||
}); | ||
|
||
const topologyType = topologyTypeFromSeedlist(seedlist, options); | ||
const topologyType = topologyTypeFromOptions(options); | ||
const topologyId = globalTopologyCounter++; | ||
const serverDescriptions = seedlist.reduce((result, seed) => { | ||
if (seed.domain_socket) seed.host = seed.domain_socket; | ||
|
@@ -797,10 +801,26 @@ function parseStringSeedlist(seedlist) { | |
})); | ||
} | ||
|
||
function topologyTypeFromSeedlist(seedlist, options) { | ||
/** | ||
* Gets the TopologyType from the client options | ||
* | ||
* @param {object} options mongo client options | ||
* @param {boolean} [options.directConnection] will set topology to Single | ||
* @param {string} [options.replicaSet] will set topology to ReplicaSetNoPrimary | ||
* @param {string} [options.setName] will set topology to ReplicaSetNoPrimary | ||
* @param {string} [options.rs_name] will set topology to ReplicaSetNoPrimary | ||
* @returns TopologyType | ||
*/ | ||
function topologyTypeFromOptions(options) { | ||
if (options.directConnection) { | ||
return TopologyType.Single; | ||
} | ||
|
||
const replicaSet = options.replicaSet || options.setName || options.rs_name; | ||
if (seedlist.length === 1 && !replicaSet) return TopologyType.Single; | ||
if (replicaSet) return TopologyType.ReplicaSetNoPrimary; | ||
if (replicaSet) { | ||
return TopologyType.ReplicaSetNoPrimary; | ||
} | ||
|
||
return TopologyType.Unknown; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -163,7 +163,7 @@ class TopologyDescription { | |
} | ||
|
||
if (topologyType === TopologyType.Unknown) { | ||
if (serverType === ServerType.Standalone) { | ||
if (serverType === ServerType.Standalone && this.servers.size !== 1) { | ||
emadum marked this conversation as resolved.
Show resolved
Hide resolved
|
||
serverDescriptions.delete(address); | ||
} else { | ||
topologyType = topologyTypeForServerType(serverType); | ||
|
@@ -277,9 +277,19 @@ class TopologyDescription { | |
} | ||
|
||
function topologyTypeForServerType(serverType) { | ||
if (serverType === ServerType.Mongos) return TopologyType.Sharded; | ||
if (serverType === ServerType.RSPrimary) return TopologyType.ReplicaSetWithPrimary; | ||
return TopologyType.ReplicaSetNoPrimary; | ||
switch (serverType) { | ||
case ServerType.Standalone: | ||
return TopologyType.Single; | ||
case ServerType.Mongos: | ||
return TopologyType.Sharded; | ||
case ServerType.RSPrimary: | ||
return TopologyType.ReplicaSetWithPrimary; | ||
case ServerType.RSOther: | ||
case ServerType.RSSecondary: | ||
return TopologyType.ReplicaSetNoPrimary; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It didn't make sense to me to have the "default" be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I don't find that using a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I generally don't prefer switch syntax, but i think @emadum's recommendation makes sense here it does help restrict what this function can do. It makes it more obvious that this function takes a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. default |
||
default: | ||
return TopologyType.Unknown; | ||
} | ||
} | ||
|
||
function compareObjectId(oid1, oid2) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"uri": "mongodb+srv://test3.test.build.10gen.cc/?directConnection=false", | ||
"seeds": [ | ||
"localhost.test.build.10gen.cc:27017" | ||
], | ||
"hosts": [ | ||
"localhost:27017", | ||
"localhost:27018", | ||
"localhost:27019" | ||
], | ||
"options": { | ||
"ssl": true | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
uri: "mongodb+srv://test3.test.build.10gen.cc/?directConnection=false" | ||
seeds: | ||
- localhost.test.build.10gen.cc:27017 | ||
hosts: | ||
- localhost:27017 | ||
- localhost:27018 | ||
- localhost:27019 | ||
options: | ||
ssl: true |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"uri": "mongodb+srv://test3.test.build.10gen.cc/?directConnection=true", | ||
"seeds": [], | ||
"hosts": [], | ||
"error": true, | ||
"comment": "Should fail because directConnection=true is incompatible with SRV URIs." | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
uri: "mongodb+srv://test3.test.build.10gen.cc/?directConnection=true" | ||
seeds: [] | ||
hosts: [] | ||
error: true | ||
comment: Should fail because directConnection=true is incompatible with SRV URIs. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
{ | ||
"description": "Discover arbiters with replicaSet URI option", | ||
"uri": "mongodb://a/?replicaSet=rs", | ||
"phases": [ | ||
{ | ||
"responses": [ | ||
[ | ||
"a:27017", | ||
{ | ||
"ok": 1, | ||
"ismaster": true, | ||
"hosts": [ | ||
"a:27017" | ||
], | ||
"arbiters": [ | ||
"b:27017" | ||
], | ||
"setName": "rs", | ||
"minWireVersion": 0, | ||
"maxWireVersion": 6 | ||
} | ||
] | ||
], | ||
"outcome": { | ||
"servers": { | ||
"a:27017": { | ||
"type": "RSPrimary", | ||
"setName": "rs" | ||
}, | ||
"b:27017": { | ||
"type": "Unknown", | ||
"setName": null | ||
} | ||
}, | ||
"topologyType": "ReplicaSetWithPrimary", | ||
"logicalSessionTimeoutMinutes": null, | ||
"setName": "rs" | ||
} | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
description: "Discover arbiters with replicaSet URI option" | ||
|
||
uri: "mongodb://a/?replicaSet=rs" | ||
|
||
phases: [ | ||
|
||
{ | ||
responses: [ | ||
|
||
["a:27017", { | ||
|
||
ok: 1, | ||
ismaster: true, | ||
hosts: ["a:27017"], | ||
arbiters: ["b:27017"], | ||
setName: "rs", | ||
minWireVersion: 0, | ||
maxWireVersion: 6 | ||
}] | ||
], | ||
|
||
outcome: { | ||
|
||
servers: { | ||
|
||
"a:27017": { | ||
|
||
type: "RSPrimary", | ||
setName: "rs" | ||
}, | ||
|
||
"b:27017": { | ||
|
||
type: "Unknown", | ||
setName: | ||
} | ||
}, | ||
topologyType: "ReplicaSetWithPrimary", | ||
logicalSessionTimeoutMinutes: null, | ||
setName: "rs" | ||
} | ||
} | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"description": "Discover ghost with directConnection URI option", | ||
"uri": "mongodb://b/?directConnection=false", | ||
"phases": [ | ||
{ | ||
"responses": [ | ||
[ | ||
"b:27017", | ||
{ | ||
"ok": 1, | ||
"ismaster": false, | ||
"isreplicaset": true, | ||
"minWireVersion": 0, | ||
"maxWireVersion": 6 | ||
} | ||
] | ||
], | ||
"outcome": { | ||
"servers": { | ||
"b:27017": { | ||
"type": "RSGhost", | ||
"setName": null | ||
} | ||
}, | ||
"topologyType": "Unknown", | ||
"logicalSessionTimeoutMinutes": null, | ||
"setName": null | ||
} | ||
} | ||
] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible to have both forms here? I was hoping we wouldn't be introducing more cases where we had to check the upper and lowercase version of URI options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
During testing it came in as both, I think it performs differently when in URL and passed in, this covers all our bases.
Is there a helper I'm missing? It's also in
CASE_TRANSLATION
. 😫There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so case translation comes into play in applyConnectionStringOption which is in turn called by parseQueryString. Your change here moves the call to
parseSrvConnectionString
to afterparseQueryString
is called, which means that we should be in a world where all known values are camelCased.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@reggi I think we're waiting on you here