Skip to content
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(NODE-3424): use hello for monitoring commands #2964

Merged
merged 14 commits into from
Sep 13, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .evergreen/install-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
set -o errexit # Exit the script with error if any of the commands fail

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

NODE_LTS_NAME=${NODE_LTS_NAME:-carbon}
MSVS_VERSION=${MSVS_VERSION:-2017}
Expand Down
5 changes: 3 additions & 2 deletions lib/cmap/connection_pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,13 @@ class ConnectionPool extends EventEmitter {
waitQueueTimeoutMS:
typeof options.waitQueueTimeoutMS === 'number' ? options.waitQueueTimeoutMS : 0,
autoEncrypter: options.autoEncrypter,
metadata: options.metadata
metadata: options.metadata,
useUnifiedTopology: options.useUnifiedTopology
});

if (options.minSize > options.maxSize) {
throw new TypeError(
'Connection pool minimum size must not be greater than maxiumum pool size'
'Connection pool minimum size must not be greater than maximum pool size'
);
}

Expand Down
6 changes: 6 additions & 0 deletions lib/core/connection/connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ function performInitialHandshake(conn, options, _callback) {
handshakeOptions.socketTimeout = options.connectTimeoutMS || options.connectionTimeout;
}

handshakeDoc.helloOk = !!options.useUnifiedTopology;

