mongodb 3.2.5 → 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.
Files changed (133) hide show
  1. package/HISTORY.md +0 -10
  2. package/index.js +4 -4
  3. package/lib/admin.js +56 -56
  4. package/lib/aggregation_cursor.js +7 -3
  5. package/lib/bulk/common.js +18 -13
  6. package/lib/change_stream.js +196 -89
  7. package/lib/collection.js +217 -169
  8. package/lib/command_cursor.js +17 -7
  9. package/lib/core/auth/auth_provider.js +158 -0
  10. package/lib/core/auth/defaultAuthProviders.js +29 -0
  11. package/lib/core/auth/gssapi.js +241 -0
  12. package/lib/core/auth/mongo_credentials.js +81 -0
  13. package/lib/core/auth/mongocr.js +51 -0
  14. package/lib/core/auth/plain.js +35 -0
  15. package/lib/core/auth/scram.js +293 -0
  16. package/lib/core/auth/sspi.js +131 -0
  17. package/lib/core/auth/x509.js +26 -0
  18. package/lib/core/connection/apm.js +236 -0
  19. package/lib/core/connection/command_result.js +36 -0
  20. package/lib/core/connection/commands.js +507 -0
  21. package/lib/core/connection/connect.js +370 -0
  22. package/lib/core/connection/connection.js +624 -0
  23. package/lib/core/connection/logger.js +246 -0
  24. package/lib/core/connection/msg.js +219 -0
  25. package/lib/core/connection/pool.js +1285 -0
  26. package/lib/core/connection/utils.js +57 -0
  27. package/lib/core/cursor.js +752 -0
  28. package/lib/core/error.js +186 -0
  29. package/lib/core/index.js +50 -0
  30. package/lib/core/sdam/monitoring.js +228 -0
  31. package/lib/core/sdam/server.js +467 -0
  32. package/lib/core/sdam/server_description.js +163 -0
  33. package/lib/core/sdam/server_selectors.js +244 -0
  34. package/lib/core/sdam/srv_polling.js +135 -0
  35. package/lib/core/sdam/topology.js +1151 -0
  36. package/lib/core/sdam/topology_description.js +408 -0
  37. package/lib/core/sessions.js +711 -0
  38. package/lib/core/tools/smoke_plugin.js +61 -0
  39. package/lib/core/topologies/mongos.js +1337 -0
  40. package/lib/core/topologies/read_preference.js +202 -0
  41. package/lib/core/topologies/replset.js +1507 -0
  42. package/lib/core/topologies/replset_state.js +1121 -0
  43. package/lib/core/topologies/server.js +984 -0
  44. package/lib/core/topologies/shared.js +453 -0
  45. package/lib/core/transactions.js +167 -0
  46. package/lib/core/uri_parser.js +631 -0
  47. package/lib/core/utils.js +165 -0
  48. package/lib/core/wireprotocol/command.js +170 -0
  49. package/lib/core/wireprotocol/compression.js +73 -0
  50. package/lib/core/wireprotocol/constants.js +13 -0
  51. package/lib/core/wireprotocol/get_more.js +86 -0
  52. package/lib/core/wireprotocol/index.js +18 -0
  53. package/lib/core/wireprotocol/kill_cursors.js +70 -0
  54. package/lib/core/wireprotocol/query.js +224 -0
  55. package/lib/core/wireprotocol/shared.js +115 -0
  56. package/lib/core/wireprotocol/write_command.js +50 -0
  57. package/lib/cursor.js +40 -46
  58. package/lib/db.js +141 -95
  59. package/lib/dynamic_loaders.js +32 -0
  60. package/lib/error.js +12 -10
  61. package/lib/gridfs/chunk.js +2 -2
  62. package/lib/gridfs/grid_store.js +31 -25
  63. package/lib/gridfs-stream/index.js +4 -4
  64. package/lib/gridfs-stream/upload.js +1 -1
  65. package/lib/mongo_client.js +37 -15
  66. package/lib/operations/add_user.js +96 -0
  67. package/lib/operations/aggregate.js +24 -13
  68. package/lib/operations/aggregate_operation.js +127 -0
  69. package/lib/operations/bulk_write.js +104 -0
  70. package/lib/operations/close.js +47 -0
  71. package/lib/operations/collection_ops.js +28 -287
  72. package/lib/operations/collections.js +55 -0
  73. package/lib/operations/command.js +120 -0
  74. package/lib/operations/command_v2.js +43 -0
  75. package/lib/operations/common_functions.js +372 -0
  76. package/lib/operations/{mongo_client_ops.js → connect.js} +185 -157
  77. package/lib/operations/count.js +72 -0
  78. package/lib/operations/count_documents.js +46 -0
  79. package/lib/operations/create_collection.js +118 -0
  80. package/lib/operations/create_index.js +92 -0
  81. package/lib/operations/create_indexes.js +61 -0
  82. package/lib/operations/cursor_ops.js +3 -4
  83. package/lib/operations/db_ops.js +15 -12
  84. package/lib/operations/delete_many.js +25 -0
  85. package/lib/operations/delete_one.js +25 -0
  86. package/lib/operations/distinct.js +85 -0
  87. package/lib/operations/drop.js +53 -0
  88. package/lib/operations/drop_index.js +42 -0
  89. package/lib/operations/drop_indexes.js +23 -0
  90. package/lib/operations/estimated_document_count.js +33 -0
  91. package/lib/operations/execute_db_admin_command.js +34 -0
  92. package/lib/operations/execute_operation.js +165 -0
  93. package/lib/operations/explain.js +23 -0
  94. package/lib/operations/find_and_modify.js +98 -0
  95. package/lib/operations/find_one.js +33 -0
  96. package/lib/operations/find_one_and_delete.js +16 -0
  97. package/lib/operations/find_one_and_replace.js +18 -0
  98. package/lib/operations/find_one_and_update.js +19 -0
  99. package/lib/operations/geo_haystack_search.js +79 -0
  100. package/lib/operations/has_next.js +40 -0
  101. package/lib/operations/index_exists.js +39 -0
  102. package/lib/operations/index_information.js +23 -0
  103. package/lib/operations/indexes.js +22 -0
  104. package/lib/operations/insert_many.js +63 -0
  105. package/lib/operations/insert_one.js +75 -0
  106. package/lib/operations/is_capped.js +19 -0
  107. package/lib/operations/list_indexes.js +66 -0
  108. package/lib/operations/map_reduce.js +189 -0
  109. package/lib/operations/next.js +32 -0
  110. package/lib/operations/operation.js +63 -0
  111. package/lib/operations/options_operation.js +32 -0
  112. package/lib/operations/profiling_level.js +31 -0
  113. package/lib/operations/re_index.js +28 -0
  114. package/lib/operations/remove_user.js +52 -0
  115. package/lib/operations/rename.js +61 -0
  116. package/lib/operations/replace_one.js +47 -0
  117. package/lib/operations/set_profiling_level.js +48 -0
  118. package/lib/operations/stats.js +45 -0
  119. package/lib/operations/to_array.js +68 -0
  120. package/lib/operations/update_many.js +29 -0
  121. package/lib/operations/update_one.js +44 -0
  122. package/lib/operations/validate_collection.js +40 -0
  123. package/lib/read_concern.js +55 -0
  124. package/lib/topologies/mongos.js +3 -3
  125. package/lib/topologies/native_topology.js +22 -2
  126. package/lib/topologies/replset.js +3 -3
  127. package/lib/topologies/server.js +4 -4
  128. package/lib/topologies/topology_base.js +6 -6
  129. package/lib/url_parser.js +4 -3
  130. package/lib/utils.js +46 -59
  131. package/lib/write_concern.js +66 -0
  132. package/package.json +15 -6
  133. package/lib/.DS_Store +0 -0
