mongodb 3.6.11 → 3.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/index.js +4 -0
  2. package/lib/cmap/connection.js +34 -7
  3. package/lib/cmap/connection_pool.js +4 -2
  4. package/lib/collection.js +1 -1
  5. package/lib/core/auth/mongo_credentials.js +4 -1
  6. package/lib/core/auth/mongodb_aws.js +16 -15
  7. package/lib/core/connection/connect.js +22 -5
  8. package/lib/core/connection/connection.js +2 -0
  9. package/lib/core/connection/pool.js +1 -0
  10. package/lib/core/connection/utils.js +35 -2
  11. package/lib/core/error.js +46 -46
  12. package/lib/core/index.js +9 -0
  13. package/lib/core/sdam/monitor.js +16 -2
  14. package/lib/core/sdam/server.js +2 -0
  15. package/lib/core/sdam/server_description.js +4 -0
  16. package/lib/core/sdam/topology.js +10 -2
  17. package/lib/core/sessions.js +11 -8
  18. package/lib/core/topologies/replset_state.js +5 -3
  19. package/lib/core/topologies/shared.js +4 -1
  20. package/lib/core/transactions.js +5 -1
  21. package/lib/core/uri_parser.js +14 -0
  22. package/lib/core/wireprotocol/command.js +24 -0
  23. package/lib/core/wireprotocol/kill_cursors.js +7 -2
  24. package/lib/core/wireprotocol/query.js +9 -5
  25. package/lib/cursor.js +4 -0
  26. package/lib/db.js +1 -0
  27. package/lib/error.js +21 -20
  28. package/lib/error_codes.js +36 -0
  29. package/lib/explain.js +5 -12
  30. package/lib/gridfs-stream/index.js +39 -24
  31. package/lib/gridfs-stream/upload.js +53 -46
  32. package/lib/mongo_client.js +30 -5
  33. package/lib/operations/connect.js +1 -0
  34. package/lib/operations/db_ops.js +10 -7
  35. package/lib/operations/estimated_document_count.js +47 -18
  36. package/lib/topologies/native_topology.js +4 -0
  37. package/lib/url_parser.js +8 -0
  38. package/lib/utils.js +10 -1
  39. package/package.json +3 -3
  40. package/HISTORY.md +0 -2908
package/index.js CHANGED
@@ -17,6 +17,9 @@ connect.MongoWriteConcernError = core.MongoWriteConcernError;
17
17
  connect.MongoBulkWriteError = require('./lib/bulk/common').BulkWriteError;
18
18
  connect.BulkWriteError = connect.MongoBulkWriteError;
19
19
 
20
+ // Expose server versions
21
+ connect.ServerApiVersion = core.ServerApiVersion;
22
+
20
23
  // Actual driver classes exported
21
24
  connect.Admin = require('./lib/admin');
22
25
  connect.MongoClient = require('./lib/mongo_client');
@@ -47,6 +50,7 @@ connect.Int32 = core.BSON.Int32;
47
50
  connect.Long = core.BSON.Long;
48
51
  connect.MinKey = core.BSON.MinKey;
49
52
  connect.MaxKey = core.BSON.MaxKey;
53
+ /** @deprecated Please use `ObjectId` */
50
54
  connect.ObjectID = core.BSON.ObjectID;
51
55
  connect.ObjectId = core.BSON.ObjectID;
52
56
  connect.Symbol = core.BSON.Symbol;
@@ -37,6 +37,8 @@ class Connection extends EventEmitter {
37
37
  this.port = options.port || 27017;
38
38
  this.monitorCommands =
39
39
  typeof options.monitorCommands === 'boolean' ? options.monitorCommands : false;
40
+ this.serverApi = options.serverApi;
41
+
40
42
  this.closed = false;
41
43
  this.destroyed = false;
42
44
 
@@ -170,33 +172,58 @@ class Connection extends EventEmitter {
170
172
  });
171
173
  }
172
174
 
175
+ applyApiVersion(options) {
176
+ if (this.serverApi) {
177
+ options.serverApi = this.serverApi;
178
+ }
179
+ return options;
180
+ }
181
+
173
182
  // Wire protocol methods
174
183
  command(ns, cmd, options, callback) {
175
- wp.command(makeServerTrampoline(this), ns, cmd, options, callback);
184
+ if (typeof options === 'function') {
185
+ callback = options;
186
+ options = {};
187
+ }
188
+ wp.command(makeServerTrampoline(this), ns, cmd, this.applyApiVersion(options), callback);
176
189
  }
