mongodb 3.5.7 → 3.5.11

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 (42) hide show
  1. package/HISTORY.md +44 -0
  2. package/lib/bulk/common.js +4 -11
  3. package/lib/bulk/unordered.js +8 -0
  4. package/lib/change_stream.js +194 -145
  5. package/lib/cmap/connection.js +8 -4
  6. package/lib/cmap/connection_pool.js +11 -3
  7. package/lib/collection.js +2 -3
  8. package/lib/core/connection/apm.js +1 -1
  9. package/lib/core/connection/pool.js +2 -1
  10. package/lib/core/cursor.js +46 -59
  11. package/lib/core/error.js +11 -4
  12. package/lib/core/index.js +0 -1
  13. package/lib/core/sdam/monitor.js +53 -61
  14. package/lib/core/sdam/server.js +5 -2
  15. package/lib/core/sdam/server_description.js +12 -1
  16. package/lib/core/sdam/server_selection.js +25 -37
  17. package/lib/core/sdam/topology.js +4 -26
  18. package/lib/core/sessions.js +12 -6
  19. package/lib/core/topologies/read_preference.js +58 -6
  20. package/lib/core/topologies/replset.js +4 -4
  21. package/lib/core/utils.js +13 -23
  22. package/lib/core/wireprotocol/command.js +1 -6
  23. package/lib/core/wireprotocol/get_more.js +6 -1
  24. package/lib/cursor.js +10 -32
  25. package/lib/db.js +3 -3
  26. package/lib/error.js +26 -33
  27. package/lib/mongo_client.js +8 -0
  28. package/lib/operations/collection_ops.js +1 -2
  29. package/lib/operations/command.js +2 -3
  30. package/lib/operations/command_v2.js +7 -6
  31. package/lib/operations/connect.js +11 -0
  32. package/lib/operations/cursor_ops.js +2 -3
  33. package/lib/operations/db_ops.js +1 -2
  34. package/lib/operations/find.js +2 -2
  35. package/lib/operations/geo_haystack_search.js +2 -2
  36. package/lib/operations/map_reduce.js +2 -2
  37. package/lib/operations/operation.js +2 -1
  38. package/lib/operations/run_command.js +19 -0
  39. package/lib/topologies/native_topology.js +12 -2
  40. package/lib/topologies/topology_base.js +4 -4
  41. package/lib/utils.js +96 -60
  42. package/package.json +7 -4
