Skip to content

Commit 910c564

Browse files
authored
feat(NODE-3424): use hello for monitoring commands (#2964)
1 parent 44df7d7 commit 910c564

File tree

412 files changed

+5589
-1057
lines changed

Some content is hidden

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

412 files changed

+5589
-1057
lines changed

.evergreen/install-dependencies.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
set -o errexit # Exit the script with error if any of the commands fail
44

55
NVM_WINDOWS_URL="https://github.com./coreybutler/nvm-windows/releases/download/1.1.7/nvm-noinstall.zip"
6-
NVM_URL="https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh"
6+
NVM_URL="https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh"
77

88
NODE_LTS_NAME=${NODE_LTS_NAME:-carbon}
99
MSVS_VERSION=${MSVS_VERSION:-2017}

lib/cmap/connection_pool.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,13 @@ class ConnectionPool extends EventEmitter {
158158
waitQueueTimeoutMS:
159159
typeof options.waitQueueTimeoutMS === 'number' ? options.waitQueueTimeoutMS : 0,
160160
autoEncrypter: options.autoEncrypter,
161-
metadata: options.metadata
161+
metadata: options.metadata,
162+
useUnifiedTopology: options.useUnifiedTopology
162163
});
163164

164165
if (options.minSize > options.maxSize) {
165166
throw new TypeError(
166-
'Connection pool minimum size must not be greater than maxiumum pool size'
167+
'Connection pool minimum size must not be greater than maximum pool size'
167168
);
168169
}
169170

lib/core/connection/connect.js

+15-4
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ function performInitialHandshake(conn, options, _callback) {
9595
handshakeOptions.socketTimeout = options.connectTimeoutMS || options.connectionTimeout;
9696
}
9797