const start = new Date().getTime();
conn.command('admin.$cmd', handshakeDoc, handshakeOptions, (err, result) => {
if (err) {
Expand All @@ -113,6 +115,10 @@ function performInitialHandshake(conn, options, _callback) {
response.ismaster = response.isWritablePrimary;
}

if (options.useUnifiedTopology && response.helloOk) {
conn.helloOk = true;
}

const supportedServerErr = checkSupportedServer(response, options);
if (supportedServerErr) {
callback(supportedServerErr);
Expand Down
1 change: 1 addition & 0 deletions lib/core/connection/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class Connection extends EventEmitter {
this.bson = options.bson;
this.tag = options.tag;
this.maxBsonMessageSize = options.maxBsonMessageSize || DEFAULT_MAX_BSON_MESSAGE_SIZE;
this.helloOk = undefined;

this.port = options.port || 27017;
this.host = options.host || 'localhost';
Expand Down
85 changes: 47 additions & 38 deletions lib/core/error.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

const MONGODB_ERROR_CODES = require('../error_codes').MONGODB_ERROR_CODES;

const kErrorLabels = Symbol('errorLabels');

/**
Expand Down Expand Up @@ -216,32 +218,32 @@ class MongoWriteConcernError extends MongoError {

// see: https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst#terms
const RETRYABLE_ERROR_CODES = new Set([
6, // HostUnreachable
7, // HostNotFound
89, // NetworkTimeout
91, // ShutdownInProgress
189, // PrimarySteppedDown
9001, // SocketException
10107, // NotMaster
11600, // InterruptedAtShutdown
11602, // InterruptedDueToReplStateChange
13435, // NotMasterNoSlaveOk
13436 // NotMasterOrSecondary
MONGODB_ERROR_CODES.HostUnreachable,
MONGODB_ERROR_CODES.HostNotFound,
MONGODB_ERROR_CODES.NetworkTimeout,
MONGODB_ERROR_CODES.ShutdownInProgress,
MONGODB_ERROR_CODES.PrimarySteppedDown,
MONGODB_ERROR_CODES.SocketException,
MONGODB_ERROR_CODES.NotMaster,
MONGODB_ERROR_CODES.InterruptedAtShutdown,
MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
MONGODB_ERROR_CODES.NotMasterOrSecondary
]);

const RETRYABLE_WRITE_ERROR_CODES = new Set([
11600, // InterruptedAtShutdown
11602, // InterruptedDueToReplStateChange
10107, // NotMaster
13435, // NotMasterNoSlaveOk
13436, // NotMasterOrSecondary
189, // PrimarySteppedDown
91, // ShutdownInProgress
7, // HostNotFound
6, // HostUnreachable
89, // NetworkTimeout
9001, // SocketException
262 // ExceededTimeLimit
MONGODB_ERROR_CODES.InterruptedAtShutdown,
MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
MONGODB_ERROR_CODES.NotMaster,
MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
MONGODB_ERROR_CODES.NotMasterOrSecondary,
MONGODB_ERROR_CODES.PrimarySteppedDown,
MONGODB_ERROR_CODES.ShutdownInProgress,
MONGODB_ERROR_CODES.HostNotFound,
MONGODB_ERROR_CODES.HostUnreachable,
MONGODB_ERROR_CODES.NetworkTimeout,
MONGODB_ERROR_CODES.SocketException,
MONGODB_ERROR_CODES.ExceededTimeLimit
]);

function isRetryableWriteError(error) {
Expand Down Expand Up @@ -271,41 +273,44 @@ function isRetryableError(error) {
}

const SDAM_RECOVERING_CODES = new Set([
91, // ShutdownInProgress
189, // PrimarySteppedDown
11600, // InterruptedAtShutdown
11602, // InterruptedDueToReplStateChange
13436 // NotMasterOrSecondary
MONGODB_ERROR_CODES.ShutdownInProgress,
MONGODB_ERROR_CODES.PrimarySteppedDown,
MONGODB_ERROR_CODES.InterruptedAtShutdown,
MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
MONGODB_ERROR_CODES.NotMasterOrSecondary
]);

const SDAM_NOTMASTER_CODES = new Set([
10107, // NotMaster
13435 // NotMasterNoSlaveOk
MONGODB_ERROR_CODES.NotMaster,
MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
MONGODB_ERROR_CODES.LegacyNotPrimary
]);

const SDAM_NODE_SHUTTING_DOWN_ERROR_CODES = new Set([
11600, // InterruptedAtShutdown
91 // ShutdownInProgress
MONGODB_ERROR_CODES.InterruptedAtShutdown,
MONGODB_ERROR_CODES.ShutdownInProgress
]);

function isRecoveringError(err) {
if (err.code && SDAM_RECOVERING_CODES.has(err.code)) {
return true;
if (typeof err.code !== 'undefined') {
// If any error code exists, we ignore the error.message
return SDAM_RECOVERING_CODES.has(err.code);
}

return err.message.match(/not master or secondary/) || err.message.match(/node is recovering/);
return /not master or secondary/.test(err.message) || /node is recovering/.test(err.message);
}

function isNotMasterError(err) {
if (err.code && SDAM_NOTMASTER_CODES.has(err.code)) {
return true;
if (typeof err.code !== 'undefined') {
// If any error code exists, we ignore the error.message
return SDAM_NOTMASTER_CODES.has(err.code);
}

if (isRecoveringError(err)) {
return false;
}

return err.message.match(/not master/);
return /not master/.test(err.message);
}

function isNodeShuttingDownError(err) {
Expand All @@ -328,6 +333,10 @@ function isSDAMUnrecoverableError(error) {
return true;
}

if (typeof error.code !== 'undefined') {
dariakp marked this conversation as resolved.
Show resolved Hide resolved
return isRecoveringError(error) || isNotMasterError(error);
}

if (isRecoveringError(error) || isNotMasterError(error)) {
return true;
}
Expand Down
12 changes: 10 additions & 2 deletions lib/core/sdam/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ class Monitor extends EventEmitter {
heartbeatFrequencyMS:
typeof options.heartbeatFrequencyMS === 'number' ? options.heartbeatFrequencyMS : 10000,
minHeartbeatFrequencyMS:
typeof options.minHeartbeatFrequencyMS === 'number' ? options.minHeartbeatFrequencyMS : 500
typeof options.minHeartbeatFrequencyMS === 'number' ? options.minHeartbeatFrequencyMS : 500,
useUnifiedTopology: options.useUnifiedTopology
});

// TODO: refactor this to pull it directly from the pool, requires new ConnectionPool integration
Expand Down Expand Up @@ -205,8 +206,15 @@ function checkServer(monitor, callback) {
const topologyVersion = monitor[kServer].description.topologyVersion;
const isAwaitable = topologyVersion != null;
const serverApi = monitor[kConnection].serverApi;
const helloOk = monitor[kConnection].helloOk;

const cmd = {
[serverApi || helloOk ? 'hello' : 'ismaster']: true
};

// written this way omit helloOk from the command if its false-y (do not want -> helloOk: null)
if (helloOk) cmd.helloOk = helloOk;

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

if (isAwaitable) {
Expand Down
2 changes: 1 addition & 1 deletion lib/core/sdam/server_description.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ function parseServerType(ismaster) {
if (ismaster.setName) {
if (ismaster.hidden) {
return ServerType.RSOther;
} else if (ismaster.ismaster) {
} else if (ismaster.ismaster || ismaster.isWritablePrimary) {
dariakp marked this conversation as resolved.
Show resolved Hide resolved
return ServerType.RSPrimary;
} else if (ismaster.secondary) {
return ServerType.RSSecondary;
Expand Down
8 changes: 5 additions & 3 deletions lib/core/topologies/replset_state.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var ReplSetState = function(options) {
// Add event listener
EventEmitter.call(this);
// Topology state
this.topologyType = TopologyType.ReplicaSetNoPrimary;
this.topologyType = options.setName ? TopologyType.ReplicaSetNoPrimary : TopologyType.Unknown;
this.setName = options.setName;

// Server set
Expand Down Expand Up @@ -218,7 +218,8 @@ const isArbiter = ismaster => ismaster.arbiterOnly && ismaster.setName;
ReplSetState.prototype.update = function(server) {
var self = this;
// Get the current ismaster
var ismaster = server.lastIsMaster();
const ismaster = server.lastIsMaster();
if (ismaster && ismaster.isWritablePrimary) ismaster.ismaster = ismaster.isWritablePrimary;

// Get the server name and lowerCase it
var serverName = server.name.toLowerCase();
Expand Down Expand Up @@ -358,7 +359,8 @@ ReplSetState.prototype.update = function(server) {
// Standalone server, destroy and return
//
if (ismaster && ismaster.ismaster && !ismaster.setName) {
this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.Unknown;
// We should not mark the topology as Unknown because of one standalone
dariakp marked this conversation as resolved.
Show resolved Hide resolved
// we should just remove this server from the set
this.remove(server, { force: true });
return false;
}
Expand Down
5 changes: 4 additions & 1 deletion lib/core/topologies/shared.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
'use strict';

const MONGODB_ERROR_CODES = require('../../error_codes').MONGODB_ERROR_CODES;
const ReadPreference = require('./read_preference');
const TopologyType = require('../sdam/common').TopologyType;
const MongoError = require('../error').MongoError;
const isRetryableWriteError = require('../error').isRetryableWriteError;
const maxWireVersion = require('../utils').maxWireVersion;
const MongoNetworkError = require('../error').MongoNetworkError;
const MMAPv1_RETRY_WRITES_ERROR_CODE = 20;

const MMAPv1_RETRY_WRITES_ERROR_CODE = MONGODB_ERROR_CODES.IllegalOperation;

/**
* Emit event if it exists
Expand Down
17 changes: 17 additions & 0 deletions lib/core/uri_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ function parseSrvConnectionString(uri, options, callback) {
}

record = qs.parse(record[0].join(''));

if (
Object.keys(record)
dariakp marked this conversation as resolved.
Show resolved Hide resolved
.map(k => k.toLowerCase())
.indexOf('loadbalanced') !== -1
) {
return callback(new MongoParseError('Load balancer mode requires driver version 4+'));
}

if (Object.keys(record).some(key => key !== 'authSource' && key !== 'replicaSet')) {
return callback(
new MongoParseError('Text record must only set `authSource` or `replicaSet`')
Expand Down Expand Up @@ -598,6 +607,14 @@ function parseConnectionString(uri, options, callback) {

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

if (
Object.keys(parsedOptions)
.map(k => k.toLowerCase())
.indexOf('loadbalanced') !== -1
) {
return callback(new MongoParseError('Load balancer mode requires driver version 4+'));
}

if (protocol === PROTOCOL_MONGODB_SRV) {
return parseSrvConnectionString(uri, parsedOptions, callback);
}
Expand Down
41 changes: 21 additions & 20 deletions lib/error.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
'use strict';

const MongoNetworkError = require('./core').MongoNetworkError;
const MONGODB_ERROR_CODES = require('./error_codes').MONGODB_ERROR_CODES;

// From spec@https://github.com/mongodb/specifications/blob/f93d78191f3db2898a59013a7ed5650352ef6da8/source/change-streams/change-streams.rst#resumable-error
const GET_MORE_RESUMABLE_CODES = new Set([
6, // HostUnreachable
7, // HostNotFound
89, // NetworkTimeout
91, // ShutdownInProgress
189, // PrimarySteppedDown
262, // ExceededTimeLimit
9001, // SocketException
10107, // NotMaster
11600, // InterruptedAtShutdown
11602, // InterruptedDueToReplStateChange
13435, // NotMasterNoSlaveOk
13436, // NotMasterOrSecondary
63, // StaleShardVersion
150, // StaleEpoch
13388, // StaleConfig
234, // RetryChangeStream
133, // FailedToSatisfyReadPreference
43 // CursorNotFound
MONGODB_ERROR_CODES.HostUnreachable,
MONGODB_ERROR_CODES.HostNotFound,
MONGODB_ERROR_CODES.NetworkTimeout,
MONGODB_ERROR_CODES.ShutdownInProgress,
MONGODB_ERROR_CODES.PrimarySteppedDown,
MONGODB_ERROR_CODES.ExceededTimeLimit,
MONGODB_ERROR_CODES.SocketException,
MONGODB_ERROR_CODES.NotMaster,
MONGODB_ERROR_CODES.InterruptedAtShutdown,
MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
MONGODB_ERROR_CODES.NotMasterOrSecondary,
MONGODB_ERROR_CODES.StaleShardVersion,
MONGODB_ERROR_CODES.StaleEpoch,
MONGODB_ERROR_CODES.StaleConfig,
MONGODB_ERROR_CODES.RetryChangeStream,
MONGODB_ERROR_CODES.FailedToSatisfyReadPreference,
MONGODB_ERROR_CODES.CursorNotFound
]);

function isResumableError(error, wireVersion) {
Expand All @@ -31,7 +32,7 @@ function isResumableError(error, wireVersion) {

if (wireVersion >= 9) {
// DRIVERS-1308: For 4.4 drivers running against 4.4 servers, drivers will add a special case to treat the CursorNotFound error code as resumable
if (error.code === 43) {
if (error.code === MONGODB_ERROR_CODES.CursorNotFound) {
return true;
}
return error.hasErrorLabel('ResumableChangeStreamError');
Expand All @@ -40,4 +41,4 @@ function isResumableError(error, wireVersion) {
return GET_MORE_RESUMABLE_CODES.has(error.code);
}

module.exports = { GET_MORE_RESUMABLE_CODES, isResumableError };
module.exports = { GET_MORE_RESUMABLE_CODES, isResumableError, MONGODB_ERROR_CODES };
36 changes: 36 additions & 0 deletions lib/error_codes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict';

const MONGODB_ERROR_CODES = Object.freeze({
HostUnreachable: 6,
HostNotFound: 7,
NetworkTimeout: 89,
ShutdownInProgress: 91,
PrimarySteppedDown: 189,
ExceededTimeLimit: 262,
SocketException: 9001,
NotMaster: 10107,
InterruptedAtShutdown: 11600,
InterruptedDueToReplStateChange: 11602,
NotMasterNoSlaveOk: 13435,
NotMasterOrSecondary: 13436,
StaleShardVersion: 63,
StaleEpoch: 150,
StaleConfig: 13388,
RetryChangeStream: 234,
FailedToSatisfyReadPreference: 133,
CursorNotFound: 43,
LegacyNotPrimary: 10058,
WriteConcernFailed: 64,
NamespaceNotFound: 26,
IllegalOperation: 20,
MaxTimeMSExpired: 50,
UnknownReplWriteConcern: 79,
UnsatisfiableWriteConcern: 100,
DuplicateKey: 11000,
CannotCreateIndex: 67,
IndexOptionsConflict: 85,
IndexKeySpecsConflict: 86,
InvalidIndexSpecificationOption: 197
});

module.exports = Object.freeze({ MONGODB_ERROR_CODES });
Loading