@@ -275,7 +275,7 @@ class Topology extends EventEmitter {
275
275
  // connect all known servers, then attempt server selection to connect
276
276
  connectServers(this, Array.from(this.s.description.servers.values()));
277
277
 
278
- translateReadPreference(options);
278
+ ReadPreference.translate(options);
279
279
  const readPreference = options.readPreference || ReadPreference.primary;
280
280
  this.selectServer(readPreferenceServerSelector(readPreference), options, err => {
281
281
  if (err) {
@@ -381,7 +381,7 @@ class Topology extends EventEmitter {
381
381
  } else if (typeof selector === 'string') {
382
382
  readPreference = new ReadPreference(selector);
383
383
  } else {
384
- translateReadPreference(options);
384
+ ReadPreference.translate(options);
385
385
  readPreference = options.readPreference || ReadPreference.primary;
386
386
  }
387
387
 
@@ -647,7 +647,7 @@ class Topology extends EventEmitter {
647
647
  (callback = options), (options = {}), (options = options || {});
648
648
  }
649
649
 
650
- translateReadPreference(options);
650
+ ReadPreference.translate(options);
651
651
  const readPreference = options.readPreference || ReadPreference.primary;
652
652
 
653
653
  this.selectServer(readPreferenceServerSelector(readPreference), options, (err, server) => {
@@ -708,7 +708,7 @@ class Topology extends EventEmitter {
708
708
  options = options || {};
709
709
  const topology = options.topology || this;
710
710
  const CursorClass = options.cursorFactory || this.s.Cursor;
711
- translateReadPreference(options);
711
+ ReadPreference.translate(options);
712
712
 
713
713
  return new CursorClass(topology, ns, cmd, options);
714
714
  }
@@ -939,28 +939,6 @@ function executeWriteOperation(args, options, callback) {
939
939
  });
940
940
  }
941
941
 
942
- function translateReadPreference(options) {
943
- if (options.readPreference == null) {
944
- return;
945
- }
946
-
947
- let r = options.readPreference;
948
- if (typeof r === 'string') {
949
- options.readPreference = new ReadPreference(r);
950
- } else if (r && !(r instanceof ReadPreference) && typeof r === 'object') {
951
- const mode = r.mode || r.preference;
952
- if (mode && typeof mode === 'string') {
953
- options.readPreference = new ReadPreference(mode, r.tags, {
954
- maxStalenessSeconds: r.maxStalenessSeconds
955
- });
956
- }
957
- } else if (!(r instanceof ReadPreference)) {
958
- throw new TypeError('Invalid read preference: ' + r);
959
- }
960
-
961
- return options;
962
- }
963
-
964
942
  function srvPollingHandler(topology) {
965
943
  return function handleSrvPolling(ev) {
966
944
  const previousTopologyDescription = topology.s.description;
@@ -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) {
@@ -285,7 +286,7 @@ class ClientSession extends EventEmitter {
285
286
  * @param {TransactionOptions} [options] Optional settings for the transaction
286
287
  */
287
288
  withTransaction(fn, options) {
288
- const startTime = Date.now();
289
+ const startTime = now();
289
290
  return attemptTransaction(this, startTime, fn, options);
290
291
  }
291
292
  }
@@ -301,7 +302,7 @@ const NON_DETERMINISTIC_WRITE_CONCERN_ERRORS = new Set([
301
302
  ]);
302
303
 
303
304
  function hasNotTimedOut(startTime, max) {
304
- return Date.now() - startTime < max;
305
+ return calculateDurationInMs(startTime) < max;
305
306
  }
306
307
 
307
308
  function isUnknownTransactionCommitResult(err) {
@@ -558,7 +559,7 @@ function supportsRecoveryToken(session) {
558
559
  class ServerSession {
559
560
  constructor() {
560
561
  this.id = { id: new Binary(uuidV4(), Binary.SUBTYPE_UUID) };
561
- this.lastUse = Date.now();
562
+ this.lastUse = now();
562
563
  this.txnNumber = 0;
563
564
  this.isDirty = false;
564
565
  }
@@ -573,7 +574,7 @@ class ServerSession {
573
574
  // Take the difference of the lastUse timestamp and now, which will result in a value in
574
575
  // milliseconds, and then convert milliseconds to minutes to compare to `sessionTimeoutMinutes`
575
576
  const idleTimeMinutes = Math.round(
576
- (((Date.now() - this.lastUse) % 86400000) % 3600000) / 60000
577
+ ((calculateDurationInMs(this.lastUse) % 86400000) % 3600000) / 60000
577
578
  );
578
579
 
579
580
  return idleTimeMinutes > sessionTimeoutMinutes - 1;
@@ -707,8 +708,13 @@ function applySession(session, command, options) {
707
708
  return new MongoError('Cannot use a session that has ended');
708
709
  }
709
710
 
711
+ // SPEC-1019: silently ignore explicit session with unacknowledged write for backwards compatibility
712
+ if (options && options.writeConcern && options.writeConcern.w === 0) {
713
+ return;
714
+ }
715
+
710
716
  const serverSession = session.serverSession;
711
- serverSession.lastUse = Date.now();
717
+ serverSession.lastUse = now();
712
718
  command.lsid = serverSession.id;
713
719
 
714
720
  // first apply non-transaction-specific sessions data
@@ -91,20 +91,18 @@ const VALID_MODES = [
91
91
  * @return {ReadPreference}
92
92
  */
93
93
  ReadPreference.fromOptions = function(options) {
94
+ if (!options) return null;
94
95
  const readPreference = options.readPreference;
96
+ if (readPreference == null) return null;
95
97
  const readPreferenceTags = options.readPreferenceTags;
96
-
97
- if (readPreference == null) {
98
- return null;
99
- }
100
-
98
+ const maxStalenessSeconds = options.maxStalenessSeconds;
101
99
  if (typeof readPreference === 'string') {
102
100
  return new ReadPreference(readPreference, readPreferenceTags);
103
101
  } else if (!(readPreference instanceof ReadPreference) && typeof readPreference === 'object') {
104
102
  const mode = readPreference.mode || readPreference.preference;
105
103
  if (mode && typeof mode === 'string') {
106
104
  return new ReadPreference(mode, readPreference.tags, {
107
- maxStalenessSeconds: readPreference.maxStalenessSeconds
105
+ maxStalenessSeconds: readPreference.maxStalenessSeconds || maxStalenessSeconds
108
106
  });
109
107
  }
110
108
  }
@@ -112,6 +110,60 @@ ReadPreference.fromOptions = function(options) {
112
110
  return readPreference;
113
111
  };
114
112
 
113
+ /**
114
+ * Resolves a read preference based on well-defined inheritance rules. This method will not only
115
+ * determine the read preference (if there is one), but will also ensure the returned value is a
116
+ * properly constructed instance of `ReadPreference`.
117
+ *
118
+ * @param {Collection|Db|MongoClient} parent The parent of the operation on which to determine the read
119
+ * preference, used for determining the inherited read preference.
120
+ * @param {object} options The options passed into the method, potentially containing a read preference
121
+ * @returns {(ReadPreference|null)} The resolved read preference
122
+ */
123
+ ReadPreference.resolve = function(parent, options) {
124
+ options = options || {};
125
+ const session = options.session;
126
+
127
+ const inheritedReadPreference = parent && parent.readPreference;
128
+
129
+ let readPreference;
130
+ if (options.readPreference) {
131
+ readPreference = ReadPreference.fromOptions(options);
132
+ } else if (session && session.inTransaction() && session.transaction.options.readPreference) {
133
+ // The transaction’s read preference MUST override all other user configurable read preferences.
134
+ readPreference = session.transaction.options.readPreference;
135
+ } else if (inheritedReadPreference != null) {
136
+ readPreference = inheritedReadPreference;
137
+ } else {
138
+ readPreference = ReadPreference.primary;
139
+ }
140
+
141
+ return typeof readPreference === 'string' ? new ReadPreference(readPreference) : readPreference;
142
+ };
143
+
144
+ /**
145
+ * Replaces options.readPreference with a ReadPreference instance
146
+ */
147
+ ReadPreference.translate = function(options) {
148
+ if (options.readPreference == null) return options;
149
+ const r = options.readPreference;
150
+
151
+ if (typeof r === 'string') {
152
+ options.readPreference = new ReadPreference(r);
153
+ } else if (r && !(r instanceof ReadPreference) && typeof r === 'object') {
154
+ const mode = r.mode || r.preference;
155
+ if (mode && typeof mode === 'string') {
156
+ options.readPreference = new ReadPreference(mode, r.tags, {
157
+ maxStalenessSeconds: r.maxStalenessSeconds
158
+ });
159
+ }
160
+ } else if (!(r instanceof ReadPreference)) {
161
+ throw new TypeError('Invalid read preference: ' + r);
162
+ }
163
+
164
+ return options;
165
+ };
166
+
115
167
  /**
116
168
  * Validate if a mode is legal
117
169
  *
@@ -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) {
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,
@@ -66,12 +66,7 @@ function _command(server, ns, cmd, options, callback) {
66
66
  finalCmd.$clusterTime = clusterTime;
67
67
  }
68
68
 
69
- if (
70
- isSharded(server) &&
71
- !shouldUseOpMsg &&
72
- readPreference &&
73
- readPreference.preference !== 'primary'
74
- ) {
69
+ if (isSharded(server) && !shouldUseOpMsg && readPreference && readPreference.mode !== 'primary') {
75
70
  finalCmd = {
76
71
  $query: finalCmd,
77
72
  $readPreference: readPreference.toJSON()
@@ -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
@@ -832,9 +832,7 @@ class Cursor extends CoreCursor {
832
832
  const fetchDocs = () => {
833
833
  cursor._next((err, doc) => {
834
834
  if (err) {
835
- return cursor._endSession
836
- ? cursor._endSession(() => handleCallback(cb, err))
837
- : handleCallback(cb, err);
835
+ return handleCallback(cb, err);
838
836
  }
839
837
 
840
838
  if (doc == null) {
@@ -914,38 +912,18 @@ class Cursor extends CoreCursor {
914
912
  if (typeof options === 'function') (callback = options), (options = {});
915
913
  options = Object.assign({}, { skipKillCursors: false }, options);
916
914
 
917
- this.s.state = CursorState.CLOSED;
918
- if (!options.skipKillCursors) {
919
- // Kill the cursor
920
- this.kill();
921
- }
922
-
923
- const completeClose = () => {
924
- // Emit the close event for the cursor
925
- this.emit('close');
926
-
927
- // Callback if provided
928
- if (typeof callback === 'function') {
929
- return handleCallback(callback, null, this);
930
- }
931
-
932
- // Return a Promise
933
- return new this.s.promiseLibrary(resolve => {
934
- resolve();
935
- });
936
- };
937
-
938
- if (this.cursorState.session) {
939
- if (typeof callback === 'function') {
940
- 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();
941
920
  }
942
921
 
943
- return new this.s.promiseLibrary(resolve => {
944
- this._endSession(() => completeClose().then(resolve));
922
+ this._endSession(() => {
923
+ this.emit('close');
924
+ cb(null, this);
945
925
  });
946
- }
947
-
948
- return completeClose();
926
+ });
949
927
  }
950
928
 
951
929
  /**
package/lib/db.js CHANGED
@@ -14,7 +14,6 @@ const Logger = require('./core').Logger;
14
14
  const Collection = require('./collection');
15
15
  const mergeOptionsAndWriteConcern = require('./utils').mergeOptionsAndWriteConcern;
16
16
  const executeLegacyOperation = require('./utils').executeLegacyOperation;
17
- const resolveReadPreference = require('./utils').resolveReadPreference;
18
17
  const ChangeStream = require('./change_stream');
19
18
  const deprecate = require('util').deprecate;
20
19
  const deprecateOptions = require('./utils').deprecateOptions;
@@ -35,6 +34,7 @@ const AggregateOperation = require('./operations/aggregate');
35
34
  const AddUserOperation = require('./operations/add_user');
36
35
  const CollectionsOperation = require('./operations/collections');
37
36
  const CommandOperation = require('./operations/command');
37
+ const RunCommandOperation = require('./operations/run_command');
38
38
  const CreateCollectionOperation = require('./operations/create_collection');
39
39
  const CreateIndexOperation = require('./operations/create_index');
40
40
  const DropCollectionOperation = require('./operations/drop').DropCollectionOperation;
@@ -290,7 +290,7 @@ Db.prototype.command = function(command, options, callback) {
290
290
  if (typeof options === 'function') (callback = options), (options = {});
291
291
  options = Object.assign({}, options);
292
292
 
293
- const commandOperation = new CommandOperation(this, options, null, command);
293
+ const commandOperation = new RunCommandOperation(this, command, options);
294
294
 
295
295
  return executeOperation(this.s.topology, commandOperation, callback);
296
296
  };
@@ -709,7 +709,7 @@ Db.prototype.collections = function(options, callback) {
709
709
  Db.prototype.executeDbAdminCommand = function(selector, options, callback) {
710
710
  if (typeof options === 'function') (callback = options), (options = {});
711
711
  options = options || {};
712
- options.readPreference = resolveReadPreference(this, options);
712
+ options.readPreference = ReadPreference.resolve(this, options);
713
713
 
714
714
  const executeDbAdminCommandOperation = new ExecuteDbAdminCommandOperation(
715
715
  this,
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 };
@@ -146,6 +146,10 @@ const validOptions = require('./operations/connect').validOptions;
146
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
147
  * @param {Number} [options.serverSelectionTimeoutMS=30000] **Only applies to the unified topology** How long to block for server selection before throwing an error
148
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.
149
153
  * @param {AutoEncrypter~AutoEncryptionOptions} [options.autoEncryption] Optionally enable client side auto encryption
150
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
151
155
  * @param {MongoClient~connectCallback} [callback] The command result callback
@@ -415,6 +419,10 @@ MongoClient.prototype.isConnected = function(options) {
415
419
  * @param {Number} [options.localThresholdMS=15] **Only applies to the unified topology** The size of the latency window for selecting among multiple suitable servers
416
420
  * @param {Number} [options.serverSelectionTimeoutMS=30000] **Only applies to the unified topology** How long to block for server selection before throwing an error
417
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.
418
426
  * @param {AutoEncrypter~AutoEncryptionOptions} [options.autoEncryption] Optionally enable client side auto encryption
419
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
420
428
  * @param {MongoClient~connectCallback} [callback] The command result callback
@@ -8,7 +8,6 @@ const decorateWithReadConcern = require('../utils').decorateWithReadConcern;
8
8
  const ensureIndexDb = require('./db_ops').ensureIndex;
9
9
  const evaluate = require('./db_ops').evaluate;
10
10
  const executeCommand = require('./db_ops').executeCommand;
11
- const resolveReadPreference = require('../utils').resolveReadPreference;
12
11
  const handleCallback = require('../utils').handleCallback;
13
12
  const indexInformationDb = require('./db_ops').indexInformation;
14
13
  const Long = require('../core').BSON.Long;
@@ -188,7 +187,7 @@ function group(coll, keys, condition, initial, reduce, finalize, command, option
188
187
 
189
188
  options = Object.assign({}, options);
190
189
  // Ensure we have the right read preference inheritance
191
- options.readPreference = resolveReadPreference(coll, options);
190
+ options.readPreference = ReadPreference.resolve(coll, options);
192
191
 
193
192
  // Do we have a readConcern specified
194
193
  decorateWithReadConcern(selector, coll, options);
@@ -7,7 +7,6 @@ const debugOptions = require('../utils').debugOptions;
7
7
  const handleCallback = require('../utils').handleCallback;
8
8
  const MongoError = require('../core').MongoError;
9
9
  const ReadPreference = require('../core').ReadPreference;
10
- const resolveReadPreference = require('../utils').resolveReadPreference;
11
10
  const MongoDBNamespace = require('../utils').MongoDBNamespace;
12
11
 
13
12
  const debugFields = [
@@ -38,9 +37,9 @@ class CommandOperation extends OperationBase {
38
37
 
39
38
  if (!this.hasAspect(Aspect.WRITE_OPERATION)) {
40
39
  if (collection != null) {
41
- this.options.readPreference = resolveReadPreference(collection, options);
40
+ this.options.readPreference = ReadPreference.resolve(collection, options);
42
41
  } else {
43
- this.options.readPreference = resolveReadPreference(db, options);
42
+ this.options.readPreference = ReadPreference.resolve(db, options);
44
43
  }
45
44
  } else {
46
45
  if (collection != null) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  const Aspect = require('./operation').Aspect;
4
4
  const OperationBase = require('./operation').OperationBase;
5
- const resolveReadPreference = require('../utils').resolveReadPreference;
5
+ const ReadPreference = require('../core').ReadPreference;
6
6
  const ReadConcern = require('../read_concern');
7
7
  const WriteConcern = require('../write_concern');
8
8
  const maxWireVersion = require('../core/utils').maxWireVersion;
@@ -16,9 +16,10 @@ class CommandOperationV2 extends OperationBase {
16
16
  super(options);
17
17
 
18
18
  this.ns = parent.s.namespace.withCollection('$cmd');
19
- this.readPreference = resolveReadPreference(parent, this.options);
20
- this.readConcern = resolveReadConcern(parent, this.options);
21
- this.writeConcern = resolveWriteConcern(parent, this.options);
19
+ const propertyProvider = this.hasAspect(Aspect.NO_INHERIT_OPTIONS) ? undefined : parent;
20
+ this.readPreference = ReadPreference.resolve(propertyProvider, this.options);
21
+ this.readConcern = resolveReadConcern(propertyProvider, this.options);
22
+ this.writeConcern = resolveWriteConcern(propertyProvider, this.options);
22
23
  this.explain = false;
23
24
 
24
25
  if (operationOptions && typeof operationOptions.fullResponse === 'boolean') {
@@ -97,11 +98,11 @@ class CommandOperationV2 extends OperationBase {
97
98
  }
98
99
 
99
100
  function resolveWriteConcern(parent, options) {
100
- return WriteConcern.fromOptions(options) || parent.writeConcern;
101
+ return WriteConcern.fromOptions(options) || (parent && parent.writeConcern);
101
102
  }
102
103
 
103
104
  function resolveReadConcern(parent, options) {
104
- return ReadConcern.fromOptions(options) || parent.readConcern;
105
+ return ReadConcern.fromOptions(options) || (parent && parent.readConcern);
105
106
  }
106
107
 
107
108
  module.exports = CommandOperationV2;
@@ -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
  }
@@ -2,7 +2,6 @@
2
2
 
3
3
  const applyWriteConcern = require('../utils').applyWriteConcern;
4
4
  const Code = require('../core').BSON.Code;
5
- const resolveReadPreference = require('../utils').resolveReadPreference;
6
5
  const debugOptions = require('../utils').debugOptions;
7
6
  const handleCallback = require('../utils').handleCallback;
8
7
  const MongoError = require('../core').MongoError;
@@ -225,7 +224,7 @@ function executeCommand(db, command, options, callback) {
225
224
  const dbName = options.dbName || options.authdb || db.databaseName;
226
225
 
227
226
  // Convert the readPreference if its not a write
228
- options.readPreference = resolveReadPreference(db, options);
227
+ options.readPreference = ReadPreference.resolve(db, options);
229
228
 
230
229
  // Debug information
231
230
  if (db.s.logger.isDebug())