98+
handshakeDoc.helloOk = !!options.useUnifiedTopology;
99+
98100
const start = new Date().getTime();
99101
conn.command('admin.$cmd', handshakeDoc, handshakeOptions, (err, result) => {
100102
if (err) {
@@ -113,6 +115,10 @@ function performInitialHandshake(conn, options, _callback) {
113115
response.ismaster = response.isWritablePrimary;
114116
}
115117

118+
if (options.useUnifiedTopology && response.helloOk) {
119+
conn.helloOk = true;
120+
}
121+
116122
const supportedServerErr = checkSupportedServer(response, options);
117123
if (supportedServerErr) {
118124
callback(supportedServerErr);
@@ -272,12 +278,17 @@ function makeConnection(family, options, cancellationToken, _callback) {
272278
: typeof options.connectTimeoutMS === 'number'
273279
? options.connectTimeoutMS
274280
: 30000;
275-
const socketTimeout = typeof options.socketTimeout === 'number' ? options.socketTimeout : 0;
281+
const socketTimeoutMS =
282+
typeof options.socketTimeoutMS === 'number'
283+
? options.socketTimeoutMS
284+
: typeof options.socketTimeout === 'number'
285+
? options.socketTimeout
286+
: 0;
276287
const rejectUnauthorized =
277288
typeof options.rejectUnauthorized === 'boolean' ? options.rejectUnauthorized : true;
278289

279-
if (keepAliveInitialDelay > socketTimeout) {
280-
keepAliveInitialDelay = Math.round(socketTimeout / 2);
290+
if (keepAliveInitialDelay > socketTimeoutMS) {
291+
keepAliveInitialDelay = Math.round(socketTimeoutMS / 2);
281292
}
282293

283294
let socket;
@@ -330,7 +341,7 @@ function makeConnection(family, options, cancellationToken, _callback) {
330341
return callback(socket.authorizationError);
331342
}
332343

333-
socket.setTimeout(socketTimeout);
344+
socket.setTimeout(socketTimeoutMS);
334345
callback(null, socket);
335346
}
336347

lib/core/connection/connection.js

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ class Connection extends EventEmitter {
9191
this.bson = options.bson;
9292
this.tag = options.tag;
9393
this.maxBsonMessageSize = options.maxBsonMessageSize || DEFAULT_MAX_BSON_MESSAGE_SIZE;
94+
this.helloOk = undefined;
9495

9596
this.port = options.port || 27017;
9697
this.host = options.host || 'localhost';

lib/core/error.js

+46-46
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
const MONGODB_ERROR_CODES = require('../error_codes').MONGODB_ERROR_CODES;
4+
35
const kErrorLabels = Symbol('errorLabels');
46

57
/**
@@ -216,32 +218,32 @@ class MongoWriteConcernError extends MongoError {
216218

217219
// see: https://github.com./mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst#terms
218220
const RETRYABLE_ERROR_CODES = new Set([
219-
6, // HostUnreachable
220-
7, // HostNotFound
221-
89, // NetworkTimeout
222-
91, // ShutdownInProgress
223-
189, // PrimarySteppedDown
224-
9001, // SocketException
225-
10107, // NotMaster
226-
11600, // InterruptedAtShutdown
227-
11602, // InterruptedDueToReplStateChange
228-
13435, // NotMasterNoSlaveOk
229-
13436 // NotMasterOrSecondary
221+
MONGODB_ERROR_CODES.HostUnreachable,
222+
MONGODB_ERROR_CODES.HostNotFound,
223+
MONGODB_ERROR_CODES.NetworkTimeout,
224+
MONGODB_ERROR_CODES.ShutdownInProgress,
225+
MONGODB_ERROR_CODES.PrimarySteppedDown,
226+
MONGODB_ERROR_CODES.SocketException,
227+
MONGODB_ERROR_CODES.NotMaster,
228+
MONGODB_ERROR_CODES.InterruptedAtShutdown,
229+
MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
230+
MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
231+
MONGODB_ERROR_CODES.NotMasterOrSecondary
230232
]);
231233

232234
const RETRYABLE_WRITE_ERROR_CODES = new Set([
233-
11600, // InterruptedAtShutdown
234-
11602, // InterruptedDueToReplStateChange
235-
10107, // NotMaster
236-
13435, // NotMasterNoSlaveOk
237-
13436, // NotMasterOrSecondary
238-
189, // PrimarySteppedDown
239-
91, // ShutdownInProgress
240-
7, // HostNotFound
241-
6, // HostUnreachable
242-
89, // NetworkTimeout
243-
9001, // SocketException
244-
262 // ExceededTimeLimit
235+
MONGODB_ERROR_CODES.InterruptedAtShutdown,
236+
MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
237+
MONGODB_ERROR_CODES.NotMaster,
238+
MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
239+
MONGODB_ERROR_CODES.NotMasterOrSecondary,
240+
MONGODB_ERROR_CODES.PrimarySteppedDown,
241+
MONGODB_ERROR_CODES.ShutdownInProgress,
242+
MONGODB_ERROR_CODES.HostNotFound,
243+
MONGODB_ERROR_CODES.HostUnreachable,
244+
MONGODB_ERROR_CODES.NetworkTimeout,
245+
MONGODB_ERROR_CODES.SocketException,
246+
MONGODB_ERROR_CODES.ExceededTimeLimit
245247
]);
246248

247249
function isRetryableWriteError(error) {
@@ -271,41 +273,44 @@ function isRetryableError(error) {
271273
}
272274

273275
const SDAM_RECOVERING_CODES = new Set([
274-
91, // ShutdownInProgress
275-
189, // PrimarySteppedDown
276-
11600, // InterruptedAtShutdown
277-
11602, // InterruptedDueToReplStateChange
278-
13436 // NotMasterOrSecondary
276+
MONGODB_ERROR_CODES.ShutdownInProgress,
277+
MONGODB_ERROR_CODES.PrimarySteppedDown,
278+
MONGODB_ERROR_CODES.InterruptedAtShutdown,
279+
MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
280+
MONGODB_ERROR_CODES.NotMasterOrSecondary
279281
]);
280282

281283
const SDAM_NOTMASTER_CODES = new Set([
282-
10107, // NotMaster
283-
13435 // NotMasterNoSlaveOk
284+
MONGODB_ERROR_CODES.NotMaster,
285+
MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
286+
MONGODB_ERROR_CODES.LegacyNotPrimary
284287
]);
285288

286289
const SDAM_NODE_SHUTTING_DOWN_ERROR_CODES = new Set([
287-
11600, // InterruptedAtShutdown
288-
91 // ShutdownInProgress
290+
MONGODB_ERROR_CODES.InterruptedAtShutdown,
291+
MONGODB_ERROR_CODES.ShutdownInProgress
289292
]);
290293

291294
function isRecoveringError(err) {
292-
if (err.code && SDAM_RECOVERING_CODES.has(err.code)) {
293-
return true;
295+
if (typeof err.code === 'number') {
296+
// If any error code exists, we ignore the error.message
297+
return SDAM_RECOVERING_CODES.has(err.code);
294298
}
295299

296-
return err.message.match(/not master or secondary/) || err.message.match(/node is recovering/);
300+
return /not master or secondary/.test(err.message) || /node is recovering/.test(err.message);
297301
}
298302

299303
function isNotMasterError(err) {
300-
if (err.code && SDAM_NOTMASTER_CODES.has(err.code)) {
301-
return true;
304+
if (typeof err.code === 'number') {
305+
// If any error code exists, we ignore the error.message
306+
return SDAM_NOTMASTER_CODES.has(err.code);
302307
}
303308

304309
if (isRecoveringError(err)) {
305310
return false;
306311
}
307312

308-
return err.message.match(/not master/);
313+
return /not master/.test(err.message);
309314
}
310315

311316
function isNodeShuttingDownError(err) {
@@ -316,10 +321,9 @@ function isNodeShuttingDownError(err) {
316321
* Determines whether SDAM can recover from a given error. If it cannot
317322
* then the pool will be cleared, and server state will completely reset
318323
* locally.
319-
*
320-
* @ignore
321324
* @see https://github.com./mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#not-master-and-node-is-recovering
322-
* @param {MongoError|Error} error
325+
* @param {MongoError} error
326+
* @returns {boolean}
323327
*/
324328
function isSDAMUnrecoverableError(error) {
325329
// NOTE: null check is here for a strictly pre-CMAP world, a timeout or
@@ -328,11 +332,7 @@ function isSDAMUnrecoverableError(error) {
328332
return true;
329333
}
330334

331-
if (isRecoveringError(error) || isNotMasterError(error)) {
332-
return true;
333-
}
334-
335-
return false;
335+
return isRecoveringError(error) || isNotMasterError(error);
336336
}
337337

338338
module.exports = {

lib/core/sdam/monitor.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ class Monitor extends EventEmitter {
6565
heartbeatFrequencyMS:
6666
typeof options.heartbeatFrequencyMS === 'number' ? options.heartbeatFrequencyMS : 10000,
6767
minHeartbeatFrequencyMS:
68-
typeof options.minHeartbeatFrequencyMS === 'number' ? options.minHeartbeatFrequencyMS : 500
68+
typeof options.minHeartbeatFrequencyMS === 'number' ? options.minHeartbeatFrequencyMS : 500,
69+
useUnifiedTopology: options.useUnifiedTopology
6970
});
7071

7172
// TODO: refactor this to pull it directly from the pool, requires new ConnectionPool integration
@@ -205,8 +206,15 @@ function checkServer(monitor, callback) {
205206
const topologyVersion = monitor[kServer].description.topologyVersion;
206207
const isAwaitable = topologyVersion != null;
207208
const serverApi = monitor[kConnection].serverApi;
209+
const helloOk = monitor[kConnection].helloOk;
210+
211+
const cmd = {
212+
[serverApi || helloOk ? 'hello' : 'ismaster']: true
213+
};
214+
215+
// written this way omit helloOk from the command if its false-y (do not want -> helloOk: null)
216+
if (helloOk) cmd.helloOk = helloOk;
208217

209-
const cmd = { [serverApi ? 'hello' : 'ismaster']: true };
210218
const options = { socketTimeout: connectTimeoutMS };
211219

212220
if (isAwaitable) {

lib/core/sdam/server_description.js

+4
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ class ServerDescription {
7070
ismaster
7171
);
7272

73+
if (ismaster.isWritablePrimary != null) {
74+
ismaster.ismaster = ismaster.isWritablePrimary;
75+
}
76+
7377
this.address = address;
7478
this.error = options.error;
7579
this.roundTripTime = options.roundTripTime || -1;

lib/core/topologies/replset_state.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ var ReplSetState = function(options) {
3434
// Add event listener
3535
EventEmitter.call(this);
3636
// Topology state
37-
this.topologyType = TopologyType.ReplicaSetNoPrimary;
37+
this.topologyType = options.setName ? TopologyType.ReplicaSetNoPrimary : TopologyType.Unknown;
3838
this.setName = options.setName;
3939

4040
// Server set
@@ -218,7 +218,8 @@ const isArbiter = ismaster => ismaster.arbiterOnly && ismaster.setName;
218218
ReplSetState.prototype.update = function(server) {
219219
var self = this;
220220
// Get the current ismaster
221-
var ismaster = server.lastIsMaster();
221+
const ismaster = server.lastIsMaster();
222+
if (ismaster && ismaster.isWritablePrimary) ismaster.ismaster = ismaster.isWritablePrimary;
222223

223224
// Get the server name and lowerCase it
224225
var serverName = server.name.toLowerCase();
@@ -358,7 +359,8 @@ ReplSetState.prototype.update = function(server) {
358359
// Standalone server, destroy and return
359360
//
360361
if (ismaster && ismaster.ismaster && !ismaster.setName) {
361-
this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.Unknown;
362+
// We should not mark the topology as Unknown because of one standalone
363+
// we should just remove this server from the set
362364
this.remove(server, { force: true });
363365
return false;
364366
}

lib/core/topologies/shared.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
'use strict';
2+
3+
const MONGODB_ERROR_CODES = require('../../error_codes').MONGODB_ERROR_CODES;
24
const ReadPreference = require('./read_preference');
35
const TopologyType = require('../sdam/common').TopologyType;
46
const MongoError = require('../error').MongoError;
57
const isRetryableWriteError = require('../error').isRetryableWriteError;
68
const maxWireVersion = require('../utils').maxWireVersion;
79
const MongoNetworkError = require('../error').MongoNetworkError;
8-
const MMAPv1_RETRY_WRITES_ERROR_CODE = 20;
10+
11+
const MMAPv1_RETRY_WRITES_ERROR_CODE = MONGODB_ERROR_CODES.IllegalOperation;
912

1013
/**
1114
* Emit event if it exists

lib/core/uri_parser.js

+9
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ function parseSrvConnectionString(uri, options, callback) {
106106
}
107107

108108
record = qs.parse(record[0].join(''));
109+
110+
if (Object.keys(record).some(k => k.toLowerCase() === 'loadbalanced')) {
111+
return callback(new MongoParseError('Load balancer mode requires driver version 4+'));
112+
}
113+
109114
if (Object.keys(record).some(key => key !== 'authSource' && key !== 'replicaSet')) {
110115
return callback(
111116
new MongoParseError('Text record must only set `authSource` or `replicaSet`')
@@ -598,6 +603,10 @@ function parseConnectionString(uri, options, callback) {
598603

599604
parsedOptions = Object.assign({}, parsedOptions, options);
600605

606+
if (Object.keys(parsedOptions).some(k => k.toLowerCase() === 'loadbalanced')) {
607+
return callback(new MongoParseError('Load balancer mode requires driver version 4+'));
608+
}
609+
601610
if (protocol === PROTOCOL_MONGODB_SRV) {
602611
return parseSrvConnectionString(uri, parsedOptions, callback);
603612
}

0 commit comments

Comments
 (0)