177
190
 
178
191
  query(ns, cmd, cursorState, options, callback) {
179
- wp.query(makeServerTrampoline(this), ns, cmd, cursorState, options, callback);
192
+ wp.query(
193
+ makeServerTrampoline(this),
194
+ ns,
195
+ cmd,
196
+ cursorState,
197
+ this.applyApiVersion(options),
198
+ callback
199
+ );
180
200
  }
181
201
 
182
202
  getMore(ns, cursorState, batchSize, options, callback) {
183
- wp.getMore(makeServerTrampoline(this), ns, cursorState, batchSize, options, callback);
203
+ wp.getMore(
204
+ makeServerTrampoline(this),
205
+ ns,
206
+ cursorState,
207
+ batchSize,
208
+ this.applyApiVersion(options),
209
+ callback
210
+ );
184
211
  }
185
212
 
186
213
  killCursors(ns, cursorState, callback) {
187
- wp.killCursors(makeServerTrampoline(this), ns, cursorState, callback);
214
+ wp.killCursors(makeServerTrampoline(this), ns, cursorState, this.applyApiVersion({}), callback);
188
215
  }
189
216
 
190
217
  insert(ns, ops, options, callback) {
191
- wp.insert(makeServerTrampoline(this), ns, ops, options, callback);
218
+ wp.insert(makeServerTrampoline(this), ns, ops, this.applyApiVersion(options), callback);
192
219
  }
193
220
 
194
221
  update(ns, ops, options, callback) {
195
- wp.update(makeServerTrampoline(this), ns, ops, options, callback);
222
+ wp.update(makeServerTrampoline(this), ns, ops, this.applyApiVersion(options), callback);
196
223
  }
197
224
 
198
225
  remove(ns, ops, options, callback) {
199
- wp.remove(makeServerTrampoline(this), ns, ops, options, callback);
226
+ wp.remove(makeServerTrampoline(this), ns, ops, this.applyApiVersion(options), callback);
200
227
  }
201
228
  }
202
229
 