@@ -0,0 +1,1151 @@
1
+ 'use strict';
2
+ const EventEmitter = require('events');
3
+ const ServerDescription = require('./server_description').ServerDescription;
4
+ const ServerType = require('./server_description').ServerType;
5
+ const TopologyDescription = require('./topology_description').TopologyDescription;
6
+ const TopologyType = require('./topology_description').TopologyType;
7
+ const monitoring = require('./monitoring');
8
+ const calculateDurationInMs = require('../utils').calculateDurationInMs;
9
+ const MongoTimeoutError = require('../error').MongoTimeoutError;
10
+ const Server = require('./server');
11
+ const relayEvents = require('../utils').relayEvents;
12
+ const ReadPreference = require('../topologies/read_preference');
13
+ const readPreferenceServerSelector = require('./server_selectors').readPreferenceServerSelector;
14
+ const writableServerSelector = require('./server_selectors').writableServerSelector;
15
+ const isRetryableWritesSupported = require('../topologies/shared').isRetryableWritesSupported;
16
+ const Cursor = require('../cursor');
17
+ const deprecate = require('util').deprecate;
18
+ const BSON = require('../connection/utils').retrieveBSON();
19
+ const createCompressionInfo = require('../topologies/shared').createCompressionInfo;
20
+ const isRetryableError = require('../error').isRetryableError;
21
+ const MongoParseError = require('../error').MongoParseError;
22
+ const isSDAMUnrecoverableError = require('../error').isSDAMUnrecoverableError;
23
+ const ClientSession = require('../sessions').ClientSession;
24
+ const createClientInfo = require('../topologies/shared').createClientInfo;
25
+ const MongoError = require('../error').MongoError;
26
+ const resolveClusterTime = require('../topologies/shared').resolveClusterTime;
27
+ const SrvPoller = require('./srv_polling').SrvPoller;
28
+
29
+ // Global state
30
+ let globalTopologyCounter = 0;
31
+
32
+ // Constants
33
+ const TOPOLOGY_DEFAULTS = {
34
+ localThresholdMS: 15,
35
+ serverSelectionTimeoutMS: 10000,
36
+ heartbeatFrequencyMS: 30000,
37
+ minHeartbeatFrequencyMS: 500
38
+ };
39
+
40
+ // events that we relay to the `Topology`
41
+ const SERVER_RELAY_EVENTS = [
42
+ 'serverHeartbeatStarted',
43
+ 'serverHeartbeatSucceeded',
44
+ 'serverHeartbeatFailed',
45
+ 'commandStarted',
46
+ 'commandSucceeded',
47
+ 'commandFailed',
48
+
49
+ // NOTE: Legacy events
50
+ 'monitoring'
51
+ ];
52
+
53
+ // all events we listen to from `Server` instances
54
+ const LOCAL_SERVER_EVENTS = SERVER_RELAY_EVENTS.concat([
55
+ 'error',
56
+ 'connect',
57
+ 'descriptionReceived',
58
+ 'close',
59
+ 'ended'
60
+ ]);
61
+
62
+ /**
63
+ * A container of server instances representing a connection to a MongoDB topology.
64
+ *
65
+ * @fires Topology#serverOpening
66
+ * @fires Topology#serverClosed
67
+ * @fires Topology#serverDescriptionChanged
68
+ * @fires Topology#topologyOpening
69
+ * @fires Topology#topologyClosed
70
+ * @fires Topology#topologyDescriptionChanged
71
+ * @fires Topology#serverHeartbeatStarted
72
+ * @fires Topology#serverHeartbeatSucceeded
73
+ * @fires Topology#serverHeartbeatFailed
74
+ */
75
+ class Topology extends EventEmitter {
76
+ /**
77
+ * Create a topology
78
+ *
79
+ * @param {Array|String} [seedlist] a string list, or array of Server instances to connect to
80
+ * @param {Object} [options] Optional settings
81
+ * @param {Number} [options.localThresholdMS=15] The size of the latency window for selecting among multiple suitable servers
82
+ * @param {Number} [options.serverSelectionTimeoutMS=30000] How long to block for server selection before throwing an error
83
+ * @param {Number} [options.heartbeatFrequencyMS=10000] The frequency with which topology updates are scheduled
84
+ */
85
+ constructor(seedlist, options) {
86
+ super();
87
+ if (typeof options === 'undefined' && typeof seedlist !== 'string') {
88
+ options = seedlist;
89
+ seedlist = [];
90
+
91
+ // this is for legacy single server constructor support
92
+ if (options.host) {
93
+ seedlist.push({ host: options.host, port: options.port });
94
+ }
95
+ }
96
+
97
+ seedlist = seedlist || [];
98
+ if (typeof seedlist === 'string') {
99
+ seedlist = parseStringSeedlist(seedlist);
100
+ }
101
+
102
+ options = Object.assign({}, TOPOLOGY_DEFAULTS, options);
103
+
104
+ const topologyType = topologyTypeFromSeedlist(seedlist, options);
105
+ const topologyId = globalTopologyCounter++;
106
+ const serverDescriptions = seedlist.reduce((result, seed) => {
107
+ if (seed.domain_socket) seed.host = seed.domain_socket;
108
+ const address = seed.port ? `${seed.host}:${seed.port}` : `${seed.host}:27017`;
109
+ result.set(address, new ServerDescription(address));
110
+ return result;
111
+ }, new Map());
112
+
113
+ this.s = {
114
+ // the id of this topology
115
+ id: topologyId,
116
+ // passed in options
117
+ options,
118
+ // initial seedlist of servers to connect to
119
+ seedlist: seedlist,
120
+ // the topology description
121
+ description: new TopologyDescription(
122
+ topologyType,
123
+ serverDescriptions,
124
+ options.replicaSet,
125
+ null,
126
+ null,
127
+ null,
128
+ options
129
+ ),
130
+ serverSelectionTimeoutMS: options.serverSelectionTimeoutMS,
131
+ heartbeatFrequencyMS: options.heartbeatFrequencyMS,
132
+ minHeartbeatIntervalMS: options.minHeartbeatIntervalMS,
133
+ // allow users to override the cursor factory
134
+ Cursor: options.cursorFactory || Cursor,
135
+ // the bson parser
136
+ bson: options.bson || new BSON(),
137
+ // a map of server instances to normalized addresses
138
+ servers: new Map(),
139
+ // Server Session Pool
140
+ sessionPool: null,
141
+ // Active client sessions
142
+ sessions: [],
143
+ // Promise library
144
+ promiseLibrary: options.promiseLibrary || Promise,
145
+ credentials: options.credentials,
146
+ clusterTime: null,
147
+
148
+ // timer management
149
+ monitorTimers: [],
150
+ iterationTimers: []
151
+ };
152
+
153
+ // amend options for server instance creation
154
+ this.s.options.compression = { compressors: createCompressionInfo(options) };
155
+
156
+ // add client info
157
+ this.s.clientInfo = createClientInfo(options);
158
+
159
+ if (options.srvHost) {
160
+ this.s.srvPoller =
161
+ options.srvPoller ||
162
+ new SrvPoller({
163
+ heartbeatFrequencyMS: this.s.heartbeatFrequencyMS,
164
+ srvHost: options.srvHost, // TODO: GET THIS
165
+ logger: options.logger,
166
+ loggerLevel: options.loggerLevel
167
+ });
168
+ this.s.detectTopologyDescriptionChange = ev => {
169
+ const previousType = ev.previousDescription.type;
170
+ const newType = ev.newDescription.type;
171
+
172
+ if (previousType !== TopologyType.Sharded && newType === TopologyType.Sharded) {
173
+ this.s.handleSrvPolling = srvPollingHandler(this);
174
+ this.s.srvPoller.on('srvRecordDiscovery', this.s.handleSrvPolling);
175
+ this.s.srvPoller.start();
176
+ }
177
+ };
178
+
179
+ this.on('topologyDescriptionChanged', this.s.detectTopologyDescriptionChange);
180
+ }
181
+ }
182
+
183
+ /**
184
+ * @return A `TopologyDescription` for this topology
185
+ */
186
+ get description() {
187
+ return this.s.description;
188
+ }
189
+
190
+ get parserType() {
191
+ return BSON.native ? 'c++' : 'js';
192
+ }
193
+
194
+ /**
195
+ * All raw connections
196
+ * @method
197
+ * @return {Connection[]}
198
+ */
199
+ connections() {
200
+ return Array.from(this.s.servers.values()).reduce((result, server) => {
201
+ return result.concat(server.s.pool.allConnections());
202
+ }, []);
203
+ }
204
+
205
+ /**
206
+ * Initiate server connect
207
+ *
208
+ * @param {Object} [options] Optional settings
209
+ * @param {Array} [options.auth=null] Array of auth options to apply on connect
210
+ * @param {function} [callback] An optional callback called once on the first connected server
211
+ */
212
+ connect(options, callback) {
213
+ if (typeof options === 'function') (callback = options), (options = {});
214
+ options = options || {};
215
+
216
+ // emit SDAM monitoring events
217
+ this.emit('topologyOpening', new monitoring.TopologyOpeningEvent(this.s.id));
218
+
219
+ // emit an event for the topology change
220
+ this.emit(
221
+ 'topologyDescriptionChanged',
222
+ new monitoring.TopologyDescriptionChangedEvent(
223
+ this.s.id,
224
+ new TopologyDescription(TopologyType.Unknown), // initial is always Unknown
225
+ this.s.description
226
+ )
227
+ );
228
+
229
+ connectServers(this, Array.from(this.s.description.servers.values()));
230
+ this.s.connected = true;
231
+
232
+ // otherwise, wait for a server to properly connect based on user provided read preference,
233
+ // or primary.
234
+
235
+ translateReadPreference(options);
236
+ const readPreference = options.readPreference || ReadPreference.primary;
237
+
238
+ this.selectServer(readPreferenceServerSelector(readPreference), options, (err, server) => {
239
+ if (err) {
240
+ if (typeof callback === 'function') {
241
+ callback(err, null);
242
+ } else {
243
+ this.emit('error', err);
244
+ }
245
+
246
+ return;
247
+ }
248
+
249
+ const errorHandler = err => {
250
+ server.removeListener('connect', connectHandler);
251
+ if (typeof callback === 'function') callback(err, null);
252
+ };
253
+
254
+ const connectHandler = (_, err) => {
255
+ server.removeListener('error', errorHandler);
256
+ this.emit('open', err, this);
257
+ this.emit('connect', this);
258
+
259
+ if (typeof callback === 'function') callback(err, this);
260
+ };
261
+
262
+ const STATE_CONNECTING = 1;
263
+ if (server.s.state === STATE_CONNECTING) {
264
+ server.once('error', errorHandler);
265
+ server.once('connect', connectHandler);
266
+ return;
267
+ }
268
+
269
+ connectHandler();
270
+ });
271
+ }
272
+
273
+ /**
274
+ * Close this topology
275
+ */
276
+ close(options, callback) {
277
+ if (typeof options === 'function') (callback = options), (options = {});
278
+ options = options || {};
279
+
280
+ // clear all existing monitor timers
281
+ this.s.monitorTimers.map(timer => clearTimeout(timer));
282
+ this.s.monitorTimers = [];
283
+
284
+ this.s.iterationTimers.map(timer => clearTimeout(timer));
285
+ this.s.iterationTimers = [];
286
+
287
+ if (this.s.sessionPool) {
288
+ this.s.sessions.forEach(session => session.endSession());
289
+ this.s.sessionPool.endAllPooledSessions();
290
+ }
291
+
292
+ if (this.s.srvPoller) {
293
+ this.s.srvPoller.stop();
294
+ if (this.s.handleSrvPolling) {
295
+ this.s.srvPoller.removeListener('srvRecordDiscovery', this.s.handleSrvPolling);
296
+ delete this.s.handleSrvPolling;
297
+ }
298
+ }
299
+
300
+ if (this.s.detectTopologyDescriptionChange) {
301
+ this.removeListener('topologyDescriptionChanged', this.s.detectTopologyDescriptionChange);
302
+ delete this.s.detectTopologyDescriptionChange;
303
+ }
304
+
305
+ const servers = this.s.servers;
306
+ if (servers.size === 0) {
307
+ this.s.connected = false;
308
+ if (typeof callback === 'function') {
309
+ callback(null, null);
310
+ }
311
+
312
+ return;
313
+ }
314
+
315
+ // destroy all child servers
316
+ let destroyed = 0;
317
+ servers.forEach(server =>
318
+ destroyServer(server, this, () => {
319
+ destroyed++;
320
+ if (destroyed === servers.size) {
321
+ // emit an event for close
322
+ this.emit('topologyClosed', new monitoring.TopologyClosedEvent(this.s.id));
323
+
324
+ this.s.connected = false;
325
+ if (typeof callback === 'function') {
326
+ callback(null, null);
327
+ }
328
+ }
329
+ })
330
+ );
331
+ }
332
+
333
+ /**
334
+ * Selects a server according to the selection predicate provided
335
+ *
336
+ * @param {function} [selector] An optional selector to select servers by, defaults to a random selection within a latency window
337
+ * @param {object} [options] Optional settings related to server selection
338
+ * @param {number} [options.serverSelectionTimeoutMS] How long to block for server selection before throwing an error
339
+ * @param {function} callback The callback used to indicate success or failure
340
+ * @return {Server} An instance of a `Server` meeting the criteria of the predicate provided
341
+ */
342
+ selectServer(selector, options, callback) {
343
+ if (typeof options === 'function') {
344
+ callback = options;
345
+ if (typeof selector !== 'function') {
346
+ options = selector;
347
+
348
+ translateReadPreference(options);
349
+ const readPreference = options.readPreference || ReadPreference.primary;
350
+ selector = readPreferenceServerSelector(readPreference);
351
+ } else {
352
+ options = {};
353
+ }
354
+ }
355
+
356
+ options = Object.assign(
357
+ {},
358
+ { serverSelectionTimeoutMS: this.s.serverSelectionTimeoutMS },
359
+ options
360
+ );
361
+
362
+ const isSharded = this.description.type === TopologyType.Sharded;
363
+ const session = options.session;
364
+ const transaction = session && session.transaction;
365
+
366
+ if (isSharded && transaction && transaction.server) {
367
+ callback(null, transaction.server);
368
+ return;
369
+ }
370
+
371
+ // clear out any existing iteration timers
372
+ this.s.iterationTimers.map(timer => clearTimeout(timer));
373
+ this.s.iterationTimers = [];
374
+
375
+ selectServers(
376
+ this,
377
+ selector,
378
+ options.serverSelectionTimeoutMS,
379
+ process.hrtime(),
380
+ (err, servers) => {
381
+ if (err) return callback(err, null);
382
+
383
+ const selectedServer = randomSelection(servers);
384
+ if (isSharded && transaction && transaction.isActive) {
385
+ transaction.pinServer(selectedServer);
386
+ }
387
+
388
+ callback(null, selectedServer);
389
+ }
390
+ );
391
+ }
392
+
393
+ // Sessions related methods
394
+
395
+ /**
396
+ * @return Whether the topology should initiate selection to determine session support
397
+ */
398
+ shouldCheckForSessionSupport() {
399
+ return (
400
+ (this.description.type === TopologyType.Single && !this.description.hasKnownServers) ||
401
+ !this.description.hasDataBearingServers
402
+ );
403
+ }
404
+
405
+ /**
406
+ * @return Whether sessions are supported on the current topology
407
+ */
408
+ hasSessionSupport() {
409
+ return this.description.logicalSessionTimeoutMinutes != null;
410
+ }
411
+
412
+ /**
413
+ * Start a logical session
414
+ */
415
+ startSession(options, clientOptions) {
416
+ const session = new ClientSession(this, this.s.sessionPool, options, clientOptions);
417
+ session.once('ended', () => {
418
+ this.s.sessions = this.s.sessions.filter(s => !s.equals(session));
419
+ });
420
+
421
+ this.s.sessions.push(session);
422
+ return session;
423
+ }
424
+
425
+ /**
426
+ * Send endSessions command(s) with the given session ids
427
+ *
428
+ * @param {Array} sessions The sessions to end
429
+ * @param {function} [callback]
430
+ */
431
+ endSessions(sessions, callback) {
432
+ if (!Array.isArray(sessions)) {
433
+ sessions = [sessions];
434
+ }
435
+
436
+ this.command(
437
+ 'admin.$cmd',
438
+ { endSessions: sessions },
439
+ { readPreference: ReadPreference.primaryPreferred, noResponse: true },
440
+ () => {
441
+ // intentionally ignored, per spec
442
+ if (typeof callback === 'function') callback();
443
+ }
444
+ );
445
+ }
446
+
447
+ /**
448
+ * Update the internal TopologyDescription with a ServerDescription
449
+ *
450
+ * @param {object} serverDescription The server to update in the internal list of server descriptions
451
+ */
452
+ serverUpdateHandler(serverDescription) {
453
+ if (!this.s.description.hasServer(serverDescription.address)) {
454
+ return;
455
+ }
456
+
457
+ // these will be used for monitoring events later
458
+ const previousTopologyDescription = this.s.description;
459
+ const previousServerDescription = this.s.description.servers.get(serverDescription.address);
460
+
461
+ // first update the TopologyDescription
462
+ this.s.description = this.s.description.update(serverDescription);
463
+ if (this.s.description.compatibilityError) {
464
+ this.emit('error', new MongoError(this.s.description.compatibilityError));
465
+ return;
466
+ }
467
+
468
+ // emit monitoring events for this change
469
+ this.emit(
470
+ 'serverDescriptionChanged',
471
+ new monitoring.ServerDescriptionChangedEvent(
472
+ this.s.id,
473
+ serverDescription.address,
474
+ previousServerDescription,
475
+ this.s.description.servers.get(serverDescription.address)
476
+ )
477
+ );
478
+
479
+ // update server list from updated descriptions
480
+ updateServers(this, serverDescription);
481
+
482
+ // Driver Sessions Spec: "Whenever a driver receives a cluster time from
483
+ // a server it MUST compare it to the current highest seen cluster time
484
+ // for the deployment. If the new cluster time is higher than the
485
+ // highest seen cluster time it MUST become the new highest seen cluster
486
+ // time. Two cluster times are compared using only the BsonTimestamp
487
+ // value of the clusterTime embedded field."
488
+ const clusterTime = serverDescription.$clusterTime;
489
+ if (clusterTime) {
490
+ resolveClusterTime(this, clusterTime);
491
+ }
492
+
493
+ this.emit(
494
+ 'topologyDescriptionChanged',
495
+ new monitoring.TopologyDescriptionChangedEvent(
496
+ this.s.id,
497
+ previousTopologyDescription,
498
+ this.s.description
499
+ )
500
+ );
501
+ }
502
+
503
+ auth(credentials, callback) {
504
+ if (typeof credentials === 'function') (callback = credentials), (credentials = null);
505
+ if (typeof callback === 'function') callback(null, true);
506
+ }
507
+
508
+ logout(callback) {
509
+ if (typeof callback === 'function') callback(null, true);
510
+ }
511
+
512
+ // Basic operation support. Eventually this should be moved into command construction
513
+ // during the command refactor.
514
+
515
+ /**
516
+ * Insert one or more documents
517
+ *
518
+ * @param {String} ns The full qualified namespace for this operation
519
+ * @param {Array} ops An array of documents to insert
520
+ * @param {Boolean} [options.ordered=true] Execute in order or out of order
521
+ * @param {Object} [options.writeConcern] Write concern for the operation
522
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized
523
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields
524
+ * @param {ClientSession} [options.session] Session to use for the operation
525
+ * @param {boolean} [options.retryWrites] Enable retryable writes for this operation
526
+ * @param {opResultCallback} callback A callback function
527
+ */
528
+ insert(ns, ops, options, callback) {
529
+ executeWriteOperation({ topology: this, op: 'insert', ns, ops }, options, callback);
530
+ }
531
+
532
+ /**
533
+ * Perform one or more update operations
534
+ *
535
+ * @param {string} ns The fully qualified namespace for this operation
536
+ * @param {array} ops An array of updates
537
+ * @param {boolean} [options.ordered=true] Execute in order or out of order
538
+ * @param {object} [options.writeConcern] Write concern for the operation
539
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized
540
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields
541
+ * @param {ClientSession} [options.session] Session to use for the operation
542
+ * @param {boolean} [options.retryWrites] Enable retryable writes for this operation
543
+ * @param {opResultCallback} callback A callback function
544
+ */
545
+ update(ns, ops, options, callback) {
546
+ executeWriteOperation({ topology: this, op: 'update', ns, ops }, options, callback);
547
+ }
548
+
549
+ /**
550
+ * Perform one or more remove operations
551
+ *
552
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
553
+ * @param {array} ops An array of removes
554
+ * @param {boolean} [options.ordered=true] Execute in order or out of order
555
+ * @param {object} [options.writeConcern={}] Write concern for the operation
556
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
557
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
558
+ * @param {ClientSession} [options.session=null] Session to use for the operation
559
+ * @param {boolean} [options.retryWrites] Enable retryable writes for this operation
560
+ * @param {opResultCallback} callback A callback function
561
+ */
562
+ remove(ns, ops, options, callback) {
563
+ executeWriteOperation({ topology: this, op: 'remove', ns, ops }, options, callback);
564
+ }
565
+
566
+ /**
567
+ * Execute a command
568
+ *
569
+ * @method
570
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
571
+ * @param {object} cmd The command hash
572
+ * @param {ReadPreference} [options.readPreference] Specify read preference if command supports it
573
+ * @param {Connection} [options.connection] Specify connection object to execute command against
574
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
575
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
576
+ * @param {ClientSession} [options.session=null] Session to use for the operation
577
+ * @param {opResultCallback} callback A callback function
578
+ */
579
+ command(ns, cmd, options, callback) {
580
+ if (typeof options === 'function') {
581
+ (callback = options), (options = {}), (options = options || {});
582
+ }
583
+
584
+ translateReadPreference(options);
585
+ const readPreference = options.readPreference || ReadPreference.primary;
586
+
587
+ this.selectServer(readPreferenceServerSelector(readPreference), options, (err, server) => {
588
+ if (err) {
589
+ callback(err, null);
590
+ return;
591
+ }
592
+
593
+ const willRetryWrite =
594
+ !options.retrying &&
595
+ !!options.retryWrites &&
596
+ options.session &&
597
+ isRetryableWritesSupported(this) &&
598
+ !options.session.inTransaction() &&
599
+ isWriteCommand(cmd);
600
+
601
+ const cb = (err, result) => {
602
+ if (!err) return callback(null, result);
603
+ if (!isRetryableError(err)) {
604
+ return callback(err);
605
+ }
606
+
607
+ if (willRetryWrite) {
608
+ const newOptions = Object.assign({}, options, { retrying: true });
609
+ return this.command(ns, cmd, newOptions, callback);
610
+ }
611
+
612
+ return callback(err);
613
+ };
614
+
615
+ // increment and assign txnNumber
616
+ if (willRetryWrite) {
617
+ options.session.incrementTransactionNumber();
618
+ options.willRetryWrite = willRetryWrite;
619
+ }
620
+
621
+ server.command(ns, cmd, options, cb);
622
+ });
623
+ }
624
+
625
+ /**
626
+ * Create a new cursor
627
+ *
628
+ * @method
629
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
630
+ * @param {object|Long} cmd Can be either a command returning a cursor or a cursorId
631
+ * @param {object} [options] Options for the cursor
632
+ * @param {object} [options.batchSize=0] Batchsize for the operation
633
+ * @param {array} [options.documents=[]] Initial documents list for cursor
634
+ * @param {ReadPreference} [options.readPreference] Specify read preference if command supports it
635
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
636
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
637
+ * @param {ClientSession} [options.session=null] Session to use for the operation
638
+ * @param {object} [options.topology] The internal topology of the created cursor
639
+ * @returns {Cursor}
640
+ */
641
+ cursor(ns, cmd, options) {
642
+ options = options || {};
643
+ const topology = options.topology || this;
644
+ const CursorClass = options.cursorFactory || this.s.Cursor;
645
+ translateReadPreference(options);
646
+
647
+ return new CursorClass(topology, ns, cmd, options);
648
+ }
649
+
650
+ get clientInfo() {
651
+ return this.s.clientInfo;
652
+ }
653
+
654
+ // Legacy methods for compat with old topology types
655
+ isConnected() {
656
+ // console.log('not implemented: `isConnected`');
657
+ return true;
658
+ }
659
+
660
+ isDestroyed() {
661
+ // console.log('not implemented: `isDestroyed`');
662
+ return false;
663
+ }
664
+
665
+ unref() {
666
+ console.log('not implemented: `unref`');
667
+ }
668
+
669
+ // NOTE: There are many places in code where we explicitly check the last isMaster
670
+ // to do feature support detection. This should be done any other way, but for
671
+ // now we will just return the first isMaster seen, which should suffice.
672
+ lastIsMaster() {
673
+ const serverDescriptions = Array.from(this.description.servers.values());
674
+ if (serverDescriptions.length === 0) return {};
675
+
676
+ const sd = serverDescriptions.filter(sd => sd.type !== ServerType.Unknown)[0];
677
+ const result = sd || { maxWireVersion: this.description.commonWireVersion };
678
+ return result;
679
+ }
680
+
681
+ get logicalSessionTimeoutMinutes() {
682
+ return this.description.logicalSessionTimeoutMinutes;
683
+ }
684
+
685
+ get bson() {
686
+ return this.s.bson;
687
+ }
688
+ }
689
+
690
+ Object.defineProperty(Topology.prototype, 'clusterTime', {
691
+ enumerable: true,
692
+ get: function() {
693
+ return this.s.clusterTime;
694
+ },
695
+ set: function(clusterTime) {
696
+ this.s.clusterTime = clusterTime;
697
+ }
698
+ });
699
+
700
+ // legacy aliases
701
+ Topology.prototype.destroy = deprecate(
702
+ Topology.prototype.close,
703
+ 'destroy() is deprecated, please use close() instead'
704
+ );
705
+
706
+ const RETRYABLE_WRITE_OPERATIONS = ['findAndModify', 'insert', 'update', 'delete'];
707
+ function isWriteCommand(command) {
708
+ return RETRYABLE_WRITE_OPERATIONS.some(op => command[op]);
709
+ }
710
+
711
+ /**
712
+ * Destroys a server, and removes all event listeners from the instance
713
+ *
714
+ * @param {Server} server
715
+ */
716
+ function destroyServer(server, topology, callback) {
717
+ LOCAL_SERVER_EVENTS.forEach(event => server.removeAllListeners(event));
718
+
719
+ server.destroy(() => {
720
+ topology.emit(
721
+ 'serverClosed',
722
+ new monitoring.ServerClosedEvent(topology.s.id, server.description.address)
723
+ );
724
+
725
+ if (typeof callback === 'function') callback(null, null);
726
+ });
727
+ }
728
+
729
+ /**
730
+ * Parses a basic seedlist in string form
731
+ *
732
+ * @param {string} seedlist The seedlist to parse
733
+ */
734
+ function parseStringSeedlist(seedlist) {
735
+ return seedlist.split(',').map(seed => ({
736
+ host: seed.split(':')[0],
737
+ port: seed.split(':')[1] || 27017
738
+ }));
739
+ }
740
+
741
+ function topologyTypeFromSeedlist(seedlist, options) {
742
+ const replicaSet = options.replicaSet || options.setName || options.rs_name;
743
+ if (seedlist.length === 1 && !replicaSet) return TopologyType.Single;
744
+ if (replicaSet) return TopologyType.ReplicaSetNoPrimary;
745
+ return TopologyType.Unknown;
746
+ }
747
+
748
+ function randomSelection(array) {
749
+ return array[Math.floor(Math.random() * array.length)];
750
+ }
751
+
752
+ /**
753
+ * Selects servers using the provided selector
754
+ *
755
+ * @private
756
+ * @param {Topology} topology The topology to select servers from
757
+ * @param {function} selector The actual predicate used for selecting servers
758
+ * @param {Number} timeout The max time we are willing wait for selection
759
+ * @param {Number} start A high precision timestamp for the start of the selection process
760
+ * @param {function} callback The callback used to convey errors or the resultant servers
761
+ */
762
+ function selectServers(topology, selector, timeout, start, callback) {
763
+ const duration = calculateDurationInMs(start);
764
+ if (duration >= timeout) {
765
+ return callback(new MongoTimeoutError(`Server selection timed out after ${timeout} ms`));
766
+ }
767
+
768
+ // ensure we are connected
769
+ if (!topology.s.connected) {
770
+ topology.connect();
771
+
772
+ // we want to make sure we're still within the requested timeout window
773
+ const failToConnectTimer = setTimeout(() => {
774
+ topology.removeListener('connect', connectHandler);
775
+ callback(new MongoTimeoutError('Server selection timed out waiting to connect'));
776
+ }, timeout - duration);
777
+
778
+ const connectHandler = () => {
779
+ clearTimeout(failToConnectTimer);
780
+ selectServers(topology, selector, timeout, process.hrtime(), callback);
781
+ };
782
+
783
+ topology.once('connect', connectHandler);
784
+ return;
785
+ }
786
+
787
+ // otherwise, attempt server selection
788
+ const serverDescriptions = Array.from(topology.description.servers.values());
789
+ let descriptions;
790
+
791
+ // support server selection by options with readPreference
792
+ if (typeof selector === 'object') {
793
+ const readPreference = selector.readPreference
794
+ ? selector.readPreference
795
+ : ReadPreference.primary;
796
+
797
+ selector = readPreferenceServerSelector(readPreference);
798
+ }
799
+
800
+ try {
801
+ descriptions = selector
802
+ ? selector(topology.description, serverDescriptions)
803
+ : serverDescriptions;
804
+ } catch (e) {
805
+ return callback(e, null);
806
+ }
807
+
808
+ if (descriptions.length) {
809
+ const servers = descriptions.map(description => topology.s.servers.get(description.address));
810
+ return callback(null, servers);
811
+ }
812
+
813
+ const retrySelection = () => {
814
+ // clear all existing monitor timers
815
+ topology.s.monitorTimers.map(timer => clearTimeout(timer));
816
+ topology.s.monitorTimers = [];
817
+
818
+ // ensure all server monitors attempt monitoring soon
819
+ topology.s.servers.forEach(server => {
820
+ const timer = setTimeout(
821
+ () => server.monitor({ heartbeatFrequencyMS: topology.description.heartbeatFrequencyMS }),
822
+ TOPOLOGY_DEFAULTS.minHeartbeatFrequencyMS
823
+ );
824
+
825
+ topology.s.monitorTimers.push(timer);
826
+ });
827
+
828
+ const descriptionChangedHandler = () => {
829
+ // successful iteration, clear the check timer
830
+ clearTimeout(iterationTimer);
831
+ topology.s.iterationTimers.splice(timerIndex, 1);
832
+
833
+ if (topology.description.error) {
834
+ callback(topology.description.error, null);
835
+ return;
836
+ }
837
+
838
+ // topology description has changed due to monitoring, reattempt server selection
839
+ selectServers(topology, selector, timeout, start, callback);
840
+ };
841
+
842
+ const iterationTimer = setTimeout(() => {
843
+ topology.removeListener('topologyDescriptionChanged', descriptionChangedHandler);
844
+ callback(new MongoTimeoutError(`Server selection timed out after ${timeout} ms`));
845
+ }, timeout - duration);
846
+
847
+ // track this timer in case we need to clean it up outside this loop
848
+ const timerIndex = topology.s.iterationTimers.push(iterationTimer);
849
+
850
+ topology.once('topologyDescriptionChanged', descriptionChangedHandler);
851
+ };
852
+
853
+ retrySelection();
854
+ }
855
+
856
+ function createAndConnectServer(topology, serverDescription) {
857
+ topology.emit(
858
+ 'serverOpening',
859
+ new monitoring.ServerOpeningEvent(topology.s.id, serverDescription.address)
860
+ );
861
+
862
+ const server = new Server(serverDescription, topology.s.options, topology);
863
+ relayEvents(server, topology, SERVER_RELAY_EVENTS);
864
+
865
+ server.once('connect', serverConnectEventHandler(server, topology));
866
+ server.on('descriptionReceived', topology.serverUpdateHandler.bind(topology));
867
+ server.on('error', serverErrorEventHandler(server, topology));
868
+ server.on('close', () => topology.emit('close', server));
869
+ server.connect();
870
+ return server;
871
+ }
872
+
873
+ /**
874
+ * Create `Server` instances for all initially known servers, connect them, and assign
875
+ * them to the passed in `Topology`.
876
+ *
877
+ * @param {Topology} topology The topology responsible for the servers
878
+ * @param {ServerDescription[]} serverDescriptions A list of server descriptions to connect
879
+ */
880
+ function connectServers(topology, serverDescriptions) {
881
+ topology.s.servers = serverDescriptions.reduce((servers, serverDescription) => {
882
+ const server = createAndConnectServer(topology, serverDescription);
883
+ servers.set(serverDescription.address, server);
884
+ return servers;
885
+ }, new Map());
886
+ }
887
+
888
+ function updateServers(topology, incomingServerDescription) {
889
+ // update the internal server's description
890
+ if (incomingServerDescription && topology.s.servers.has(incomingServerDescription.address)) {
891
+ const server = topology.s.servers.get(incomingServerDescription.address);
892
+ server.s.description = incomingServerDescription;
893
+ }
894
+
895
+ // add new servers for all descriptions we currently don't know about locally
896
+ for (const serverDescription of topology.description.servers.values()) {
897
+ if (!topology.s.servers.has(serverDescription.address)) {
898
+ const server = createAndConnectServer(topology, serverDescription);
899
+ topology.s.servers.set(serverDescription.address, server);
900
+ }
901
+ }
902
+
903
+ // for all servers no longer known, remove their descriptions and destroy their instances
904
+ for (const entry of topology.s.servers) {
905
+ const serverAddress = entry[0];
906
+ if (topology.description.hasServer(serverAddress)) {
907
+ continue;
908
+ }
909
+
910
+ const server = topology.s.servers.get(serverAddress);
911
+ topology.s.servers.delete(serverAddress);
912
+
913
+ // prepare server for garbage collection
914
+ destroyServer(server, topology);
915
+ }
916
+ }
917
+
918
+ function serverConnectEventHandler(server, topology) {
919
+ return function(/* isMaster, err */) {
920
+ server.monitor({
921
+ initial: true,
922
+ heartbeatFrequencyMS: topology.description.heartbeatFrequencyMS
923
+ });
924
+ };
925
+ }
926
+
927
+ function serverErrorEventHandler(server, topology) {
928
+ return function(err) {
929
+ topology.emit(
930
+ 'serverClosed',
931
+ new monitoring.ServerClosedEvent(topology.s.id, server.description.address)
932
+ );
933
+
934
+ if (err instanceof MongoParseError || isSDAMUnrecoverableError(err)) {
935
+ resetServerState(server, err, { clearPool: true });
936
+ return;
937
+ }
938
+
939
+ resetServerState(server, err);
940
+ };
941
+ }
942
+
943
+ function executeWriteOperation(args, options, callback) {
944
+ if (typeof options === 'function') (callback = options), (options = {});
945
+ options = options || {};
946
+
947
+ // TODO: once we drop Node 4, use destructuring either here or in arguments.
948
+ const topology = args.topology;
949
+ const op = args.op;
950
+ const ns = args.ns;
951
+ const ops = args.ops;
952
+
953
+ const willRetryWrite =
954
+ !args.retrying &&
955
+ !!options.retryWrites &&
956
+ options.session &&
957
+ isRetryableWritesSupported(topology) &&
958
+ !options.session.inTransaction();
959
+
960
+ topology.selectServer(writableServerSelector(), options, (err, server) => {
961
+ if (err) {
962
+ callback(err, null);
963
+ return;
964
+ }
965
+
966
+ const handler = (err, result) => {
967
+ if (!err) return callback(null, result);
968
+ if (!isRetryableError(err)) {
969
+ return callback(err);
970
+ }
971
+
972
+ if (willRetryWrite) {
973
+ const newArgs = Object.assign({}, args, { retrying: true });
974
+ return executeWriteOperation(newArgs, options, callback);
975
+ }
976
+
977
+ return callback(err);
978
+ };
979
+
980
+ if (callback.operationId) {
981
+ handler.operationId = callback.operationId;
982
+ }
983
+
984
+ // increment and assign txnNumber
985
+ if (willRetryWrite) {
986
+ options.session.incrementTransactionNumber();
987
+ options.willRetryWrite = willRetryWrite;
988
+ }
989
+
990
+ // execute the write operation
991
+ server[op](ns, ops, options, handler);
992
+ });
993
+ }
994
+
995
+ /**
996
+ * Resets the internal state of this server to `Unknown` by simulating an empty ismaster
997
+ *
998
+ * @private
999
+ * @param {Server} server
1000
+ * @param {MongoError} error The error that caused the state reset
1001
+ * @param {object} [options] Optional settings
1002
+ * @param {boolean} [options.clearPool=false] Pool should be cleared out on state reset
1003
+ */
1004
+ function resetServerState(server, error, options) {
1005
+ options = Object.assign({}, { clearPool: false }, options);
1006
+
1007
+ function resetState() {
1008
+ server.emit(
1009
+ 'descriptionReceived',
1010
+ new ServerDescription(server.description.address, null, { error })
1011
+ );
1012
+ server.monitor();
1013
+ }
1014
+
1015
+ if (options.clearPool && server.s.pool) {
1016
+ server.s.pool.reset(() => resetState());
1017
+ return;
1018
+ }
1019
+
1020
+ resetState();
1021
+ }
1022
+
1023
+ function translateReadPreference(options) {
1024
+ if (options.readPreference == null) {
1025
+ return;
1026
+ }
1027
+
1028
+ let r = options.readPreference;
1029
+ if (typeof r === 'string') {
1030
+ options.readPreference = new ReadPreference(r);
1031
+ } else if (r && !(r instanceof ReadPreference) && typeof r === 'object') {
1032
+ const mode = r.mode || r.preference;
1033
+ if (mode && typeof mode === 'string') {
1034
+ options.readPreference = new ReadPreference(mode, r.tags, {
1035
+ maxStalenessSeconds: r.maxStalenessSeconds
1036
+ });
1037
+ }
1038
+ } else if (!(r instanceof ReadPreference)) {
1039
+ throw new TypeError('Invalid read preference: ' + r);
1040
+ }
1041
+
1042
+ return options;
1043
+ }
1044
+
1045
+ function srvPollingHandler(topology) {
1046
+ return function handleSrvPolling(ev) {
1047
+ const previousTopologyDescription = topology.s.description;
1048
+ topology.s.description = topology.s.description.updateFromSrvPollingEvent(ev);
1049
+ if (topology.s.description === previousTopologyDescription) {
1050
+ // Nothing changed, so return
1051
+ return;
1052
+ }
1053
+
1054
+ updateServers(topology);
1055
+
1056
+ topology.emit(
1057
+ 'topologyDescriptionChanged',
1058
+ new monitoring.TopologyDescriptionChangedEvent(
1059
+ topology.s.id,
1060
+ previousTopologyDescription,
1061
+ topology.s.description
1062
+ )
1063
+ );
1064
+ };
1065
+ }
1066
+
1067
+ /**
1068
+ * A server opening SDAM monitoring event
1069
+ *
1070
+ * @event Topology#serverOpening
1071
+ * @type {ServerOpeningEvent}
1072
+ */
1073
+
1074
+ /**
1075
+ * A server closed SDAM monitoring event
1076
+ *
1077
+ * @event Topology#serverClosed
1078
+ * @type {ServerClosedEvent}
1079
+ */
1080
+
1081
+ /**
1082
+ * A server description SDAM change monitoring event
1083
+ *
1084
+ * @event Topology#serverDescriptionChanged
1085
+ * @type {ServerDescriptionChangedEvent}
1086
+ */
1087
+
1088
+ /**
1089
+ * A topology open SDAM event
1090
+ *
1091
+ * @event Topology#topologyOpening
1092
+ * @type {TopologyOpeningEvent}
1093
+ */
1094
+
1095
+ /**
1096
+ * A topology closed SDAM event
1097
+ *
1098
+ * @event Topology#topologyClosed
1099
+ * @type {TopologyClosedEvent}
1100
+ */
1101
+
1102
+ /**
1103
+ * A topology structure SDAM change event
1104
+ *
1105
+ * @event Topology#topologyDescriptionChanged
1106
+ * @type {TopologyDescriptionChangedEvent}
1107
+ */
1108
+
1109
+ /**
1110
+ * A topology serverHeartbeatStarted SDAM event
1111
+ *
1112
+ * @event Topology#serverHeartbeatStarted
1113
+ * @type {ServerHeartbeatStartedEvent}
1114
+ */
1115
+
1116
+ /**
1117
+ * A topology serverHeartbeatFailed SDAM event
1118
+ *
1119
+ * @event Topology#serverHeartbeatFailed
1120
+ * @type {ServerHearbeatFailedEvent}
1121
+ */
1122
+
1123
+ /**
1124
+ * A topology serverHeartbeatSucceeded SDAM change event
1125
+ *
1126
+ * @event Topology#serverHeartbeatSucceeded
1127
+ * @type {ServerHeartbeatSucceededEvent}
1128
+ */
1129
+
1130
+ /**
1131
+ * An event emitted indicating a command was started, if command monitoring is enabled
1132
+ *
1133
+ * @event Topology#commandStarted
1134
+ * @type {object}
1135
+ */
1136
+
1137
+ /**
1138
+ * An event emitted indicating a command succeeded, if command monitoring is enabled
1139
+ *
1140
+ * @event Topology#commandSucceeded
1141
+ * @type {object}
1142
+ */
1143
+
1144
+ /**
1145
+ * An event emitted indicating a command failed, if command monitoring is enabled
1146
+ *
1147
+ * @event Topology#commandFailed
1148
+ * @type {object}
1149
+ */
1150
+
1151
+ module.exports = Topology;