mongodb 3.3.0-beta1 → 3.3.0-beta2

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.
@@ -56,10 +56,12 @@ const MongoDBNamespace = require('./utils').MongoDBNamespace;
56
56
  * @fires AggregationCursor#readable
57
57
  * @return {AggregationCursor} an AggregationCursor instance.
58
58
  */
59
- var AggregationCursor = function(bson, ns, cmd, options, topology, topologyOptions) {
59
+ var AggregationCursor = function(topology, ns, cmd, options) {
60
60
  CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
61
61
  var state = AggregationCursor.INIT;
62
62
  var streamOptions = {};
63
+ const bson = topology.s.bson;
64
+ const topologyOptions = topology.s.options;
63
65
 
64
66
  // MaxTimeMS
65
67
  var maxTimeMS = null;
@@ -5,8 +5,14 @@ const isResumableError = require('./error').isResumableError;
5
5
  const MongoError = require('./core').MongoError;
6
6
  const ReadConcern = require('./read_concern');
7
7
  const MongoDBNamespace = require('./utils').MongoDBNamespace;
8
+ const Cursor = require('./cursor');
9
+ const relayEvents = require('./core/utils').relayEvents;
10
+ const maxWireVersion = require('./core/utils').maxWireVersion;
8
11
 
9
- var cursorOptionNames = ['maxAwaitTimeMS', 'collation', 'readPreference'];
12
+ const CHANGE_STREAM_OPTIONS = ['resumeAfter', 'startAfter', 'startAtOperationTime', 'fullDocument'];
13
+ const CURSOR_OPTIONS = ['batchSize', 'maxAwaitTimeMS', 'collation', 'readPreference'].concat(
14
+ CHANGE_STREAM_OPTIONS
15
+ );
10
16
 
11
17
  const CHANGE_DOMAIN_TYPES = {
12
18
  COLLECTION: Symbol('Collection'),
@@ -14,26 +20,45 @@ const CHANGE_DOMAIN_TYPES = {
14
20
  CLUSTER: Symbol('Cluster')
15
21
  };
16
22
 
23
+ /**
24
+ * @typedef ResumeToken
25
+ * @description Represents the logical starting point for a new or resuming {@link ChangeStream} on the server.
26
+ * @see https://docs.mongodb.com/master/changeStreams/#change-stream-resume-token
27
+ */
28
+
29
+ /**
30
+ * @typedef OperationTime
31
+ * @description Represents a specific point in time on a server. Can be retrieved by using {@link Db#command}
32
+ * @see https://docs.mongodb.com/manual/reference/method/db.runCommand/#response
33
+ */
34
+
35
+ /**
36
+ * @typedef ChangeStreamOptions
37
+ * @description Options that can be passed to a ChangeStream. Note that startAfter, resumeAfter, and startAtOperationTime are all mutually exclusive, and the server will error if more than one is specified.
38
+ * @property {string} [fullDocument='default'] Allowed values: ‘default’, ‘updateLookup’. When set to ‘updateLookup’, the change stream will include both a delta describing the changes to the document, as well as a copy of the entire document that was changed from some time after the change occurred.
39
+ * @property {number} [maxAwaitTimeMS] The maximum amount of time for the server to wait on new documents to satisfy a change stream query.
40
+ * @property {ResumeToken} [resumeAfter] Allows you to start a changeStream after a specified event. See {@link https://docs.mongodb.com/master/changeStreams/#resumeafter-for-change-streams|ChangeStream documentation}.
41
+ * @property {ResumeToken} [startAfter] Similar to resumeAfter, but will allow you to start after an invalidated event. See {@link https://docs.mongodb.com/master/changeStreams/#startafter-for-change-streams|ChangeStream documentation}.
42
+ * @property {OperationTime} [startAtOperationTime] Will start the changeStream after the specified operationTime.
43
+ * @property {number} [batchSize] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
44
+ * @property {object} [collation] Specify collation settings for operation. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
45
+ * @property {ReadPreference} [readPreference] The read preference. Defaults to the read preference of the database or collection. See {@link https://docs.mongodb.com/manual/reference/read-preference|read preference documentation}.
46
+ */
47
+
17
48
  /**
18
49
  * Creates a new Change Stream instance. Normally created using {@link Collection#watch|Collection.watch()}.
19
50
  * @class ChangeStream
20
51
  * @since 3.0.0
21
52
  * @param {(MongoClient|Db|Collection)} changeDomain The domain against which to create the change stream
22
53
  * @param {Array} pipeline An array of {@link https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/|aggregation pipeline stages} through which to pass change stream documents
23
- * @param {object} [options] Optional settings
24
- * @param {string} [options.fullDocument='default'] Allowed values: ‘default’, ‘updateLookup’. When set to ‘updateLookup’, the change stream will include both a delta describing the changes to the document, as well as a copy of the entire document that was changed from some time after the change occurred.
25
- * @param {number} [options.maxAwaitTimeMS] The maximum amount of time for the server to wait on new documents to satisfy a change stream query
26
- * @param {object} [options.resumeAfter] Specifies the logical starting point for the new change stream. This should be the _id field from a previously returned change stream document.
27
- * @param {number} [options.batchSize] The number of documents to return per batch. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
28
- * @param {object} [options.collation] Specify collation settings for operation. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
29
- * @param {ReadPreference} [options.readPreference] The read preference. Defaults to the read preference of the database or collection. See {@link https://docs.mongodb.com/manual/reference/read-preference|read preference documentation}.
54
+ * @param {ChangeStreamOptions} [options] Optional settings
30
55
  * @fires ChangeStream#close
31
56
  * @fires ChangeStream#change
32
57
  * @fires ChangeStream#end
33
58
  * @fires ChangeStream#error
59
+ * @fires ChangeStream#resumeTokenChanged
34
60
  * @return {ChangeStream} a ChangeStream instance.
35
61
  */
36
-
37
62
  class ChangeStream extends EventEmitter {
38
63
  constructor(changeDomain, pipeline, options) {
39
64
  super();
@@ -69,16 +94,8 @@ class ChangeStream extends EventEmitter {
69
94
  this.options.readPreference = changeDomain.s.readPreference;
70
95
  }
71
96
 
72
- // We need to get the operationTime as early as possible
73
- const isMaster = this.topology.lastIsMaster();
74
- if (!isMaster) {
75
- throw new MongoError('Topology does not have an ismaster yet.');
76
- }
77
-
78
- this.operationTime = isMaster.operationTime;
79
-
80
97
  // Create contained Change Stream cursor
81
- this.cursor = createChangeStreamCursor(this);
98
+ this.cursor = createChangeStreamCursor(this, options);
82
99
 
83
100
  // Listen for any `change` listeners being added to ChangeStream
84
101
  this.on('newListener', eventName => {
@@ -97,6 +114,15 @@ class ChangeStream extends EventEmitter {
97
114
  });
98
115
  }
99
116
 
117
+ /**
118
+ * @property {ResumeToken} resumeToken
119
+ * The cached resume token that will be used to resume
120
+ * after the most recently returned change.
121
+ */
122
+ get resumeToken() {
123
+ return this.cursor.resumeToken;
124
+ }
125
+
100
126
  /**
101
127
  * Check if there is any document still available in the Change Stream
102
128
  * @function ChangeStream.prototype.hasNext
@@ -215,13 +241,114 @@ class ChangeStream extends EventEmitter {
215
241
  }
216
242
  }
217
243
 
218
- // Create a new change stream cursor based on self's configuration
219
- var createChangeStreamCursor = function(self) {
220
- if (self.resumeToken) {
221
- self.options.resumeAfter = self.resumeToken;
244
+ class ChangeStreamCursor extends Cursor {
245
+ constructor(topology, ns, cmd, options) {
246
+ // TODO: spread will help a lot here
247
+ super(topology, ns, cmd, options);
248
+
249
+ options = options || {};
250
+ this._resumeToken = null;
251
+ this.startAtOperationTime = options.startAtOperationTime;
252
+
253
+ if (options.startAfter) {
254
+ this.resumeToken = options.startAfter;
255
+ } else if (options.resumeAfter) {
256
+ this.resumeToken = options.resumeAfter;
257
+ }
258
+ }
259
+
260
+ set resumeToken(token) {
261
+ this._resumeToken = token;
262
+ this.emit('resumeTokenChanged', token);
263
+ }
264
+
265
+ get resumeToken() {
266
+ return this._resumeToken;
267
+ }
268
+
269
+ get resumeOptions() {
270
+ const result = {};
271
+ for (const optionName of CURSOR_OPTIONS) {
272
+ if (this.options[optionName]) result[optionName] = this.options[optionName];
273
+ }
274
+
275
+ if (this.resumeToken || this.startAtOperationTime) {
276
+ ['resumeAfter', 'startAfter', 'startAtOperationTime'].forEach(key => delete result[key]);
277
+
278
+ if (this.resumeToken) {
279
+ result.resumeAfter = this.resumeToken;
280
+ } else if (this.startAtOperationTime && maxWireVersion(this.server) >= 7) {
281
+ result.startAtOperationTime = this.startAtOperationTime;
282
+ }
283
+ }
284
+
285
+ return result;
222
286
  }
223
287
 
224
- var changeStreamCursor = buildChangeStreamAggregationCommand(self);
288
+ _initializeCursor(callback) {
289
+ super._initializeCursor((err, result) => {
290
+ if (err) {
291
+ callback(err, null);
292
+ return;
293
+ }
294
+
295
+ const response = result.documents[0];
296
+
297
+ if (
298
+ this.startAtOperationTime == null &&
299
+ this.resumeAfter == null &&
300
+ this.startAfter == null &&
301
+ maxWireVersion(this.server) >= 7
302
+ ) {
303
+ this.startAtOperationTime = response.operationTime;
304
+ }
305
+
306
+ const cursor = response.cursor;
307
+ if (cursor.postBatchResumeToken) {
308
+ this.cursorState.postBatchResumeToken = cursor.postBatchResumeToken;
309
+
310
+ if (cursor.firstBatch.length === 0) {
311
+ this.resumeToken = cursor.postBatchResumeToken;
312
+ }
313
+ }
314
+
315
+ this.emit('response');
316
+ callback(err, result);
317
+ });
318
+ }
319
+
320
+ _getMore(callback) {
321
+ super._getMore((err, response) => {
322
+ if (err) {
323
+ callback(err, null);
324
+ return;
325
+ }
326
+
327
+ const cursor = response.cursor;
328
+ if (cursor.postBatchResumeToken) {
329
+ this.cursorState.postBatchResumeToken = cursor.postBatchResumeToken;
330
+
331
+ if (cursor.nextBatch.length === 0) {
332
+ this.resumeToken = cursor.postBatchResumeToken;
333
+ }
334
+ }
335
+
336
+ this.emit('response');
337
+ callback(err, response);
338
+ });
339
+ }
340
+ }
341
+
342
+ /**
343
+ * @event ChangeStreamCursor#response
344
+ * internal event DO NOT USE
345
+ * @ignore
346
+ */
347
+
348
+ // Create a new change stream cursor based on self's configuration
349
+ var createChangeStreamCursor = function(self, options) {
350
+ const changeStreamCursor = buildChangeStreamAggregationCommand(self, options);
351
+ relayEvents(changeStreamCursor, self, ['resumeTokenChanged', 'end', 'close']);
225
352
 
226
353
  /**
227
354
  * Fired for each new matching change in the specified namespace. Attaching a `change`
@@ -243,9 +370,6 @@ var createChangeStreamCursor = function(self) {
243
370
  * @event ChangeStream#close
244
371
  * @type {null}
245
372
  */
246
- changeStreamCursor.on('close', function() {
247
- self.emit('close');
248
- });
249
373
 
250
374
  /**
251
375
  * Change stream end event
@@ -253,9 +377,13 @@ var createChangeStreamCursor = function(self) {
253
377
  * @event ChangeStream#end
254
378
  * @type {null}
255
379
  */
256
- changeStreamCursor.on('end', function() {
257
- self.emit('end');
258
- });
380
+
381
+ /**
382
+ * Emitted each time the change stream stores a new resume token.
383
+ *
384
+ * @event ChangeStream#resumeTokenChanged
385
+ * @type {ResumeToken}
386
+ */
259
387
 
260
388
  /**
261
389
  * Fired when the stream encounters an error.
@@ -277,44 +405,26 @@ var createChangeStreamCursor = function(self) {
277
405
  return changeStreamCursor;
278
406
  };
279
407
 
280
- function getResumeToken(self) {
281
- return self.resumeToken || self.options.resumeAfter;
282
- }
283
-
284
- function getStartAtOperationTime(self) {
285
- const isMaster = self.topology.lastIsMaster() || {};
286
- return (
287
- isMaster.maxWireVersion && isMaster.maxWireVersion >= 7 && self.options.startAtOperationTime
288
- );
408
+ function applyKnownOptions(target, source, optionNames) {
409
+ optionNames.forEach(name => {
410
+ if (source[name]) {
411
+ target[name] = source[name];
412
+ }
413
+ });
289
414
  }
290
415
 
291
- var buildChangeStreamAggregationCommand = function(self) {
416
+ var buildChangeStreamAggregationCommand = function(self, options) {
417
+ options = options || {};
292
418
  const topology = self.topology;
293
419
  const namespace = self.namespace;
294
420
  const pipeline = self.pipeline;
295
- const options = self.options;
296
421
 
297
- var changeStreamStageOptions = {
298
- fullDocument: options.fullDocument || 'default'
299
- };
300
-
301
- const resumeToken = getResumeToken(self);
302
- const startAtOperationTime = getStartAtOperationTime(self);
303
- if (resumeToken) {
304
- changeStreamStageOptions.resumeAfter = resumeToken;
305
- }
306
-
307
- if (startAtOperationTime) {
308
- changeStreamStageOptions.startAtOperationTime = startAtOperationTime;
309
- }
422
+ const changeStreamStageOptions = { fullDocument: options.fullDocument || 'default' };
423
+ applyKnownOptions(changeStreamStageOptions, options, CHANGE_STREAM_OPTIONS);
310
424
 
311
425
  // Map cursor options
312
- var cursorOptions = {};
313
- cursorOptionNames.forEach(function(optionName) {
314
- if (options[optionName]) {
315
- cursorOptions[optionName] = options[optionName];
316
- }
317
- });
426
+ const cursorOptions = { cursorFactory: ChangeStreamCursor };
427
+ applyKnownOptions(cursorOptions, options, CURSOR_OPTIONS);
318
428
 
319
429
  if (self.type === CHANGE_DOMAIN_TYPES.CLUSTER) {
320
430
  changeStreamStageOptions.allChangesForCluster = true;
@@ -377,6 +487,7 @@ function processNewChange(args) {
377
487
  : changeStream.promiseLibrary.reject(error);
378
488
  }
379
489
 
490
+ const cursor = changeStream.cursor;
380
491
  const topology = changeStream.topology;
381
492
  const options = changeStream.cursor.options;
382
493
 
@@ -384,11 +495,6 @@ function processNewChange(args) {
384
495
  if (isResumableError(error) && !changeStream.attemptingResume) {
385
496
  changeStream.attemptingResume = true;
386
497
 
387
- if (!(getResumeToken(changeStream) || getStartAtOperationTime(changeStream))) {
388
- const startAtOperationTime = changeStream.cursor.cursorState.operationTime;
389
- changeStream.options = Object.assign({ startAtOperationTime }, changeStream.options);
390
- }
391
-
392
498
  // stop listening to all events from old cursor
393
499
  ['data', 'close', 'end', 'error'].forEach(event =>
394
500
  changeStream.cursor.removeAllListeners(event)
@@ -401,7 +507,7 @@ function processNewChange(args) {
401
507
  if (eventEmitter) {
402
508
  waitForTopologyConnected(topology, { readPreference: options.readPreference }, err => {
403
509
  if (err) return changeStream.emit('error', err);
404
- changeStream.cursor = createChangeStreamCursor(changeStream);
510
+ changeStream.cursor = createChangeStreamCursor(changeStream, cursor.resumeOptions);
405
511
  });
406
512
 
407
513
  return;
@@ -411,7 +517,7 @@ function processNewChange(args) {
411
517
  waitForTopologyConnected(topology, { readPreference: options.readPreference }, err => {
412
518
  if (err) return callback(err, null);
413
519
 
414
- changeStream.cursor = createChangeStreamCursor(changeStream);
520
+ changeStream.cursor = createChangeStreamCursor(changeStream, cursor.resumeOptions);
415
521
  changeStream.next(callback);
416
522
  });
417
523
 
@@ -424,7 +530,9 @@ function processNewChange(args) {
424
530
  resolve();
425
531
  });
426
532
  })
427
- .then(() => (changeStream.cursor = createChangeStreamCursor(changeStream)))
533
+ .then(
534
+ () => (changeStream.cursor = createChangeStreamCursor(changeStream, cursor.resumeOptions))
535
+ )
428
536
  .then(() => changeStream.next());
429
537
  }
430
538
 
@@ -435,9 +543,8 @@ function processNewChange(args) {
435
543
 
436
544
  changeStream.attemptingResume = false;
437
545
 
438
- // Cache the resume token if it is present. If it is not present return an error.
439
- if (!change || !change._id) {
440
- var noResumeTokenError = new Error(
546
+ if (change && !change._id) {
547
+ const noResumeTokenError = new Error(
441
548
  'A change stream document has been received that lacks a resume token (_id).'
442
549
  );
443
550
 
@@ -446,7 +553,12 @@ function processNewChange(args) {
446
553
  return changeStream.promiseLibrary.reject(noResumeTokenError);
447
554
  }
448
555
 
449
- changeStream.resumeToken = change._id;
556
+ // cache the resume token
557
+ if (cursor.bufferedCount() === 0 && cursor.cursorState.postBatchResumeToken) {
558
+ cursor.resumeToken = cursor.cursorState.postBatchResumeToken;
559
+ } else {
560
+ cursor.resumeToken = change._id;
561
+ }
450
562
 
451
563
  // Return the change
452
564
  if (eventEmitter) return changeStream.emit('change', change);
package/lib/collection.js CHANGED
@@ -526,6 +526,8 @@ Collection.prototype.insertMany = function(docs, options, callback) {
526
526
  *
527
527
  * { updateMany: { filter: {a:2}, update: {$set: {a:2}}, upsert:true } }
528
528
  *
529
+ * { updateMany: { filter: {}, update: {$set: {"a.$[i].x": 5}}, arrayFilters: [{ "i.x": 5 }]} }
530
+ *
529
531
  * { deleteOne: { filter: {c:1} } }
530
532
  *
531
533
  * { deleteMany: { filter: {c:1} } }
@@ -539,6 +541,7 @@ Collection.prototype.insertMany = function(docs, options, callback) {
539
541
  * @method
540
542
  * @param {object[]} operations Bulk operations to perform.
541
543
  * @param {object} [options] Optional settings.
544
+ * @param {object[]} [options.arrayFilters] Determines which array elements to modify for update operation in MongoDB 3.6 or higher.
542
545
  * @param {(number|string)} [options.w] The write concern.
543
546
  * @param {number} [options.wtimeout] The write concern timeout.
544
547
  * @param {boolean} [options.j=false] Specify a journal write concern.
@@ -686,15 +689,7 @@ Collection.prototype.updateOne = function(filter, update, options, callback) {
686
689
  if (typeof options === 'function') (callback = options), (options = {});
687
690
  options = options || {};
688
691
 
689
- let err;
690
- if (Array.isArray(update)) {
691
- for (let i = 0; !err && i < update.length; i++) {
692
- err = checkForAtomicOperators(update[i]);
693
- }
694
- } else {
695
- err = checkForAtomicOperators(update);
696
- }
697
-
692
+ const err = checkForAtomicOperators(update);
698
693
  if (err) {
699
694
  if (typeof callback === 'function') return callback(err);
700
695
  return this.s.promiseLibrary.reject(err);
@@ -1056,6 +1051,10 @@ Collection.prototype.rename = function(newName, options, callback) {
1056
1051
  *
1057
1052
  * @method
1058
1053
  * @param {object} [options] Optional settings.
1054
+ * @param {WriteConcern} [options.writeConcern] A full WriteConcern object
1055
+ * @param {(number|string)} [options.w] The write concern
1056
+ * @param {number} [options.wtimeout] The write concern timeout
1057
+ * @param {boolean} [options.j] The journal write concern
1059
1058
  * @param {ClientSession} [options.session] optional session to use for this operation
1060
1059
  * @param {Collection~resultCallback} [callback] The results callback
1061
1060
  * @return {Promise} returns Promise if no callback passed
@@ -55,10 +55,12 @@ const MongoDBNamespace = require('./utils').MongoDBNamespace;
55
55
  * @fires CommandCursor#readable
56
56
  * @return {CommandCursor} an CommandCursor instance.
57
57
  */
58
- var CommandCursor = function(bson, ns, cmd, options, topology, topologyOptions) {
58
+ var CommandCursor = function(topology, ns, cmd, options) {
59
59
  CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
60
60
  var state = CommandCursor.INIT;
61
61
  var streamOptions = {};
62
+ const bson = topology.s.bson;
63
+ const topologyOptions = topology.s.options;
62
64
 
63
65
  // MaxTimeMS
64
66
  var maxTimeMS = null;
@@ -150,14 +152,14 @@ var methodsToInherit = [
150
152
  'kill',
151
153
  'setCursorBatchSize',
152
154
  '_find',
153
- '_getmore',
155
+ '_initializeCursor',
156
+ '_getMore',
154
157
  '_killcursor',
155
158
  'isDead',
156
159
  'explain',
157
160
  'isNotified',
158
161
  'isKilled',
159
- '_endSession',
160
- '_initImplicitSession'
162
+ '_endSession'
161
163
  ];
162
164
 
163
165
  // Only inherit the types we need
@@ -278,7 +278,7 @@ function makeConnection(family, options, _callback) {
278
278
  socket.setTimeout(connectionTimeout);
279
279
  socket.setNoDelay(noDelay);
280
280
 
281
- const errorEvents = ['error', 'close', 'timeout', 'parseError', 'connect'];
281
+ const errorEvents = ['error', 'close', 'timeout', 'parseError'];
282
282
  function errorHandler(eventName) {
283
283
  return err => {
284
284
  errorEvents.forEach(event => socket.removeAllListeners(event));
@@ -19,6 +19,7 @@ const apm = require('./apm');
19
19
  const Buffer = require('safe-buffer').Buffer;
20
20
  const connect = require('./connect');
21
21
  const updateSessionFromResponse = require('../sessions').updateSessionFromResponse;
22
+ const eachAsync = require('../utils').eachAsync;
22
23
 
23
24
  var DISCONNECTED = 'disconnected';
24
25
  var CONNECTING = 'connecting';
@@ -26,6 +27,15 @@ var CONNECTED = 'connected';
26
27
  var DESTROYING = 'destroying';
27
28
  var DESTROYED = 'destroyed';
28
29
 
30
+ const CONNECTION_EVENTS = new Set([
31
+ 'error',
32
+ 'close',
33
+ 'timeout',
34
+ 'parseError',
35
+ 'connect',
36
+ 'message'
37
+ ]);
38
+
29
39
  var _id = 0;
30
40
 
31
41
  /**
@@ -198,6 +208,19 @@ Object.defineProperty(Pool.prototype, 'socketTimeout', {
198
208
  }
199
209
  });
200
210
 
211
+ // clears all pool state
212
+ function resetPoolState(pool) {
213
+ pool.inUseConnections = [];
214
+ pool.availableConnections = [];
215
+ pool.connectingConnections = 0;
216
+ pool.executing = false;
217
+ pool.reconnectConnection = null;
218
+ pool.numberOfConsecutiveTimeouts = 0;
219
+ pool.connectionIndex = 0;
220
+ pool.retriesLeft = pool.options.reconnectTries;
221
+ pool.reconnectId = null;
222
+ }
223
+
201
224
  function stateTransition(self, newState) {
202
225
  var legalTransitions = {
203
226
  disconnected: [CONNECTING, DESTROYING, DISCONNECTED],
@@ -631,43 +654,30 @@ Pool.prototype.unref = function() {
631
654
  });
632
655
  };
633
656
 
634
- // Events
635
- var events = ['error', 'close', 'timeout', 'parseError', 'connect', 'message'];
636
-
637
657
  // Destroy the connections
638
658
  function destroy(self, connections, options, callback) {
639
- let connectionCount = connections.length;
640
- function connectionDestroyed() {
641
- connectionCount--;
642
- if (connectionCount > 0) {
643
- return;
644
- }
645
-
646
- // Zero out all connections
647
- self.inUseConnections = [];
648
- self.availableConnections = [];
649
- self.connectingConnections = 0;
659
+ eachAsync(
660
+ connections,
661
+ (conn, cb) => {
662
+ for (const eventName of CONNECTION_EVENTS) {
663
+ conn.removeAllListeners(eventName);
664
+ }
650
665
 
651
- // Set state to destroyed
652
- stateTransition(self, DESTROYED);
653
- if (typeof callback === 'function') {
654
- callback(null, null);
655
- }
656
- }
666
+ conn.destroy(options, cb);
667
+ },
668
+ err => {
669
+ if (err) {
670
+ if (typeof callback === 'function') callback(err, null);
671
+ return;
672
+ }
657
673
 
658
- if (connectionCount === 0) {
659
- connectionDestroyed();
660
- return;
661
- }
674
+ resetPoolState(self);
675
+ self.queue = [];
662
676
 
663
- // Destroy all connections
664
- connections.forEach(conn => {
665
- for (var i = 0; i < events.length; i++) {
666
- conn.removeAllListeners(events[i]);
677
+ stateTransition(self, DESTROYED);
678
+ if (typeof callback === 'function') callback(null, null);
667
679
  }
668
-
669
- conn.destroy(options, connectionDestroyed);
670
- });
680
+ );
671
681
  }
672
682
 
673
683
  /**
@@ -751,19 +761,34 @@ Pool.prototype.destroy = function(force, callback) {
751
761
  * @param {function} [callback]
752
762
  */
753
763
  Pool.prototype.reset = function(callback) {
754
- // this.destroy(true, err => {
755
- // if (err && typeof callback === 'function') {
756
- // callback(err, null);
757
- // return;
758
- // }
764
+ const connections = this.availableConnections.concat(this.inUseConnections);
765
+ eachAsync(
766
+ connections,
767
+ (conn, cb) => {
768
+ for (const eventName of CONNECTION_EVENTS) {
769
+ conn.removeAllListeners(eventName);
770
+ }
771
+
772
+ conn.destroy({ force: true }, cb);
773
+ },
774
+ err => {
775
+ if (err) {
776
+ if (typeof callback === 'function') {
777
+ callback(err, null);
778
+ return;
779
+ }
780
+ }
759
781
 
760
- // stateTransition(this, DISCONNECTED);
761
- // this.connect();
782
+ resetPoolState(this);
762
783
 
763
- // if (typeof callback === 'function') callback(null, null);
764
- // });
784
+ // create an initial connection, and kick off execution again
785
+ _createConnection(this);
765
786
 
766
- if (typeof callback === 'function') callback();
787
+ if (typeof callback === 'function') {
788
+ callback(null, null);
789
+ }
790
+ }
791
+ );
767
792
  };
768
793
 
769
794
  // Prepare the buffer that Pool.prototype.write() uses to send to the server