mongodb 3.1.13 → 3.2.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.
@@ -0,0 +1,121 @@
1
+ 'use strict';
2
+
3
+ const AggregationCursor = require('../aggregation_cursor');
4
+ const applyWriteConcern = require('../utils').applyWriteConcern;
5
+ const decorateWithCollation = require('../utils').decorateWithCollation;
6
+ const decorateWithReadConcern = require('../utils').decorateWithReadConcern;
7
+ const handleCallback = require('../utils').handleCallback;
8
+ const MongoError = require('mongodb-core').MongoError;
9
+ const resolveReadPreference = require('../utils').resolveReadPreference;
10
+ const toError = require('../utils').toError;
11
+
12
+ const DB_AGGREGATE_COLLECTION = 1;
13
+
14
+ /**
15
+ * Perform an aggregate operation. See Collection.prototype.aggregate or Db.prototype.aggregate for more information.
16
+ *
17
+ * @method
18
+ * @param {Db} db A Db instance.
19
+ * @param {Collection|string} coll A collection instance or the string '1', used for db.aggregate.
20
+ * @param {object} [pipeline=[]] Array containing all the aggregation framework commands for the execution.
21
+ * @param {object} [options] Optional settings. See Collection.prototype.aggregate or Db.prototype.aggregate for a list of options.
22
+ * @param {Db~aggregationCallback|Collection~aggregationCallback} callback The command result callback
23
+ */
24
+ function aggregate(db, coll, pipeline, options, callback) {
25
+ const isDbAggregate = typeof coll === 'string';
26
+ const target = isDbAggregate ? db : coll;
27
+ const topology = target.s.topology;
28
+ let hasOutStage = false;
29
+
30
+ if (typeof options.out === 'string') {
31
+ pipeline = pipeline.concat({ $out: options.out });
32
+ hasOutStage = true;
33
+ } else if (pipeline.length > 0 && pipeline[pipeline.length - 1]['$out']) {
34
+ hasOutStage = true;
35
+ }
36
+
37
+ let command;
38
+ let namespace;
39
+ let optionSources;
40
+
41
+ if (isDbAggregate) {
42
+ command = { aggregate: DB_AGGREGATE_COLLECTION, pipeline: pipeline };
43
+ namespace = `${db.s.databaseName}.${DB_AGGREGATE_COLLECTION}`;
44
+
45
+ optionSources = { db };
46
+ } else {
47
+ command = { aggregate: coll.s.name, pipeline: pipeline };
48
+ namespace = coll.s.namespace;
49
+
50
+ optionSources = { db: coll.s.db, collection: coll };
51
+ }
52
+
53
+ const takesWriteConcern = topology.capabilities().commandsTakeWriteConcern;
54
+
55
+ if (!hasOutStage) {
56
+ decorateWithReadConcern(command, target, options);
57
+ }
58
+
59
+ if (pipeline.length > 0 && pipeline[pipeline.length - 1]['$out'] && takesWriteConcern) {
60
+ applyWriteConcern(command, optionSources, options);
61
+ }
62
+
63
+ try {
64
+ decorateWithCollation(command, target, options);
65
+ } catch (err) {
66
+ if (typeof callback === 'function') return callback(err, null);
67
+ throw err;
68
+ }
69
+
70
+ if (options.bypassDocumentValidation === true) {
71
+ command.bypassDocumentValidation = options.bypassDocumentValidation;
72
+ }
73
+
74
+ if (typeof options.allowDiskUse === 'boolean') command.allowDiskUse = options.allowDiskUse;
75
+ if (typeof options.maxTimeMS === 'number') command.maxTimeMS = options.maxTimeMS;
76
+
77
+ if (options.hint) command.hint = options.hint;
78
+
79
+ options = Object.assign({}, options);
80
+
81
+ // Ensure we have the right read preference inheritance
82
+ options.readPreference = resolveReadPreference(options, optionSources);
83
+
84
+ if (options.explain) {
85
+ if (command.readConcern || command.writeConcern) {
86
+ throw toError('"explain" cannot be used on an aggregate call with readConcern/writeConcern');
87
+ }
88
+ command.explain = options.explain;
89
+ }
90
+
91
+ if (typeof options.comment === 'string') command.comment = options.comment;
92
+
93
+ // Validate that cursor options is valid
94
+ if (options.cursor != null && typeof options.cursor !== 'object') {
95
+ throw toError('cursor options must be an object');
96
+ }
97
+
98
+ options.cursor = options.cursor || {};
99
+ if (options.batchSize && !hasOutStage) options.cursor.batchSize = options.batchSize;
100
+ command.cursor = options.cursor;
101
+
102
+ // promiseLibrary
103
+ options.promiseLibrary = target.s.promiseLibrary;
104
+
105
+ // Set the AggregationCursor constructor
106
+ options.cursorFactory = AggregationCursor;
107
+
108
+ if (typeof callback !== 'function') {
109
+ if (!topology.capabilities()) {
110
+ throw new MongoError('cannot connect to server');
111
+ }
112
+
113
+ return topology.cursor(namespace, command, options);
114
+ }
115
+
116
+ return handleCallback(callback, null, topology.cursor(namespace, command, options));
117
+ }
118
+
119
+ module.exports = {
120
+ aggregate
121
+ };
@@ -225,7 +225,7 @@ function countDocuments(coll, query, options, callback) {
225
225
  pipeline.push({ $limit: limit });
226
226
  }
