mongodb 3.5.5 → 3.5.9

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.
@@ -17,7 +17,8 @@ const isTransactionCommand = require('./transactions').isTransactionCommand;
17
17
  const resolveClusterTime = require('./topologies/shared').resolveClusterTime;
18
18
  const isSharded = require('./wireprotocol/shared').isSharded;
19
19
  const maxWireVersion = require('./utils').maxWireVersion;
20
-
20
+ const now = require('./../utils').now;
21
+ const calculateDurationInMs = require('./../utils').calculateDurationInMs;
21
22
  const minWireVersionForShardedTransactions = 8;
22
23
 
23
24
  function assertAlive(session, callback) {
@@ -46,6 +47,8 @@ function assertAlive(session, callback) {
46
47
  * @typedef {Object} SessionId
47
48
  */
48
49
 
50
+ const kServerSession = Symbol('serverSession');
51
+
49
52
  /**
50
53
  * A class representing a client session on the server
51
54
  * WARNING: not meant to be instantiated directly.
@@ -79,8 +82,8 @@ class ClientSession extends EventEmitter {
79
82
  this.topology = topology;
80
83
  this.sessionPool = sessionPool;
81
84
  this.hasEnded = false;
82
- this.serverSession = sessionPool.acquire();
83
85
  this.clientOptions = clientOptions;
86
+ this[kServerSession] = undefined;
84
87
 
85
88
  this.supports = {
86
89
  causalConsistency:
@@ -104,6 +107,14 @@ class ClientSession extends EventEmitter {
104
107
  return this.serverSession.id;
105
108
  }
106
109
 
110
+ get serverSession() {
111
+ if (this[kServerSession] == null) {
112
+ this[kServerSession] = this.sessionPool.acquire();
113
+ }
114
+
115
+ return this[kServerSession];
116
+ }
117
+
107
118
  /**
108
119
  * Ends this session on the server
109
120
  *
@@ -123,14 +134,14 @@ class ClientSession extends EventEmitter {
123
134
  this.abortTransaction(); // pass in callback?
124
135
  }
125
136
 
137
+ // release the server session back to the pool
138
+ this.sessionPool.release(this.serverSession);
139
+ this[kServerSession] = undefined;
140
+
126
141
  // mark the session as ended, and emit a signal
127
142
  this.hasEnded = true;
128
143
  this.emit('ended', this);
129
144
 
130
- // release the server session back to the pool
131
- this.sessionPool.release(this.serverSession);
132
- this.serverSession = null;
133
-
134
145
  // spec indicates that we should ignore all errors for `endSessions`
135
146
  if (typeof callback === 'function') callback(null, null);
136
147
  }
@@ -275,7 +286,7 @@ class ClientSession extends EventEmitter {
275
286
  * @param {TransactionOptions} [options] Optional settings for the transaction
276
287
  */
277
288
  withTransaction(fn, options) {
278
- const startTime = Date.now();
289
+ const startTime = now();
279
290
  return attemptTransaction(this, startTime, fn, options);
280
291
  }
281
292
  }
@@ -291,7 +302,7 @@ const NON_DETERMINISTIC_WRITE_CONCERN_ERRORS = new Set([
291
302
  ]);
292
303
 
293
304
  function hasNotTimedOut(startTime, max) {
294
- return Date.now() - startTime < max;
305
+ return calculateDurationInMs(startTime) < max;
295
306
  }
296
307
 
297
308
  function isUnknownTransactionCommitResult(err) {
@@ -304,6 +315,7 @@ function isUnknownTransactionCommitResult(err) {
304
315
  }
305
316
 
306
317
  function isMaxTimeMSExpiredError(err) {
318
+ if (err == null) return false;
307
319
  return (
308
320
  err.code === MAX_TIME_MS_EXPIRED_CODE ||
309
321
  (err.writeConcernError && err.writeConcernError.code === MAX_TIME_MS_EXPIRED_CODE)
@@ -547,7 +559,7 @@ function supportsRecoveryToken(session) {
547
559
  class ServerSession {
548
560
  constructor() {
549
561
  this.id = { id: new Binary(uuidV4(), Binary.SUBTYPE_UUID) };
550
- this.lastUse = Date.now();
562
+ this.lastUse = now();
551
563
  this.txnNumber = 0;
552
564
  this.isDirty = false;
553
565
  }
@@ -562,7 +574,7 @@ class ServerSession {
562
574
  // Take the difference of the lastUse timestamp and now, which will result in a value in
563
575
  // milliseconds, and then convert milliseconds to minutes to compare to `sessionTimeoutMinutes`
564
576
  const idleTimeMinutes = Math.round(
565
- (((Date.now() - this.lastUse) % 86400000) % 3600000) / 60000
577
+ ((calculateDurationInMs(this.lastUse) % 86400000) % 3600000) / 60000
566
578
  );
567
579
 
568
580
  return idleTimeMinutes > sessionTimeoutMinutes - 1;
@@ -691,14 +703,13 @@ function commandSupportsReadConcern(command, options) {
691
703
  * @return {MongoError|null} An error, if some error condition was met
692
704
  */
693
705
  function applySession(session, command, options) {
694
- const serverSession = session.serverSession;
695
- if (serverSession == null) {
706
+ if (session.hasEnded) {
696
707
  // TODO: merge this with `assertAlive`, did not want to throw a try/catch here
697
708
  return new MongoError('Cannot use a session that has ended');
698
709
  }
699
710
 
700
- // mark the last use of this session, and apply the `lsid`
701
- serverSession.lastUse = Date.now();
711
+ const serverSession = session.serverSession;
712
+ serverSession.lastUse = now();
702
713
  command.lsid = serverSession.id;
703
714
 
704
715
  // first apply non-transaction-specific sessions data
@@ -17,9 +17,10 @@ const isRetryableWritesSupported = require('./shared').isRetryableWritesSupporte
17
17
  const relayEvents = require('../utils').relayEvents;
18
18
  const isRetryableError = require('../error').isRetryableError;
19
19
  const BSON = retrieveBSON();
20
- const calculateDurationInMs = require('../utils').calculateDurationInMs;
21
20
  const getMMAPError = require('./shared').getMMAPError;
22
21
  const makeClientMetadata = require('../utils').makeClientMetadata;
22
+ const now = require('../../utils').now;
23
+ const calculateDurationInMs = require('../../utils').calculateDurationInMs;
23
24
 
24
25
  //
25
26
  // States
@@ -426,8 +427,7 @@ var pingServer = function(self, server, cb) {
426
427
  var latencyMS = new Date().getTime() - start;
427
428
 
428
429
  // Set the last updatedTime
429
- var hrtime = process.hrtime();
430
- server.lastUpdateTime = (hrtime[0] * 1e9 + hrtime[1]) / 1e6;
430
+ server.lastUpdateTime = now();
431
431
 
432
432
  // We had an error, remove it from the state
433
433
  if (err) {
@@ -1127,7 +1127,7 @@ ReplSet.prototype.selectServer = function(selector, options, callback) {
1127
1127
  }
1128
1128
 
1129
1129
  let lastError;
1130
- const start = process.hrtime();
1130
+ const start = now();
1131
1131
  const _selectServer = () => {
1132
1132
  if (calculateDurationInMs(start) >= SERVER_SELECTION_TIMEOUT_MS) {
1133
1133
  if (lastError != null) {
@@ -285,8 +285,8 @@ function applyConnectionStringOption(obj, key, value, options) {
285
285
  }
286
286
  }
287
287
 
288
- if (key === 'readpreferencetags' && Array.isArray(value)) {
289
- value = splitArrayOfMultipleReadPreferenceTags(value);
288
+ if (key === 'readpreferencetags') {
289
+ value = Array.isArray(value) ? splitArrayOfMultipleReadPreferenceTags(value) : [value];
290
290
  }
291
291
 
292
292
  // set the actual value
package/lib/core/utils.js CHANGED
@@ -13,17 +13,6 @@ const uuidV4 = () => {
13
13
  return result;
14
14
  };
15
15
 
16
- /**
17
- * Returns the duration calculated from two high resolution timers in milliseconds
18
- *
19
- * @param {Object} started A high resolution timestamp created from `process.hrtime()`
20
- * @returns {Number} The duration in milliseconds
21
- */
22
- const calculateDurationInMs = started => {
23
- const hrtime = process.hrtime(started);
24
- return (hrtime[0] * 1e9 + hrtime[1]) / 1e6;
25
- };
26
-
27
16
  /**
28
17
  * Relays events for a given listener and emitter
29
18
  *
@@ -83,22 +72,24 @@ function retrieveEJSON() {
83
72
  * @param {(Topology|Server)} topologyOrServer
84
73
  */
85
74
  function maxWireVersion(topologyOrServer) {
86
- if (topologyOrServer.ismaster) {
87
- return topologyOrServer.ismaster.maxWireVersion;
88
- }
75
+ if (topologyOrServer) {
76
+ if (topologyOrServer.ismaster) {
77
+ return topologyOrServer.ismaster.maxWireVersion;
78
+ }
89
79
 
90
- if (typeof topologyOrServer.lastIsMaster === 'function') {
91
- const lastIsMaster = topologyOrServer.lastIsMaster();
92
- if (lastIsMaster) {
93
- return lastIsMaster.maxWireVersion;
80
+ if (typeof topologyOrServer.lastIsMaster === 'function') {
81
+ const lastIsMaster = topologyOrServer.lastIsMaster();
82
+ if (lastIsMaster) {
83
+ return lastIsMaster.maxWireVersion;
84
+ }
94
85
  }
95
- }
96
86
 
97
- if (topologyOrServer.description) {
98
- return topologyOrServer.description.maxWireVersion;
87
+ if (topologyOrServer.description) {
88
+ return topologyOrServer.description.maxWireVersion;
89
+ }
99
90
  }
100
91
 
101
- return null;
92
+ return 0;
102
93
  }
103
94
 
104
95
  /*
@@ -259,7 +250,6 @@ const noop = () => {};
259
250
 
260
251
  module.exports = {
261
252
  uuidV4,
262
- calculateDurationInMs,
263
253
  relayEvents,
264
254
  collationNotSupported,
265
255
  retrieveEJSON,
@@ -62,8 +62,13 @@ function getMore(server, ns, cursorState, batchSize, options, callback) {
62
62
  return;
63
63
  }
64
64
 
65
+ const cursorId =
66
+ cursorState.cursorId instanceof Long
67
+ ? cursorState.cursorId
68
+ : Long.fromNumber(cursorState.cursorId);
69
+
65
70
  const getMoreCmd = {
66
- getMore: cursorState.cursorId,
71
+ getMore: cursorId,
67
72
  collection: collectionNamespace(ns),
68
73
  batchSize: Math.abs(batchSize)
69
74
  };
package/lib/cursor.js CHANGED
@@ -819,6 +819,7 @@ class Cursor extends CoreCursor {
819
819
  driver: true
820
820
  });
821
821
  }
822
+
822
823
  return maybePromise(this, callback, cb => {
823
824
  const cursor = this;
824
825
  const items = [];
@@ -831,9 +832,7 @@ class Cursor extends CoreCursor {
831
832
  const fetchDocs = () => {
832
833
  cursor._next((err, doc) => {
833
834
  if (err) {
834
- return cursor._endSession
835
- ? cursor._endSession(() => handleCallback(cb, err))
836
- : handleCallback(cb, err);
835
+ return handleCallback(cb, err);
837
836
  }
838
837
 
839
838
  if (doc == null) {
@@ -846,12 +845,6 @@ class Cursor extends CoreCursor {
846
845
  // Get all buffered objects
847
846
  if (cursor.bufferedCount() > 0) {
848
847
  let docs = cursor.readBufferedDocuments(cursor.bufferedCount());
849
-
850
- // Transform the doc if transform method added
851
- if (cursor.s.transforms && typeof cursor.s.transforms.doc === 'function') {
852
- docs = docs.map(cursor.s.transforms.doc);
853
- }
854
-
855
848
  Array.prototype.push.apply(items, docs);
856
849
  }
857
850
 
@@ -919,38 +912,18 @@ class Cursor extends CoreCursor {
919
912
  if (typeof options === 'function') (callback = options), (options = {});
920
913
  options = Object.assign({}, { skipKillCursors: false }, options);
921
914
 
922
- this.s.state = CursorState.CLOSED;
923
- if (!options.skipKillCursors) {
924
- // Kill the cursor
925
- this.kill();
926
- }
927
-
928
- const completeClose = () => {
929
- // Emit the close event for the cursor
930
- this.emit('close');
931
-
932
- // Callback if provided
933
- if (typeof callback === 'function') {
934
- return handleCallback(callback, null, this);
935
- }
936
-
937
- // Return a Promise
938
- return new this.s.promiseLibrary(resolve => {
939
- resolve();
940
- });
941
- };
942
-
943
- if (this.cursorState.session) {
944
- if (typeof callback === 'function') {
945
- return this._endSession(() => completeClose());
915
+ return maybePromise(this, callback, cb => {
916
+ this.s.state = CursorState.CLOSED;
917
+ if (!options.skipKillCursors) {
918
+ // Kill the cursor
919
+ this.kill();
946
920
  }
947
921
 
948
- return new this.s.promiseLibrary(resolve => {
949
- this._endSession(() => completeClose().then(resolve));
922
+ this._endSession(() => {
923
+ this.emit('close');
924
+ cb(null, this);
950
925
  });
951
- }
952
-
953
- return completeClose();
926
+ });
954
927
  }
955
928
 
956
929
  /**
package/lib/error.js CHANGED
@@ -1,45 +1,38 @@
1
1
  'use strict';
2
2
 
3
3
  const MongoNetworkError = require('./core').MongoNetworkError;
4
- const mongoErrorContextSymbol = require('./core').mongoErrorContextSymbol;
5
4
 
6
- const GET_MORE_NON_RESUMABLE_CODES = new Set([
7
- 136, // CappedPositionLost
8
- 237, // CursorKilled
9
- 11601 // Interrupted
5
+ // From spec@https://github.com/mongodb/specifications/blob/f93d78191f3db2898a59013a7ed5650352ef6da8/source/change-streams/change-streams.rst#resumable-error
6
+ const GET_MORE_RESUMABLE_CODES = new Set([
7
+ 6, // HostUnreachable
8
+ 7, // HostNotFound
9
+ 89, // NetworkTimeout
10
+ 91, // ShutdownInProgress
11
+ 189, // PrimarySteppedDown
12
+ 262, // ExceededTimeLimit
13
+ 9001, // SocketException
14
+ 10107, // NotMaster
15
+ 11600, // InterruptedAtShutdown
16
+ 11602, // InterruptedDueToReplStateChange
17
+ 13435, // NotMasterNoSlaveOk
18
+ 13436, // NotMasterOrSecondary
19
+ 63, // StaleShardVersion
20
+ 150, // StaleEpoch
21
+ 13388, // StaleConfig
22
+ 234, // RetryChangeStream
23
+ 133 // FailedToSatisfyReadPreference
10
24
  ]);
11
25
 
12
- // From spec@https://github.com/mongodb/specifications/blob/7a2e93d85935ee4b1046a8d2ad3514c657dc74fa/source/change-streams/change-streams.rst#resumable-error:
13
- //
14
- // An error is considered resumable if it meets any of the following criteria:
15
- // - any error encountered which is not a server error (e.g. a timeout error or network error)
16
- // - any server error response from a getMore command excluding those containing the error label
17
- // NonRetryableChangeStreamError and those containing the following error codes:
18
- // - Interrupted: 11601
19
- // - CappedPositionLost: 136
20
- // - CursorKilled: 237
21
- //
22
- // An error on an aggregate command is not a resumable error. Only errors on a getMore command may be considered resumable errors.
23
-
24
- function isGetMoreError(error) {
25
- if (error[mongoErrorContextSymbol]) {
26
- return error[mongoErrorContextSymbol].isGetMore;
27
- }
28
- }
29
-
30
- function isResumableError(error) {
31
- if (!isGetMoreError(error)) {
32
- return false;
33
- }
34
-
26
+ function isResumableError(error, wireVersion) {
35
27
  if (error instanceof MongoNetworkError) {
36
28
  return true;
37
29
  }
38
30
 
39
- return !(
40
- GET_MORE_NON_RESUMABLE_CODES.has(error.code) ||
41
- error.hasErrorLabel('NonRetryableChangeStreamError')
42
- );
31
+ if (wireVersion >= 9) {
32
+ return error.hasErrorLabel('ResumableChangeStreamError');
33
+ }
34
+
35
+ return GET_MORE_RESUMABLE_CODES.has(error.code);
43
36
  }
44
37
 
45
- module.exports = { GET_MORE_NON_RESUMABLE_CODES, isResumableError };
38
+ module.exports = { GET_MORE_RESUMABLE_CODES, isResumableError };
@@ -143,6 +143,13 @@ const validOptions = require('./operations/connect').validOptions;
143
143
  * @param {number} [options.minSize] If present, the connection pool will be initialized with minSize connections, and will never dip below minSize connections
144
144
  * @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.
145
145
  * @param {boolean} [options.useUnifiedTopology] Enables the new unified topology layer
146
+ * @param {Number} [options.localThresholdMS=15] **Only applies to the unified topology** The size of the latency window for selecting among multiple suitable servers
147
+ * @param {Number} [options.serverSelectionTimeoutMS=30000] **Only applies to the unified topology** How long to block for server selection before throwing an error
148
+ * @param {Number} [options.heartbeatFrequencyMS=10000] **Only applies to the unified topology** The frequency with which topology updates are scheduled
149
+ * @param {number} [options.maxPoolSize=10] **Only applies to the unified topology** The maximum number of connections that may be associated with a pool at a given time. This includes in use and available connections.
150
+ * @param {number} [options.minPoolSize=0] **Only applies to the unified topology** The minimum number of connections that MUST exist at any moment in a single connection pool.
151
+ * @param {number} [options.maxIdleTimeMS] **Only applies to the unified topology** The maximum amount of time a connection should remain idle in the connection pool before being marked idle. The default is infinity.
152
+ * @param {number} [options.waitQueueTimeoutMS=0] **Only applies to the unified topology** The maximum amount of time operation execution should wait for a connection to become available. The default is 0 which means there is no limit.
146
153
  * @param {AutoEncrypter~AutoEncryptionOptions} [options.autoEncryption] Optionally enable client side auto encryption
147
154
  * @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
155
  * @param {MongoClient~connectCallback} [callback] The command result callback
@@ -344,7 +351,7 @@ MongoClient.prototype.isConnected = function(options) {
344
351
  * @param {object} [options] Optional settings
345
352
  * @param {number} [options.poolSize=5] The maximum size of the individual server pool
346
353
  * @param {boolean} [options.ssl=false] Enable SSL connection. *deprecated* use `tls` variants
347
- * @param {boolean} [options.sslValidate=false] Validate mongod server certificate against Certificate Authority *deprecated* use `tls` variants
354
+ * @param {boolean} [options.sslValidate=false] Validate mongod server certificate against Certificate Authority
348
355
  * @param {buffer} [options.sslCA=undefined] SSL Certificate store binary buffer *deprecated* use `tls` variants
349
356
  * @param {buffer} [options.sslCert=undefined] SSL Certificate binary buffer *deprecated* use `tls` variants
350
357
  * @param {buffer} [options.sslKey=undefined] SSL Key file binary buffer *deprecated* use `tls` variants
@@ -361,7 +368,7 @@ MongoClient.prototype.isConnected = function(options) {
361
368
  * @param {boolean} [options.autoReconnect=true] Enable autoReconnect for single server instances
362
369
  * @param {boolean} [options.noDelay=true] TCP Connection no delay
363
370
  * @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
364
- * @param {boolean} [options.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
371
+ * @param {number} [options.keepAliveInitialDelay=30000] The number of milliseconds to wait before initiating keepAlive on the TCP socket
365
372
  * @param {number} [options.connectTimeoutMS=10000] How long to wait for a connection to be established before timing out
366
373
  * @param {number} [options.socketTimeoutMS=360000] How long a send or receive on a socket can take before timing out
367
374
  * @param {number} [options.family] Version of IP stack. Can be 4, 6 or null (default).
@@ -405,7 +412,19 @@ MongoClient.prototype.isConnected = function(options) {
405
412
  * @param {array} [options.readPreferenceTags] Read preference tags
406
413
  * @param {number} [options.numberOfRetries=5] The number of retries for a tailable cursor
407
414
  * @param {boolean} [options.auto_reconnect=true] Enable auto reconnecting for single server instances
415
+ * @param {boolean} [options.monitorCommands=false] Enable command monitoring for this client
408
416
  * @param {number} [options.minSize] If present, the connection pool will be initialized with minSize connections, and will never dip below minSize connections
417
+ * @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.
418
+ * @param {boolean} [options.useUnifiedTopology] Enables the new unified topology layer
419
+ * @param {Number} [options.localThresholdMS=15] **Only applies to the unified topology** The size of the latency window for selecting among multiple suitable servers
420
+ * @param {Number} [options.serverSelectionTimeoutMS=30000] **Only applies to the unified topology** How long to block for server selection before throwing an error
421
+ * @param {Number} [options.heartbeatFrequencyMS=10000] **Only applies to the unified topology** The frequency with which topology updates are scheduled
422
+ * @param {number} [options.maxPoolSize=10] **Only applies to the unified topology** The maximum number of connections that may be associated with a pool at a given time. This includes in use and available connections.
423
+ * @param {number} [options.minPoolSize=0] **Only applies to the unified topology** The minimum number of connections that MUST exist at any moment in a single connection pool.
424
+ * @param {number} [options.maxIdleTimeMS] **Only applies to the unified topology** The maximum amount of time a connection should remain idle in the connection pool before being marked idle. The default is infinity.
425
+ * @param {number} [options.waitQueueTimeoutMS=0] **Only applies to the unified topology** The maximum amount of time operation execution should wait for a connection to become available. The default is 0 which means there is no limit.
426
+ * @param {AutoEncrypter~AutoEncryptionOptions} [options.autoEncryption] Optionally enable client side auto encryption
427
+ * @param {DriverInfoOptions} [options.driverInfo] Allows a wrapping driver to amend the client metadata generated by the driver to include information about the wrapping driver
409
428
  * @param {MongoClient~connectCallback} [callback] The command result callback
410
429
  * @return {Promise<MongoClient>} returns Promise if no callback passed
411
430
  */
@@ -151,6 +151,11 @@ const validOptionNames = [
151
151
  'tlsCertificateKeyFilePassword',
152
152
  'minHeartbeatFrequencyMS',
153
153
  'heartbeatFrequencyMS',
154
+
155
+ // CMAP options
156
+ 'maxPoolSize',
157
+ 'minPoolSize',
158
+ 'maxIdleTimeMS',
154
159
  'waitQueueTimeoutMS'
155
160
  ];
156
161
 
@@ -290,6 +295,12 @@ function connect(mongoClient, url, options, callback) {
290
295
  delete _finalOptions.db_options.auth;
291
296
  }
292
297
 
298
+ // `journal` should be translated to `j` for the driver
299
+ if (_finalOptions.journal != null) {
300
+ _finalOptions.j = _finalOptions.journal;
301
+ _finalOptions.journal = undefined;
302
+ }
303
+
293
304
  // resolve tls options if needed
294
305
  resolveTLSOptions(_finalOptions);
295
306
 
@@ -134,10 +134,9 @@ function toArray(cursor, callback) {
134
134
  const fetchDocs = () => {
135
135
  cursor._next((err, doc) => {
136
136
  if (err) {
137
- return cursor._endSession
138
- ? cursor._endSession(() => handleCallback(callback, err))
139
- : handleCallback(callback, err);
137
+ return handleCallback(callback, err);
140
138
  }
139
+
141
140
  if (doc == null) {
142
141
  return cursor.close({ skipKillCursors: true }, () => handleCallback(callback, null, items));
143
142
  }