Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

topology.js 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. 'use strict';
  2. const EventEmitter = require('events');
  3. const ServerDescription = require('./server_description').ServerDescription;
  4. const TopologyDescription = require('./topology_description').TopologyDescription;
  5. const TopologyType = require('./topology_description').TopologyType;
  6. const monitoring = require('./monitoring');
  7. const calculateDurationInMs = require('../utils').calculateDurationInMs;
  8. const MongoTimeoutError = require('../error').MongoTimeoutError;
  9. const MongoNetworkError = require('../error').MongoNetworkError;
  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. // Global state
  21. let globalTopologyCounter = 0;
  22. // Constants
  23. const TOPOLOGY_DEFAULTS = {
  24. localThresholdMS: 15,
  25. serverSelectionTimeoutMS: 10000,
  26. heartbeatFrequencyMS: 30000,
  27. minHeartbeatIntervalMS: 500
  28. };
  29. /**
  30. * A container of server instances representing a connection to a MongoDB topology.
  31. *
  32. * @fires Topology#serverOpening
  33. * @fires Topology#serverClosed
  34. * @fires Topology#serverDescriptionChanged
  35. * @fires Topology#topologyOpening
  36. * @fires Topology#topologyClosed
  37. * @fires Topology#topologyDescriptionChanged
  38. * @fires Topology#serverHeartbeatStarted
  39. * @fires Topology#serverHeartbeatSucceeded
  40. * @fires Topology#serverHeartbeatFailed
  41. */
  42. class Topology extends EventEmitter {
  43. /**
  44. * Create a topology
  45. *
  46. * @param {Array|String} [seedlist] a string list, or array of Server instances to connect to
  47. * @param {Object} [options] Optional settings
  48. * @param {Number} [options.localThresholdMS=15] The size of the latency window for selecting among multiple suitable servers
  49. * @param {Number} [options.serverSelectionTimeoutMS=30000] How long to block for server selection before throwing an error
  50. * @param {Number} [options.heartbeatFrequencyMS=10000] The frequency with which topology updates are scheduled
  51. */
  52. constructor(seedlist, options) {
  53. super();
  54. if (typeof options === 'undefined') {
  55. options = seedlist;
  56. seedlist = [];
  57. // this is for legacy single server constructor support
  58. if (options.host) {
  59. seedlist.push({ host: options.host, port: options.port });
  60. }
  61. }
  62. seedlist = seedlist || [];
  63. options = Object.assign({}, TOPOLOGY_DEFAULTS, options);
  64. const topologyType = topologyTypeFromSeedlist(seedlist, options);
  65. const topologyId = globalTopologyCounter++;
  66. const serverDescriptions = seedlist.reduce((result, seed) => {
  67. const address = seed.port ? `${seed.host}:${seed.port}` : `${seed.host}:27017`;
  68. result.set(address, new ServerDescription(address));
  69. return result;
  70. }, new Map());
  71. this.s = {
  72. // the id of this topology
  73. id: topologyId,
  74. // passed in options
  75. options: Object.assign({}, options),
  76. // initial seedlist of servers to connect to
  77. seedlist: seedlist,
  78. // the topology description
  79. description: new TopologyDescription(
  80. topologyType,
  81. serverDescriptions,
  82. options.replicaSet,
  83. null,
  84. null,
  85. options
  86. ),
  87. serverSelectionTimeoutMS: options.serverSelectionTimeoutMS,
  88. heartbeatFrequencyMS: options.heartbeatFrequencyMS,
  89. minHeartbeatIntervalMS: options.minHeartbeatIntervalMS,
  90. // allow users to override the cursor factory
  91. Cursor: options.cursorFactory || Cursor,
  92. // the bson parser
  93. bson:
  94. options.bson ||
  95. new BSON([
  96. BSON.Binary,
  97. BSON.Code,
  98. BSON.DBRef,
  99. BSON.Decimal128,
  100. BSON.Double,
  101. BSON.Int32,
  102. BSON.Long,
  103. BSON.Map,
  104. BSON.MaxKey,
  105. BSON.MinKey,
  106. BSON.ObjectId,
  107. BSON.BSONRegExp,
  108. BSON.Symbol,
  109. BSON.Timestamp
  110. ])
  111. };
  112. // amend options for server instance creation
  113. this.s.options.compression = { compressors: createCompressionInfo(options) };
  114. }
  115. /**
  116. * @return A `TopologyDescription` for this topology
  117. */
  118. get description() {
  119. return this.s.description;
  120. }
  121. /**
  122. * All raw connections
  123. * @method
  124. * @return {Connection[]}
  125. */
  126. connections() {
  127. return Array.from(this.s.servers.values()).reduce((result, server) => {
  128. return result.concat(server.s.pool.allConnections());
  129. }, []);
  130. }
  131. /**
  132. * Initiate server connect
  133. *
  134. * @param {Object} [options] Optional settings
  135. * @param {Array} [options.auth=null] Array of auth options to apply on connect
  136. */
  137. connect(/* options */) {
  138. // emit SDAM monitoring events
  139. this.emit('topologyOpening', new monitoring.TopologyOpeningEvent(this.s.id));
  140. // emit an event for the topology change
  141. this.emit(
  142. 'topologyDescriptionChanged',
  143. new monitoring.TopologyDescriptionChangedEvent(
  144. this.s.id,
  145. new TopologyDescription(TopologyType.Unknown), // initial is always Unknown
  146. this.s.description
  147. )
  148. );
  149. connectServers(this, Array.from(this.s.description.servers.values()));
  150. this.s.connected = true;
  151. }
  152. /**
  153. * Close this topology
  154. */
  155. close(callback) {
  156. // destroy all child servers
  157. this.s.servers.forEach(server => server.destroy());
  158. // emit an event for close
  159. this.emit('topologyClosed', new monitoring.TopologyClosedEvent(this.s.id));
  160. this.s.connected = false;
  161. if (typeof callback === 'function') {
  162. callback(null, null);
  163. }
  164. }
  165. /**
  166. * Selects a server according to the selection predicate provided
  167. *
  168. * @param {function} [selector] An optional selector to select servers by, defaults to a random selection within a latency window
  169. * @return {Server} An instance of a `Server` meeting the criteria of the predicate provided
  170. */
  171. selectServer(selector, options, callback) {
  172. if (typeof options === 'function') (callback = options), (options = {});
  173. options = Object.assign(
  174. {},
  175. { serverSelectionTimeoutMS: this.s.serverSelectionTimeoutMS },
  176. options
  177. );
  178. selectServers(
  179. this,
  180. selector,
  181. options.serverSelectionTimeoutMS,
  182. process.hrtime(),
  183. (err, servers) => {
  184. if (err) return callback(err, null);
  185. callback(null, randomSelection(servers));
  186. }
  187. );
  188. }
  189. /**
  190. * Update the internal TopologyDescription with a ServerDescription
  191. *
  192. * @param {object} serverDescription The server to update in the internal list of server descriptions
  193. */
  194. serverUpdateHandler(serverDescription) {
  195. if (!this.s.description.hasServer(serverDescription.address)) {
  196. return;
  197. }
  198. // these will be used for monitoring events later
  199. const previousTopologyDescription = this.s.description;
  200. const previousServerDescription = this.s.description.servers.get(serverDescription.address);
  201. // first update the TopologyDescription
  202. this.s.description = this.s.description.update(serverDescription);
  203. // emit monitoring events for this change
  204. this.emit(
  205. 'serverDescriptionChanged',
  206. new monitoring.ServerDescriptionChangedEvent(
  207. this.s.id,
  208. serverDescription.address,
  209. previousServerDescription,
  210. this.s.description.servers.get(serverDescription.address)
  211. )
  212. );
  213. // update server list from updated descriptions
  214. updateServers(this, serverDescription);
  215. this.emit(
  216. 'topologyDescriptionChanged',
  217. new monitoring.TopologyDescriptionChangedEvent(
  218. this.s.id,
  219. previousTopologyDescription,
  220. this.s.description
  221. )
  222. );
  223. }
  224. /**
  225. * Authenticate using a specified mechanism
  226. *
  227. * @param {String} mechanism The auth mechanism used for authentication
  228. * @param {String} db The db we are authenticating against
  229. * @param {Object} options Optional settings for the authenticating mechanism
  230. * @param {authResultCallback} callback A callback function
  231. */
  232. auth(mechanism, db, options, callback) {
  233. callback(null, null);
  234. }
  235. /**
  236. * Logout from a database
  237. *
  238. * @param {String} db The db we are logging out from
  239. * @param {authResultCallback} callback A callback function
  240. */
  241. logout(db, callback) {
  242. callback(null, null);
  243. }
  244. // Basic operation support. Eventually this should be moved into command construction
  245. // during the command refactor.
  246. /**
  247. * Insert one or more documents
  248. *
  249. * @param {String} ns The full qualified namespace for this operation
  250. * @param {Array} ops An array of documents to insert
  251. * @param {Boolean} [options.ordered=true] Execute in order or out of order
  252. * @param {Object} [options.writeConcern] Write concern for the operation
  253. * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized
  254. * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields
  255. * @param {ClientSession} [options.session] Session to use for the operation
  256. * @param {boolean} [options.retryWrites] Enable retryable writes for this operation
  257. * @param {opResultCallback} callback A callback function
  258. */
  259. insert(ns, ops, options, callback) {
  260. executeWriteOperation({ topology: this, op: 'insert', ns, ops }, options, callback);
  261. }
  262. /**
  263. * Perform one or more update operations
  264. *
  265. * @param {string} ns The fully qualified namespace for this operation
  266. * @param {array} ops An array of updates
  267. * @param {boolean} [options.ordered=true] Execute in order or out of order
  268. * @param {object} [options.writeConcern] Write concern for the operation
  269. * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized
  270. * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields
  271. * @param {ClientSession} [options.session] Session to use for the operation
  272. * @param {boolean} [options.retryWrites] Enable retryable writes for this operation
  273. * @param {opResultCallback} callback A callback function
  274. */
  275. update(ns, ops, options, callback) {
  276. executeWriteOperation({ topology: this, op: 'update', ns, ops }, options, callback);
  277. }
  278. /**
  279. * Perform one or more remove operations
  280. *
  281. * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
  282. * @param {array} ops An array of removes
  283. * @param {boolean} [options.ordered=true] Execute in order or out of order
  284. * @param {object} [options.writeConcern={}] Write concern for the operation
  285. * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
  286. * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
  287. * @param {ClientSession} [options.session=null] Session to use for the operation
  288. * @param {boolean} [options.retryWrites] Enable retryable writes for this operation
  289. * @param {opResultCallback} callback A callback function
  290. */
  291. remove(ns, ops, options, callback) {
  292. executeWriteOperation({ topology: this, op: 'remove', ns, ops }, options, callback);
  293. }
  294. /**
  295. * Execute a command
  296. *
  297. * @method
  298. * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
  299. * @param {object} cmd The command hash
  300. * @param {ReadPreference} [options.readPreference] Specify read preference if command supports it
  301. * @param {Connection} [options.connection] Specify connection object to execute command against
  302. * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
  303. * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
  304. * @param {ClientSession} [options.session=null] Session to use for the operation
  305. * @param {opResultCallback} callback A callback function
  306. */
  307. command(ns, cmd, options, callback) {
  308. if (typeof options === 'function') {
  309. (callback = options), (options = {}), (options = options || {});
  310. }
  311. const readPreference = options.readPreference ? options.readPreference : ReadPreference.primary;
  312. this.selectServer(readPreferenceServerSelector(readPreference), (err, server) => {
  313. if (err) {
  314. callback(err, null);
  315. return;
  316. }
  317. server.command(ns, cmd, options, callback);
  318. });
  319. }
  320. /**
  321. * Create a new cursor
  322. *
  323. * @method
  324. * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
  325. * @param {object|Long} cmd Can be either a command returning a cursor or a cursorId
  326. * @param {object} [options] Options for the cursor
  327. * @param {object} [options.batchSize=0] Batchsize for the operation
  328. * @param {array} [options.documents=[]] Initial documents list for cursor
  329. * @param {ReadPreference} [options.readPreference] Specify read preference if command supports it
  330. * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
  331. * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
  332. * @param {ClientSession} [options.session=null] Session to use for the operation
  333. * @param {object} [options.topology] The internal topology of the created cursor
  334. * @returns {Cursor}
  335. */
  336. cursor(ns, cmd, options) {
  337. options = options || {};
  338. const topology = options.topology || this;
  339. const CursorClass = options.cursorFactory || this.s.Cursor;
  340. return new CursorClass(this.s.bson, ns, cmd, options, topology, this.s.options);
  341. }
  342. }
  343. // legacy aliases
  344. Topology.prototype.destroy = deprecate(
  345. Topology.prototype.close,
  346. 'destroy() is deprecated, please use close() instead'
  347. );
  348. function topologyTypeFromSeedlist(seedlist, options) {
  349. if (seedlist.length === 1 && !options.replicaSet) return TopologyType.Single;
  350. if (options.replicaSet) return TopologyType.ReplicaSetNoPrimary;
  351. return TopologyType.Unknown;
  352. }
  353. function randomSelection(array) {
  354. return array[Math.floor(Math.random() * array.length)];
  355. }
  356. /**
  357. * Selects servers using the provided selector
  358. *
  359. * @private
  360. * @param {Topology} topology The topology to select servers from
  361. * @param {function} selector The actual predicate used for selecting servers
  362. * @param {Number} timeout The max time we are willing wait for selection
  363. * @param {Number} start A high precision timestamp for the start of the selection process
  364. * @param {function} callback The callback used to convey errors or the resultant servers
  365. */
  366. function selectServers(topology, selector, timeout, start, callback) {
  367. const serverDescriptions = Array.from(topology.description.servers.values());
  368. let descriptions;
  369. try {
  370. descriptions = selector
  371. ? selector(topology.description, serverDescriptions)
  372. : serverDescriptions;
  373. } catch (e) {
  374. return callback(e, null);
  375. }
  376. if (descriptions.length) {
  377. const servers = descriptions.map(description => topology.s.servers.get(description.address));
  378. return callback(null, servers);
  379. }
  380. const duration = calculateDurationInMs(start);
  381. if (duration >= timeout) {
  382. return callback(new MongoTimeoutError(`Server selection timed out after ${timeout} ms`));
  383. }
  384. const retrySelection = () => {
  385. // ensure all server monitors attempt monitoring immediately
  386. topology.s.servers.forEach(server => server.monitor());
  387. const iterationTimer = setTimeout(() => {
  388. callback(new MongoTimeoutError('Server selection timed out due to monitoring'));
  389. }, topology.s.minHeartbeatIntervalMS);
  390. topology.once('topologyDescriptionChanged', () => {
  391. // successful iteration, clear the check timer
  392. clearTimeout(iterationTimer);
  393. // topology description has changed due to monitoring, reattempt server selection
  394. selectServers(topology, selector, timeout, start, callback);
  395. });
  396. };
  397. // ensure we are connected
  398. if (!topology.s.connected) {
  399. topology.connect();
  400. // we want to make sure we're still within the requested timeout window
  401. const failToConnectTimer = setTimeout(() => {
  402. callback(new MongoTimeoutError('Server selection timed out waiting to connect'));
  403. }, timeout - duration);
  404. topology.once('connect', () => {
  405. clearTimeout(failToConnectTimer);
  406. retrySelection();
  407. });
  408. return;
  409. }
  410. retrySelection();
  411. }
  412. /**
  413. * Create `Server` instances for all initially known servers, connect them, and assign
  414. * them to the passed in `Topology`.
  415. *
  416. * @param {Topology} topology The topology responsible for the servers
  417. * @param {ServerDescription[]} serverDescriptions A list of server descriptions to connect
  418. */
  419. function connectServers(topology, serverDescriptions) {
  420. topology.s.servers = serverDescriptions.reduce((servers, serverDescription) => {
  421. // publish an open event for each ServerDescription created
  422. topology.emit(
  423. 'serverOpening',
  424. new monitoring.ServerOpeningEvent(topology.s.id, serverDescription.address)
  425. );
  426. const server = new Server(serverDescription, topology.s.options);
  427. relayEvents(server, topology, [
  428. 'serverHeartbeatStarted',
  429. 'serverHeartbeatSucceeded',
  430. 'serverHeartbeatFailed'
  431. ]);
  432. server.on('descriptionReceived', topology.serverUpdateHandler.bind(topology));
  433. server.on('connect', serverConnectEventHandler(server, topology));
  434. servers.set(serverDescription.address, server);
  435. server.connect();
  436. return servers;
  437. }, new Map());
  438. }
  439. function updateServers(topology, currentServerDescription) {
  440. // update the internal server's description
  441. if (topology.s.servers.has(currentServerDescription.address)) {
  442. const server = topology.s.servers.get(currentServerDescription.address);
  443. server.s.description = currentServerDescription;
  444. }
  445. // add new servers for all descriptions we currently don't know about locally
  446. for (const serverDescription of topology.description.servers.values()) {
  447. if (!topology.s.servers.has(serverDescription.address)) {
  448. topology.emit(
  449. 'serverOpening',
  450. new monitoring.ServerOpeningEvent(topology.s.id, serverDescription.address)
  451. );
  452. const server = new Server(serverDescription, topology.s.options);
  453. relayEvents(server, topology, [
  454. 'serverHeartbeatStarted',
  455. 'serverHeartbeatSucceeded',
  456. 'serverHeartbeatFailed'
  457. ]);
  458. server.on('descriptionReceived', topology.serverUpdateHandler.bind(topology));
  459. server.on('connect', serverConnectEventHandler(server, topology));
  460. topology.s.servers.set(serverDescription.address, server);
  461. server.connect();
  462. }
  463. }
  464. // for all servers no longer known, remove their descriptions and destroy their instances
  465. for (const entry of topology.s.servers) {
  466. const serverAddress = entry[0];
  467. if (topology.description.hasServer(serverAddress)) {
  468. continue;
  469. }
  470. const server = topology.s.servers.get(serverAddress);
  471. topology.s.servers.delete(serverAddress);
  472. server.destroy(() =>
  473. topology.emit('serverClosed', new monitoring.ServerClosedEvent(topology.s.id, serverAddress))
  474. );
  475. }
  476. }
  477. function serverConnectEventHandler(server, topology) {
  478. return function(/* ismaster */) {
  479. topology.emit('connect', topology);
  480. };
  481. }
  482. function executeWriteOperation(args, options, callback) {
  483. if (typeof options === 'function') (callback = options), (options = {});
  484. options = options || {};
  485. // TODO: once we drop Node 4, use destructuring either here or in arguments.
  486. const topology = args.topology;
  487. const op = args.op;
  488. const ns = args.ns;
  489. const ops = args.ops;
  490. const willRetryWrite =
  491. !args.retrying &&
  492. options.retryWrites &&
  493. options.session &&
  494. isRetryableWritesSupported(topology) &&
  495. !options.session.inTransaction();
  496. topology.selectServer(writableServerSelector(), (err, server) => {
  497. if (err) {
  498. callback(err, null);
  499. return;
  500. }
  501. const handler = (err, result) => {
  502. if (!err) return callback(null, result);
  503. if (!(err instanceof MongoNetworkError) && !err.message.match(/not master/)) {
  504. return callback(err);
  505. }
  506. if (willRetryWrite) {
  507. const newArgs = Object.assign({}, args, { retrying: true });
  508. return executeWriteOperation(newArgs, options, callback);
  509. }
  510. return callback(err);
  511. };
  512. if (callback.operationId) {
  513. handler.operationId = callback.operationId;
  514. }
  515. // increment and assign txnNumber
  516. if (willRetryWrite) {
  517. options.session.incrementTransactionNumber();
  518. options.willRetryWrite = willRetryWrite;
  519. }
  520. // execute the write operation
  521. server[op](ns, ops, options, handler);
  522. // we need to increment the statement id if we're in a transaction
  523. if (options.session && options.session.inTransaction()) {
  524. options.session.incrementStatementId(ops.length);
  525. }
  526. });
  527. }
  528. /**
  529. * A server opening SDAM monitoring event
  530. *
  531. * @event Topology#serverOpening
  532. * @type {ServerOpeningEvent}
  533. */
  534. /**
  535. * A server closed SDAM monitoring event
  536. *
  537. * @event Topology#serverClosed
  538. * @type {ServerClosedEvent}
  539. */
  540. /**
  541. * A server description SDAM change monitoring event
  542. *
  543. * @event Topology#serverDescriptionChanged
  544. * @type {ServerDescriptionChangedEvent}
  545. */
  546. /**
  547. * A topology open SDAM event
  548. *
  549. * @event Topology#topologyOpening
  550. * @type {TopologyOpeningEvent}
  551. */
  552. /**
  553. * A topology closed SDAM event
  554. *
  555. * @event Topology#topologyClosed
  556. * @type {TopologyClosedEvent}
  557. */
  558. /**
  559. * A topology structure SDAM change event
  560. *
  561. * @event Topology#topologyDescriptionChanged
  562. * @type {TopologyDescriptionChangedEvent}
  563. */
  564. /**
  565. * A topology serverHeartbeatStarted SDAM event
  566. *
  567. * @event Topology#serverHeartbeatStarted
  568. * @type {ServerHeartbeatStartedEvent}
  569. */
  570. /**
  571. * A topology serverHeartbeatFailed SDAM event
  572. *
  573. * @event Topology#serverHeartbeatFailed
  574. * @type {ServerHearbeatFailedEvent}
  575. */
  576. /**
  577. * A topology serverHeartbeatSucceeded SDAM change event
  578. *
  579. * @event Topology#serverHeartbeatSucceeded
  580. * @type {ServerHeartbeatSucceededEvent}
  581. */
  582. module.exports = Topology;