227
227
 
228
- pipeline.push({ $group: { _id: null, n: { $sum: 1 } } });
228
+ pipeline.push({ $group: { _id: 1, n: { $sum: 1 } } });
229
229
 
230
230
  delete options.limit;
231
231
  delete options.skip;
@@ -892,6 +892,57 @@ function insertOne(coll, doc, options, callback) {
892
892
  });
893
893
  }
894
894
 
895
+ /**
896
+ * Inserts an array of documents into MongoDB. If documents passed in do not contain the **_id** field,
897
+ * one will be added to each of the documents missing it by the driver, mutating the document. This behavior
898
+ * can be overridden by setting the **forceServerObjectId** flag.
899
+ *
900
+ * @method
901
+ * @param {Collection} a Collection instance.
902
+ * @param {object[]} docs Documents to insert.
903
+ * @param {number} [options] Optional settings. See Collection.prototype.insertMany for a list of options.
904
+ * @param {Collection~insertWriteOpCallback} [callback] The command result callback
905
+ */
906
+ function insertMany(coll, docs, options, callback) {
907
+ if (!Array.isArray(docs)) {
908
+ return callback(
909
+ MongoError.create({ message: 'docs parameter must be an array of documents', driver: true })
910
+ );
911
+ }
912
+
913
+ // If keep going set unordered
914
+ options['serializeFunctions'] = options['serializeFunctions'] || coll.s.serializeFunctions;
915
+
916
+ docs = prepareDocs(coll, docs, options);
917
+
918
+ // Generate the bulk write operations
919
+ const operations = [
920
+ {
921
+ insertMany: docs
922
+ }
923
+ ];
924
+
925
+ bulkWrite(coll, operations, options, (err, result) => {
926
+ if (err) return callback(err, null);
927
+ callback(null, mapInsertManyResults(docs, result));
928
+ });
929
+ }
930
+
931
+ function mapInsertManyResults(docs, r) {
932
+ const finalResult = {
933
+ result: { ok: 1, n: r.insertedCount },
934
+ ops: docs,
935
+ insertedCount: r.insertedCount,
936
+ insertedIds: r.insertedIds
937
+ };
938
+
939
+ if (r.getLastOp()) {
940
+ finalResult.result.opTime = r.getLastOp();
941
+ }
942
+
943
+ return finalResult;
944
+ }
945
+
895
946
  /**
896
947
  * Determine whether the collection is a capped collection.
897
948
  *
@@ -1476,6 +1527,7 @@ module.exports = {
1476
1527
  indexes,
1477
1528
  indexExists,
1478
1529
  indexInformation,
1530
+ insertMany,
1479
1531
  insertOne,
1480
1532
  isCapped,
1481
1533
  mapReduce,
@@ -97,10 +97,10 @@ function addUser(db, username, password, options, callback) {
97
97
  const userPassword = md5.digest('hex');
98
98
 
99
99
  // If we have another db set
100
- const db = options.dbName ? new Db(options.dbName, db.s.topology, db.s.options) : db;
100
+ const dbToUse = options.dbName ? new Db(options.dbName, db.s.topology, db.s.options) : db;
101
101
 
102
102
  // Fetch a user collection
103
- const collection = db.collection(CONSTANTS.SYSTEM_USER_COLLECTION);
103
+ const collection = dbToUse.collection(CONSTANTS.SYSTEM_USER_COLLECTION);
104
104
 
105
105
  // Check if we are inserting the first user
106
106
  count(collection, {}, finalOptions, (err, count) => {
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const authenticate = require('../authenticate');
4
3
  const deprecate = require('util').deprecate;
5
4
  const Logger = require('mongodb-core').Logger;
6
5
  const MongoError = require('mongodb-core').MongoError;
@@ -10,6 +9,8 @@ const ReadPreference = require('mongodb-core').ReadPreference;
10
9
  const ReplSet = require('../topologies/replset');
11
10
  const Server = require('../topologies/server');
12
11
  const ServerSessionPool = require('mongodb-core').Sessions.ServerSessionPool;
12
+ const NativeTopology = require('../topologies/native_topology');
13
+ const MongoCredentials = require('mongodb-core').MongoCredentials;
13
14
 
14
15
  let client;
15
16
  function loadClient() {
@@ -108,7 +109,10 @@ const validOptionNames = [
108
109
  'minSize',
109
110
  'monitorCommands',
110
111
  'retryWrites',
111
- 'useNewUrlParser'
112
+ 'useNewUrlParser',
113
+ 'useUnifiedTopology',
114
+ 'serverSelectionTimeoutMS',
115
+ 'useRecoveryToken'
112
116
  ];
113
117
 
114
118
  function addListeners(mongoClient, topology) {
@@ -125,7 +129,10 @@ function addListeners(mongoClient, topology) {
125
129
 
126
130
  function assignTopology(client, topology) {
127
131
  client.topology = topology;
128
- topology.s.sessionPool = new ServerSessionPool(topology.s.coreTopology);
132
+ topology.s.sessionPool =
133
+ topology instanceof NativeTopology
134
+ ? new ServerSessionPool(topology)
135
+ : new ServerSessionPool(topology.s.coreTopology);
129
136
  }
130
137
 
131
138
  // Clear out all events
@@ -174,7 +181,7 @@ function connect(mongoClient, url, options, callback) {
174
181
  throw new Error('no callback function provided');
175
182
  }
176
183
 
177
- // Get a logger for MongoClient
184
+ let didRequestAuthentication = false;
178
185
  const logger = Logger('MongoClient', options);
179
186
 
180
187
  // Did we pass in a Server/ReplSet/Mongos
@@ -211,29 +218,34 @@ function connect(mongoClient, url, options, callback) {
211
218
  return callback(new Error('connection string must contain at least one seed host'));
212
219
  }
213
220
 
221
+ if (_finalOptions.auth && !_finalOptions.credentials) {
222
+ try {
223
+ didRequestAuthentication = true;
224
+ _finalOptions.credentials = generateCredentials(
225
+ mongoClient,
226
+ _finalOptions.auth.user,
227
+ _finalOptions.auth.password,
228
+ _finalOptions
229
+ );
230
+ } catch (err) {
231
+ return callback(err);
232
+ }
233
+ }
234
+
235
+ if (_finalOptions.useUnifiedTopology) {
236
+ return createTopology(mongoClient, 'unified', _finalOptions, connectCallback);
237
+ }
238
+
214
239
  // Do we have a replicaset then skip discovery and go straight to connectivity
215
240
  if (_finalOptions.replicaSet || _finalOptions.rs_name) {
216
- return createTopology(
217
- mongoClient,
218
- 'replicaset',
219
- _finalOptions,
220
- connectHandler(mongoClient, _finalOptions, connectCallback)
221
- );
241
+ return createTopology(mongoClient, 'replicaset', _finalOptions, connectCallback);
222
242
  } else if (object.servers.length > 1) {
223
- return createTopology(
224
- mongoClient,
225
- 'mongos',
226
- _finalOptions,
227
- connectHandler(mongoClient, _finalOptions, connectCallback)
228
- );
243
+ return createTopology(mongoClient, 'mongos', _finalOptions, connectCallback);
229
244
  } else {
230
- return createServer(
231
- mongoClient,
232
- _finalOptions,
233
- connectHandler(mongoClient, _finalOptions, connectCallback)
234
- );
245
+ return createServer(mongoClient, _finalOptions, connectCallback);
235
246
  }
236
247
  });
248
+
237
249
  function connectCallback(err, topology) {
238
250
  const warningMessage = `seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name`;
239
251
  if (err && err.message === 'no mongos proxies found in seed list') {
@@ -245,35 +257,15 @@ function connect(mongoClient, url, options, callback) {
245
257
  return callback(new MongoError(warningMessage));
246
258
  }
247
259
 
260
+ if (didRequestAuthentication) {
261
+ mongoClient.emit('authenticated', null, true);
262
+ }
263
+
248
264
  // Return the error and db instance
249
265
  callback(err, topology);
250
266
  }
251
267
  }
252
268
 
253
- function connectHandler(client, options, callback) {
254
- return (err, topology) => {
255
- if (err) {
256
- return handleConnectCallback(err, topology, callback);
257
- }
258
-
259
- // No authentication just reconnect
260
- if (!options.auth) {
261
- return handleConnectCallback(err, topology, callback);
262
- }
263
-
264
- // Authenticate
265
- authenticate(client, options.user, options.password, options, (err, success) => {
266
- if (success) {
267
- handleConnectCallback(null, topology, callback);
268
- } else {
269
- if (topology) topology.close();
270
- const authError = err ? err : new Error('Could not authenticate user ' + options.auth[0]);
271
- handleConnectCallback(authError, topology, callback);
272
- }
273
- });
274
- };
275
- }
276
-
277
269
  /**
278
270
  * Connect to MongoDB using a url as documented at
279
271
  *
@@ -314,27 +306,21 @@ function connectWithUrl(mongoClient, url, options, connectCallback) {
314
306
  );
315
307
  }
316
308
 
317
- // Connect
318
- return url.connect(
319
- finalOptions,
320
- connectHandler(mongoClient, finalOptions, (err, topology) => {
321
- if (err) return connectCallback(err, topology);
322
- if (finalOptions.user || finalOptions.password || finalOptions.authMechanism) {
323
- return authenticate(
324
- mongoClient,
325
- finalOptions.user,
326
- finalOptions.password,
327
- finalOptions,
328
- err => {
329
- if (err) return connectCallback(err, topology);
330
- connectCallback(err, topology);
331
- }
332
- );
333
- }
309
+ const isDoingAuth = finalOptions.user || finalOptions.password || finalOptions.authMechanism;
310
+ if (isDoingAuth && !finalOptions.credentials) {
311
+ try {
312
+ finalOptions.credentials = generateCredentials(
313
+ mongoClient,
314
+ finalOptions.user,
315
+ finalOptions.password,
316
+ finalOptions
317
+ );
318
+ } catch (err) {
319
+ return connectCallback(err, url);
320
+ }
321
+ }
334
322
 
335
- connectCallback(err, topology);
336
- })
337
- );
323
+ return url.connect(finalOptions, connectCallback);
338
324
  }
339
325
 
340
326
  function createListener(mongoClient, event) {
@@ -410,6 +396,8 @@ function createTopology(mongoClient, topologyType, options, callback) {
410
396
  topology = new Mongos(servers, options);
411
397
  } else if (topologyType === 'replicaset') {
412
398
  topology = new ReplSet(servers, options);
399
+ } else if (topologyType === 'unified') {
400
+ topology = new NativeTopology(options.servers, options);
413
401
  }
414
402
 
415
403
  // Add listeners
@@ -465,36 +453,10 @@ function createUnifiedOptions(finalOptions, options) {
465
453
  return finalOptions;
466
454
  }
467
455
 
468
- function handleConnectCallback(err, topology, callback) {
469
- return process.nextTick(() => {
470
- try {
471
- callback(err, topology);
472
- } catch (err) {
473
- if (topology) topology.close();
474
- throw err;
475
- }
476
- });
477
- }
478
-
479
456
  function legacyTransformUrlOptions(object) {
480
457
  return mergeOptions(createUnifiedOptions({}, object), object, false);
481
458
  }
482
459
 
483
- /**
484
- * Logout user from server, fire off on all connections and remove all auth info.
485
- *
486
- * @method
487
- * @param {MongoClient} mongoClient The MongoClient instance on which to logout.
488
- * @param {object} [options] Optional settings. See MongoClient.prototype.logout for a list of options.
489
- * @param {Db~resultCallback} [callback] The command result callback
490
- */
491
- function logout(mongoClient, dbName, callback) {
492
- mongoClient.topology.logout(dbName, err => {
493
- if (err) return callback(err);
494
- callback(null, true);
495
- });
496
- }
497
-
498
460
  function mergeOptions(target, source, flatten) {
499
461
  for (const name in source) {
500
462
  if (source[name] && typeof source[name] === 'object' && flatten) {
@@ -556,21 +518,17 @@ function transformUrlOptions(_object) {
556
518
  object[camelCaseName] = object[name];
557
519
  }
558
520
  }
559
- if (_object.auth) {
560
- const auth = _object.auth;
561
- for (let i in auth) {
562
- if (auth[i]) {
563
- object[i] = auth[i];
564
- }
565
- }
566
521
 
567
- if (auth.username) {
568
- object.auth = auth;
569
- object.user = auth.username;
522
+ const hasUsername = _object.auth && _object.auth.username;
523
+ const hasAuthMechanism = _object.options && _object.options.authMechanism;
524
+ if (hasUsername || hasAuthMechanism) {
525
+ object.auth = Object.assign({}, _object.auth);
526
+ if (object.auth.db) {
527
+ object.authSource = object.authSource || object.auth.db;
570
528
  }
571
529
 
572
- if (auth.db) {
573
- object.authSource = object.authSource || auth.db;
530
+ if (object.auth.username) {
531
+ object.auth.user = object.auth.username;
574
532
  }
575
533
  }
576
534
 
@@ -636,10 +594,12 @@ function validOptions(options) {
636
594
  continue;
637
595
  }
638
596
 
639
- if (_validOptions.indexOf(name) === -1 && options.validateOptions) {
640
- return new MongoError(`option ${name} is not supported`);
641
- } else if (_validOptions.indexOf(name) === -1) {
642
- console.warn(`the options [${name}] is not supported`);
597
+ if (_validOptions.indexOf(name) === -1) {
598
+ if (options.validateOptions) {
599
+ return new MongoError(`option ${name} is not supported`);
600
+ } else {
601
+ console.warn(`the options [${name}] is not supported`);
602
+ }
643
603
  }
644
604
 
645
605
  if (legacyOptionNames.indexOf(name) !== -1) {
@@ -651,4 +611,78 @@ function validOptions(options) {
651
611
  }
652
612
  }
653
613
 
654
- module.exports = { connectOp, logout, validOptions };
614
+ const VALID_AUTH_MECHANISMS = new Set([
615
+ 'DEFAULT',
616
+ 'MONGODB-CR',
617
+ 'PLAIN',
618
+ 'MONGODB-X509',
619
+ 'SCRAM-SHA-1',
620
+ 'SCRAM-SHA-256',
621
+ 'GSSAPI'
622
+ ]);
623
+
624
+ const AUTH_MECHANISM_INTERNAL_MAP = {
625
+ DEFAULT: 'default',
626
+ 'MONGODB-CR': 'mongocr',
627
+ PLAIN: 'plain',
628
+ 'MONGODB-X509': 'x509',
629
+ 'SCRAM-SHA-1': 'scram-sha-1',
630
+ 'SCRAM-SHA-256': 'scram-sha-256'
631
+ };
632
+
633
+ function generateCredentials(client, username, password, options) {
634
+ options = Object.assign({}, options);
635
+
636
+ // the default db to authenticate against is 'self'
637
+ // if authententicate is called from a retry context, it may be another one, like admin
638
+ const source = options.authSource || options.authdb || options.dbName;
639
+
640
+ // authMechanism
641
+ const authMechanismRaw = options.authMechanism || 'DEFAULT';
642
+ const authMechanism = authMechanismRaw.toUpperCase();
643
+
644
+ if (!VALID_AUTH_MECHANISMS.has(authMechanism)) {
645
+ throw MongoError.create({
646
+ message: `authentication mechanism ${authMechanismRaw} not supported', options.authMechanism`,
647
+ driver: true
648
+ });
649
+ }
650
+
651
+ if (authMechanism === 'GSSAPI') {
652
+ return new MongoCredentials({
653
+ mechanism: process.platform === 'win32' ? 'sspi' : 'gssapi',
654
+ mechanismProperties: options,
655
+ source,
656
+ username,
657
+ password
658
+ });
659
+ }
660
+
661
+ return new MongoCredentials({
662
+ mechanism: AUTH_MECHANISM_INTERNAL_MAP[authMechanism],
663
+ source,
664
+ username,
665
+ password
666
+ });
667
+ }
668
+
669
+ function closeOperation(client, force, callback) {
670
+ const completeClose = err => {
671
+ client.emit('close', client);
672
+ for (const name in client.s.dbCache) {
673
+ client.s.dbCache[name].emit('close', client);
674
+ }
675
+
676
+ client.removeAllListeners('close');
677
+ callback(err, null);
678
+ };
679
+
680
+ if (client.topology == null) {
681
+ completeClose();
682
+ return;
683
+ }
684
+
685
+ client.topology.close(force, completeClose);
686
+ }
687
+
688
+ module.exports = { connectOp, validOptions, closeOperation };
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ const Topology = require('mongodb-core').Topology;
4
+ const ServerCapabilities = require('./topology_base').ServerCapabilities;
5
+ const Cursor = require('../cursor');
6
+ const translateOptions = require('../utils').translateOptions;
7
+
8
+ class NativeTopology extends Topology {
9
+ constructor(servers, options) {
10
+ options = options || {};
11
+
12
+ let clonedOptions = Object.assign(
13
+ {},
14
+ {
15
+ cursorFactory: Cursor,
16
+ reconnect: false,
17
+ emitError: options.emitError,
18
+ size: options.poolSize,
19
+ monitorCommands: options.monitorCommands
20
+ }
21
+ );
22
+
23
+ // Translate any SSL options and other connectivity options
24
+ clonedOptions = translateOptions(clonedOptions, options);
25
+
26
+ // Socket options
27
+ var socketOptions =
28
+ options.socketOptions && Object.keys(options.socketOptions).length > 0
29
+ ? options.socketOptions
30
+ : options;
31
+
32
+ // Translate all the options to the mongodb-core ones
33
+ clonedOptions = translateOptions(clonedOptions, socketOptions);
34
+
35
+ super(servers, clonedOptions);
36
+
37
+ // Do we have an application specific string
38
+ if (options.appname) {
39
+ this.s.clientInfo.application = { name: options.appname };
40
+ }
41
+ }
42
+
43
+ capabilities() {
44
+ if (this.s.sCapabilities) return this.s.sCapabilities;
45
+ if (this.lastIsMaster() == null) return null;
46
+ this.s.sCapabilities = new ServerCapabilities(this.lastIsMaster());
47
+ return this.s.sCapabilities;
48
+ }
49
+ }
50
+
51
+ module.exports = NativeTopology;
@@ -381,10 +381,9 @@ class ReplSet extends TopologyBase {
381
381
  self.s.coreTopology.connect(_options);
382
382
  }
383
383
 
384
- close(forceClosed) {
385
- super.close(forceClosed);
386
-
384
+ close(forceClosed, callback) {
387
385
  ['timeout', 'error', 'close', 'joined', 'left'].forEach(e => this.removeAllListeners(e));
386
+ super.close(forceClosed, callback);
388
387
  }
389
388
  }
390
389
 
@@ -370,16 +370,6 @@ class TopologyBase extends EventEmitter {
370
370
  return this.s.coreTopology.unref();
371
371
  }
372
372
 
373
- auth() {
374
- var args = Array.prototype.slice.call(arguments, 0);
375
- this.s.coreTopology.auth.apply(this.s.coreTopology, args);
376
- }
377
-
378
- logout() {
379
- var args = Array.prototype.slice.call(arguments, 0);
380
- this.s.coreTopology.logout.apply(this.s.coreTopology, args);
381
- }
382
-
383
373
  /**
384
374
  * All raw connections
385
375
  * @method
@@ -389,7 +379,7 @@ class TopologyBase extends EventEmitter {
389
379
  return this.s.coreTopology.connections();
390
380
  }
391
381
 
392
- close(forceClosed) {
382
+ close(forceClosed, callback) {
393
383
  // If we have sessions, we want to individually move them to the session pool,
394
384
  // and then send a single endSessions call.
395
385
  if (this.s.sessions.length) {
@@ -400,15 +390,18 @@ class TopologyBase extends EventEmitter {
400
390
  this.s.sessionPool.endAllPooledSessions();
401
391
  }
402
392
 
403
- this.s.coreTopology.destroy({
404
- force: typeof forceClosed === 'boolean' ? forceClosed : false
405
- });
406
-
407
393
  // We need to wash out all stored processes
408
394
  if (forceClosed === true) {
409
395
  this.s.storeOptions.force = forceClosed;
410
396
  this.s.store.flush();
411
397
  }
398
+
399
+ this.s.coreTopology.destroy(
400
+ {
401
+ force: typeof forceClosed === 'boolean' ? forceClosed : false
402
+ },
403
+ callback
404
+ );
412
405
  }
413
406
  }
414
407