@@ -41,6 +41,7 @@ const VALID_POOL_OPTIONS = new Set([
41
41
  'ssl',
42
42
  'bson',
43
43
  'connectionType',
44
+ 'serverApi',
44
45
  'monitorCommands',
45
46
  'socketTimeout',
46
47
  'credentials',
@@ -157,12 +158,13 @@ class ConnectionPool extends EventEmitter {
157
158
  waitQueueTimeoutMS:
158
159
  typeof options.waitQueueTimeoutMS === 'number' ? options.waitQueueTimeoutMS : 0,
159
160
  autoEncrypter: options.autoEncrypter,
160
- metadata: options.metadata
161
+ metadata: options.metadata,
162
+ useUnifiedTopology: options.useUnifiedTopology
161
163
  });
162
164
 
163
165
  if (options.minSize > options.maxSize) {
164
166
  throw new TypeError(
165
- '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'
166
168
  );
167
169
  }
168
170
 
package/lib/collection.js CHANGED
@@ -1532,7 +1532,7 @@ Collection.prototype.count = deprecate(function(query, options, callback) {
1532
1532
 
1533
1533
  return executeOperation(
1534
1534
  this.s.topology,
1535
- new EstimatedDocumentCountOperation(this, query, options),
1535
+ new EstimatedDocumentCountOperation(this, Object.assign({ query }, options)),
1536
1536
  callback
1537
1537
  );
1538
1538
  }, 'collection.count is deprecated, and will be removed in a future version.' +
@@ -58,7 +58,10 @@ class MongoCredentials {
58
58
  this.password = process.env.AWS_SECRET_ACCESS_KEY;
59
59
  }
60
60
 
61
- if (!this.mechanismProperties.AWS_SESSION_TOKEN && process.env.AWS_SESSION_TOKEN) {
61
+ if (
62
+ this.mechanismProperties.AWS_SESSION_TOKEN == null &&
63
+ process.env.AWS_SESSION_TOKEN != null
64
+ ) {
62
65
  this.mechanismProperties.AWS_SESSION_TOKEN = process.env.AWS_SESSION_TOKEN;
63
66
  }
64
67
  }
@@ -51,12 +51,21 @@ class MongoDBAWS extends AuthProvider {
51
51
  return;
52
52
  }
53
53
 
54
- const username = credentials.username;
55
- const password = credentials.password;
56
54
  const db = credentials.source;
57
- const token = credentials.mechanismProperties.AWS_SESSION_TOKEN;
58
55
  const bson = this.bson;
59
56
 
57
+ const accessKeyId = credentials.username;
58
+ const secretAccessKey = credentials.password;
59
+ const sessionToken = credentials.mechanismProperties.AWS_SESSION_TOKEN;
60
+
61
+ // If all three defined, include sessionToken, else include username and pass, else no credentials
62
+ const awsCredentials =
63
+ accessKeyId && secretAccessKey && sessionToken
64
+ ? { accessKeyId, secretAccessKey, sessionToken }
65
+ : accessKeyId && secretAccessKey
66
+ ? { accessKeyId, secretAccessKey }
67
+ : undefined;
68
+
60
69
  crypto.randomBytes(32, (err, nonce) => {
61
70
  if (err) {
62
71
  callback(err);
@@ -109,18 +118,14 @@ class MongoDBAWS extends AuthProvider {
109
118
  path: '/',
110
119
  body
111
120
  },
112
- {
113
- accessKeyId: username,
114
- secretAccessKey: password,
115
- token
116
- }
121
+ awsCredentials
117
122
  );
118
123
 
119
124
  const authorization = options.headers.Authorization;
120
125
  const date = options.headers['X-Amz-Date'];
121
126
  const payload = { a: authorization, d: date };
122
- if (token) {
123
- payload.t = token;
127
+ if (sessionToken) {
128
+ payload.t = sessionToken;
124
129
  }
125
130
 
126
131
  const saslContinue = {
@@ -164,6 +169,7 @@ function makeTempCredentials(credentials, callback) {
164
169
  if (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {
165
170
  request(
166
171
  `${AWS_RELATIVE_URI}${process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}`,
172
+ undefined,
167
173
  (err, res) => {
168
174
  if (err) return callback(err);
169
175
  done(res);
@@ -215,11 +221,6 @@ function deriveRegion(host) {
215
221
  }
216
222
 
217
223
  function request(uri, options, callback) {
218
- if (typeof options === 'function') {
219
- callback = options;
220
- options = {};
221
- }
222
-
223
224
  options = Object.assign(
224
225
  {
225
226
  method: 'GET',
@@ -95,6 +95,8 @@ function performInitialHandshake(conn, options, _callback) {
95
95
  handshakeOptions.socketTimeout = options.connectTimeoutMS || options.connectionTimeout;
96
96
  }
97
97
 
98
+ handshakeDoc.helloOk = !!options.useUnifiedTopology;
99
+
98
100
  const start = new Date().getTime();
99
101
  conn.command('admin.$cmd', handshakeDoc, handshakeOptions, (err, result) => {
100
102
  if (err) {
@@ -108,6 +110,15 @@ function performInitialHandshake(conn, options, _callback) {
108
110
  return;
109
111
  }
110
112
 
113
+ if ('isWritablePrimary' in response) {
114
+ // Provide pre-hello-style response document.
115
+ response.ismaster = response.isWritablePrimary;
116
+ }
117
+
118
+ if (options.useUnifiedTopology && response.helloOk) {
119
+ conn.helloOk = true;
120
+ }
121
+
111
122
  const supportedServerErr = checkSupportedServer(response, options);
112
123
  if (supportedServerErr) {
113
124
  callback(supportedServerErr);
@@ -158,11 +169,12 @@ function performInitialHandshake(conn, options, _callback) {
158
169
 
159
170
  function prepareHandshakeDocument(authContext, callback) {
160
171
  const options = authContext.options;
172
+ const serverApi = authContext.connection.serverApi;
161
173
  const compressors =
162
174
  options.compression && options.compression.compressors ? options.compression.compressors : [];
163
175
 
164
176
  const handshakeDoc = {
165
- ismaster: true,
177
+ [serverApi ? 'hello' : 'ismaster']: true,
166
178
  client: options.metadata || makeClientMetadata(options),
167
179
  compression: compressors
168
180
  };
@@ -266,12 +278,17 @@ function makeConnection(family, options, cancellationToken, _callback) {
266
278
  : typeof options.connectTimeoutMS === 'number'
267
279
  ? options.connectTimeoutMS
268
280
  : 30000;
269
- 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;
270
287
  const rejectUnauthorized =
271
288
  typeof options.rejectUnauthorized === 'boolean' ? options.rejectUnauthorized : true;
272
289
 
273
- if (keepAliveInitialDelay > socketTimeout) {
274
- keepAliveInitialDelay = Math.round(socketTimeout / 2);
290
+ if (keepAliveInitialDelay > socketTimeoutMS) {
291
+ keepAliveInitialDelay = Math.round(socketTimeoutMS / 2);
275
292
  }
276
293
 
277
294
  let socket;
@@ -324,7 +341,7 @@ function makeConnection(family, options, cancellationToken, _callback) {
324
341
  return callback(socket.authorizationError);
325
342
  }
326
343
 
327
- socket.setTimeout(socketTimeout);
344
+ socket.setTimeout(socketTimeoutMS);
328
345
  callback(null, socket);
329
346
  }
330
347
 
@@ -91,6 +91,7 @@ class Connection extends EventEmitter {
91
91
  this.bson = options.bson;
92
92
  this.tag = options.tag;
93
93
  this.maxBsonMessageSize = options.maxBsonMessageSize || DEFAULT_MAX_BSON_MESSAGE_SIZE;
94
+ this.helloOk = undefined;
94
95
 
95
96
  this.port = options.port || 27017;
96
97
  this.host = options.host || 'localhost';
@@ -190,6 +191,7 @@ class Connection extends EventEmitter {
190
191
  * Unref this connection
191
192
  * @method
192
193
  * @return {boolean}
194
+ * @deprecated This function is deprecated and will be removed in the next major version.
193
195
  */
194
196
  unref() {
195
197
  if (this.socket == null) {
@@ -604,6 +604,7 @@ Pool.prototype.logout = function(dbName, callback) {
604
604
  /**
605
605
  * Unref the pool
606
606
  * @method
607
+ * @deprecated This function is deprecated and will be removed in the next major version.
607
608
  */
608
609
  Pool.prototype.unref = function() {
609
610
  // Get all the known connections
@@ -1,5 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ const parsePackageVersion = require('../../utils').parsePackageVersion;
4
+ const MongoError = require('../error').MongoError;
5
+
3
6
  const require_optional = require('optional-require')(require);
4
7
 
5
8
  function debugOptions(debugFields, options) {
@@ -16,7 +19,15 @@ function retrieveBSON() {
16
19
  BSON.native = false;
17
20
 
18
21
  const optionalBSON = require_optional('bson-ext');
22
+ const bsonExtVersion = parsePackageVersion(
23
+ require_optional('bson-ext/package.json') || { version: '0.0.0' }
24
+ );
19
25
  if (optionalBSON) {
26
+ if (bsonExtVersion.major >= 4) {
27
+ throw new MongoError(
28
+ 'bson-ext version 4 and above does not work with the 3.x version of the mongodb driver'
29
+ );
30
+ }
20
31
  optionalBSON.native = true;
21
32
  return optionalBSON;
22
33
  }
@@ -31,21 +42,43 @@ function noSnappyWarning() {
31
42
  );
32
43
  }
33
44
 
45
+ const PKG_VERSION = Symbol('kPkgVersion');
46
+
34
47
  // Facilitate loading Snappy optionally
35
48
  function retrieveSnappy() {
36
- let snappy = require_optional('snappy');
49
+ const snappy = require_optional('snappy');
37
50
  if (!snappy) {
38
- snappy = {
51
+ return {
39
52
  compress: noSnappyWarning,
40
53
  uncompress: noSnappyWarning,
41
54
  compressSync: noSnappyWarning,
42
55
  uncompressSync: noSnappyWarning
43
56
  };
44
57
  }
58
+
59
+ const snappyPkg = require_optional('snappy/package.json') || { version: '0.0.0' };
60
+ const version = parsePackageVersion(snappyPkg);
61
+ snappy[PKG_VERSION] = version;
62
+ if (version.major >= 7) {
63
+ const compressOriginal = snappy.compress;
64
+ const uncompressOriginal = snappy.uncompress;
65
+ snappy.compress = (data, callback) => {
66
+ compressOriginal(data)
67
+ .then(res => callback(undefined, res))
68
+ .catch(error => callback(error));
69
+ };
70
+ snappy.uncompress = (data, callback) => {
71
+ uncompressOriginal(data)
72
+ .then(res => callback(undefined, res))
73
+ .catch(error => callback(error));
74
+ };
75
+ }
76
+
45
77
  return snappy;
46
78
  }
47
79
 
48
80
  module.exports = {
81
+ PKG_VERSION,
49
82
  debugOptions,
50
83
  retrieveBSON,
51
84
  retrieveSnappy
package/lib/core/error.js CHANGED
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const MONGODB_ERROR_CODES = require('../error_codes').MONGODB_ERROR_CODES;
4
+
3
5
  const kErrorLabels = Symbol('errorLabels');
4
6
 
5
7
  /**
@@ -216,32 +218,32 @@ class MongoWriteConcernError extends MongoError {
216
218
 
217
219
  // see: https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst#terms
218
220
  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
230
232
  ]);
231
233
 
232
234
  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
245
247
  ]);
246
248
 
247
249
  function isRetryableWriteError(error) {
@@ -271,41 +273,44 @@ function isRetryableError(error) {
271
273
  }
272
274
 
273
275
  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
279
281
  ]);
280
282
 
281
283
  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
284
287
  ]);
285
288
 
286
289
  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
289
292
  ]);
290
293
 
291
294
  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);
294
298
  }
295
299
 
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);
297
301
  }
298
302
 
299
303
  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);
302
307
  }
303
308
 
304
309
  if (isRecoveringError(err)) {
305
310
  return false;
306
311
  }
307
312
 
308
- return err.message.match(/not master/);
313
+ return /not master/.test(err.message);
309
314
  }
310
315
 
311
316
  function isNodeShuttingDownError(err) {
@@ -316,10 +321,9 @@ function isNodeShuttingDownError(err) {
316
321
  * Determines whether SDAM can recover from a given error. If it cannot
317
322
  * then the pool will be cleared, and server state will completely reset
318
323
  * locally.
319
- *
320
- * @ignore
321
324
  * @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}
323
327
  */
324
328
  function isSDAMUnrecoverableError(error) {
325
329
  // NOTE: null check is here for a strictly pre-CMAP world, a timeout or
@@ -328,11 +332,7 @@ function isSDAMUnrecoverableError(error) {
328
332
  return true;
329
333
  }
330
334
 
331
- if (isRecoveringError(error) || isNotMasterError(error)) {
332
- return true;
333
- }
334
-
335
- return false;
335
+ return isRecoveringError(error) || isNotMasterError(error);
336
336
  }
337
337
 
338
338
  module.exports = {
package/lib/core/index.js CHANGED
@@ -15,7 +15,16 @@ try {
15
15
  }
16
16
  } catch (err) {} // eslint-disable-line
17
17
 
18
+ /** An enumeration of valid server API versions */
19
+ const ServerApiVersion = Object.freeze({
20
+ v1: '1'
21
+ });
22
+ const ValidServerApiVersions = Object.keys(ServerApiVersion).map(key => ServerApiVersion[key]);
23
+
18
24
  module.exports = {
25
+ // Versioned API
26
+ ServerApiVersion,
27
+ ValidServerApiVersions,
19
28
  // Errors
20
29
  MongoError: require('./error').MongoError,
21
30
  MongoNetworkError: require('./error').MongoNetworkError,
@@ -65,7 +65,8 @@ class Monitor extends EventEmitter {
65
65
  heartbeatFrequencyMS:
66
66
  typeof options.heartbeatFrequencyMS === 'number' ? options.heartbeatFrequencyMS : 10000,
67
67
  minHeartbeatFrequencyMS:
68
- typeof options.minHeartbeatFrequencyMS === 'number' ? options.minHeartbeatFrequencyMS : 500
68
+ typeof options.minHeartbeatFrequencyMS === 'number' ? options.minHeartbeatFrequencyMS : 500,
69
+ useUnifiedTopology: options.useUnifiedTopology
69
70
  });
70
71
 
71
72
  // TODO: refactor this to pull it directly from the pool, requires new ConnectionPool integration
@@ -204,8 +205,16 @@ function checkServer(monitor, callback) {
204
205
  const maxAwaitTimeMS = monitor.options.heartbeatFrequencyMS;
205
206
  const topologyVersion = monitor[kServer].description.topologyVersion;
206
207
  const isAwaitable = topologyVersion != null;
208
+ 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;
207
217
 
208
- const cmd = { ismaster: true };
209
218
  const options = { socketTimeout: connectTimeoutMS };
210
219
 
211
220
  if (isAwaitable) {
@@ -229,6 +238,11 @@ function checkServer(monitor, callback) {
229
238
  const isMaster = result.result;
230
239
  const rttPinger = monitor[kRTTPinger];
231
240
 
241
+ if ('isWritablePrimary' in isMaster) {
242
+ // Provide pre-hello-style response document.
243
+ isMaster.ismaster = isMaster.isWritablePrimary;
244
+ }
245
+
232
246
  const duration =
233
247
  isAwaitable && rttPinger ? rttPinger.roundTripTime : calculateDurationInMs(start);
234
248
 
@@ -114,6 +114,7 @@ class Server extends EventEmitter {
114
114
  credentials: options.credentials,
115
115
  topology
116
116
  };
117
+ this.serverApi = options.serverApi;
117
118
 
118
119
  // create the connection pool
119
120
  // NOTE: this used to happen in `connect`, we supported overriding pool options there
@@ -251,6 +252,7 @@ class Server extends EventEmitter {
251
252
  if (typeof options === 'function') {
252
253
  (callback = options), (options = {}), (options = options || {});
253
254
  }
255
+ options.serverApi = this.serverApi;
254
256
 
255
257
  if (this.s.state === STATE_CLOSING || this.s.state === STATE_CLOSED) {
256
258
  callback(new MongoError('server is closed'));
@@ -70,6 +70,10 @@ class ServerDescription {
70
70
  ismaster
71
71
  );
72
72
 
73
+ if (ismaster.isWritablePrimary != null) {
74
+ ismaster.ismaster = ismaster.isWritablePrimary;
75
+ }
76
+
73
77
  this.address = address;
74
78
  this.error = options.error;
75
79
  this.roundTripTime = options.roundTripTime || -1;
@@ -200,6 +200,7 @@ class Topology extends EventEmitter {
200
200
  // timer management
201
201
  connectionTimers: new Set()
202
202
  };
203
+ this.serverApi = options.serverApi;
203
204
 
204
205
  if (options.srvHost) {
205
206
  this.s.srvPoller =
@@ -494,7 +495,11 @@ class Topology extends EventEmitter {
494
495
  this.command(
495
496
  'admin.$cmd',
496
497
  { endSessions: sessions },
497
- { readPreference: ReadPreference.primaryPreferred, noResponse: true },
498
+ {
499
+ readPreference: ReadPreference.primaryPreferred,
500
+ noResponse: true,
501
+ serverApi: this.serverApi
502
+ },
498
503
  () => {
499
504
  // intentionally ignored, per spec
500
505
  if (typeof callback === 'function') callback();
@@ -742,8 +747,11 @@ class Topology extends EventEmitter {
742
747
  return this.s.state === STATE_CLOSED;
743
748
  }
744
749
 
750
+ /**
751
+ * @deprecated This function is deprecated and will be removed in the next major version.
752
+ */
745
753
  unref() {
746
- emitWarning('not implemented: `unref`');
754
+ emitWarning('`unref` is a noop and will be removed in the next major version');
747
755
  }
748
756
 
749
757
  // NOTE: There are many places in code where we explicitly check the last isMaster
@@ -472,17 +472,20 @@ function endTransaction(session, commandName, callback) {
472
472
  if (commandName === 'commitTransaction') {
473
473
  session.transaction.transition(TxnState.TRANSACTION_COMMITTED);
474
474
 
475
- if (
476
- e &&
477
- (e instanceof MongoNetworkError ||
475
+ if (e) {
476
+ if (
477
+ e instanceof MongoNetworkError ||
478
478
  e instanceof MongoWriteConcernError ||
479
479
  isRetryableError(e) ||
480
- isMaxTimeMSExpiredError(e))
481
- ) {
482
- if (isUnknownTransactionCommitResult(e)) {
483
- e.addErrorLabel('UnknownTransactionCommitResult');
480
+ isMaxTimeMSExpiredError(e)
481
+ ) {
482
+ if (isUnknownTransactionCommitResult(e)) {
483
+ e.addErrorLabel('UnknownTransactionCommitResult');
484
484
 
485
- // per txns spec, must unpin session in this case
485
+ // per txns spec, must unpin session in this case
486
+ session.transaction.unpinServer();
487
+ }
488
+ } else if (e.hasErrorLabel('TransientTransactionError')) {
486
489
  session.transaction.unpinServer();
487
490
  }
488
491
  }