3 var inherits = require('util').inherits,
4 f = require('util').format,
5 EventEmitter = require('events').EventEmitter,
6 BSON = require('bson').native().BSON,
7 ReadPreference = require('./read_preference'),
8 Logger = require('../connection/logger'),
9 debugOptions = require('../connection/utils').debugOptions,
10 Pool = require('../connection/pool'),
11 Query = require('../connection/commands').Query,
12 MongoError = require('../error'),
13 PreTwoSixWireProtocolSupport = require('../wireprotocol/2_4_support'),
14 TwoSixWireProtocolSupport = require('../wireprotocol/2_6_support'),
15 ThreeTwoWireProtocolSupport = require('../wireprotocol/3_2_support'),
16 BasicCursor = require('../cursor'),
17 sdam = require('./shared'),
18 assign = require('./shared').assign,
19 createClientInfo = require('./shared').createClientInfo;
21 // Used for filtering out fields for loggin
22 var debugFields = ['reconnect', 'reconnectTries', 'reconnectInterval', 'emitError', 'cursorFactory', 'host'
23 , 'port', 'size', 'keepAlive', 'keepAliveInitialDelay', 'noDelay', 'connectionTimeout', 'checkServerIdentity'
24 , 'socketTimeout', 'singleBufferSerializtion', 'ssl', 'ca', 'cert', 'key', 'rejectUnauthorized', 'promoteLongs', 'promoteValues'
25 , 'promoteBuffers', 'servername'];
29 var serverAccounting = false;
33 * Creates a new Server instance
35 * @param {boolean} [options.reconnect=true] Server will attempt to reconnect on loss of connection
36 * @param {number} [options.reconnectTries=30] Server attempt to reconnect #times
37 * @param {number} [options.reconnectInterval=1000] Server will wait # milliseconds between retries
38 * @param {number} [options.monitoring=true] Enable the server state monitoring (calling ismaster at monitoringInterval)
39 * @param {number} [options.monitoringInterval=5000] The interval of calling ismaster when monitoring is enabled.
40 * @param {Cursor} [options.cursorFactory=Cursor] The cursor factory class used for all query cursors
41 * @param {string} options.host The server host
42 * @param {number} options.port The server port
43 * @param {number} [options.size=5] Server connection pool size
44 * @param {boolean} [options.keepAlive=true] TCP Connection keep alive enabled
45 * @param {number} [options.keepAliveInitialDelay=0] Initial delay before TCP keep alive enabled
46 * @param {boolean} [options.noDelay=true] TCP Connection no delay
47 * @param {number} [options.connectionTimeout=0] TCP Connection timeout setting
48 * @param {number} [options.socketTimeout=0] TCP Socket timeout setting
49 * @param {boolean} [options.ssl=false] Use SSL for connection
50 * @param {boolean|function} [options.checkServerIdentity=true] Ensure we check server identify during SSL, set to false to disable checking. Only works for Node 0.12.x or higher. You can pass in a boolean or your own checkServerIdentity override function.
51 * @param {Buffer} [options.ca] SSL Certificate store binary buffer
52 * @param {Buffer} [options.cert] SSL Certificate binary buffer
53 * @param {Buffer} [options.key] SSL Key file binary buffer
54 * @param {string} [options.passphrase] SSL Certificate pass phrase
55 * @param {boolean} [options.rejectUnauthorized=true] Reject unauthorized server certificates
56 * @param {string} [options.servername=null] String containing the server name requested via TLS SNI.
57 * @param {boolean} [options.promoteLongs=true] Convert Long values from the db into Numbers if they fit into 53 bits
58 * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
59 * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
60 * @param {string} [options.appname=null] Application name, passed in on ismaster call and logged in mongod server logs. Maximum size 128 bytes.
61 * @param {boolean} [options.domainsEnabled=false] Enable the wrapping of the callback in the current domain, disabled by default to avoid perf hit.
62 * @return {Server} A cursor instance
63 * @fires Server#connect
66 * @fires Server#timeout
67 * @fires Server#parseError
68 * @fires Server#reconnect
69 * @fires Server#reconnectFailed
70 * @fires Server#serverHeartbeatStarted
71 * @fires Server#serverHeartbeatSucceeded
72 * @fires Server#serverHeartbeatFailed
73 * @fires Server#topologyOpening
74 * @fires Server#topologyClosed
75 * @fires Server#topologyDescriptionChanged
77 var Server = function(options) {
78 options = options || {};
81 EventEmitter.call(this);
87 var reconnectTries = options.reconnectTries || 30;
94 logger: Logger('Server', options),
96 Cursor: options.cursorFactory || BasicCursor,
98 bson: options.bson || new BSON(),
101 // Disconnect handler
102 disconnectHandler: options.disconnectHandler,
103 // Monitor thread (keeps the connection alive)
104 monitoring: typeof options.monitoring == 'boolean' ? options.monitoring : true,
105 // Is the server in a topology
106 inTopology: typeof options.inTopology == 'boolean' ? options.inTopology : false,
107 // Monitoring timeout
108 monitoringInterval: typeof options.monitoringInterval == 'number'
109 ? options.monitoringInterval
116 this.ismaster = null;
118 this.lastIsMasterMS = -1;
119 // The monitoringProcessId
120 this.monitoringProcessId = null;
121 // Initial connection
122 this.initalConnect = true;
123 // Wire protocol handler, default to oldest known protocol handler
124 // this gets changed when the first ismaster is called.
125 this.wireProtocolHandler = new PreTwoSixWireProtocolSupport();
127 this._type = 'server';
128 // Set the client info
129 this.clientInfo = createClientInfo(options);
131 // Max Stalleness values
132 // last time we updated the ismaster state
133 this.lastUpdateTime = 0;
135 this.lastWriteDate = 0;
140 inherits(Server, EventEmitter);
142 Object.defineProperty(Server.prototype, 'type', {
143 enumerable:true, get: function() { return this._type; }
146 Server.enableServerAccounting = function() {
147 serverAccounting = true;
151 Server.disableServerAccounting = function() {
152 serverAccounting = false;
155 Server.servers = function() {
159 Object.defineProperty(Server.prototype, 'name', {
161 get: function() { return this.s.options.host + ":" + this.s.options.port; }
164 function configureWireProtocolHandler(self, ismaster) {
165 // 3.2 wire protocol handler
166 if(ismaster.maxWireVersion >= 4) {
167 return new ThreeTwoWireProtocolSupport(new TwoSixWireProtocolSupport());
170 // 2.6 wire protocol handler
171 if(ismaster.maxWireVersion >= 2) {
172 return new TwoSixWireProtocolSupport();
175 // 2.4 or earlier wire protocol handler
176 return new PreTwoSixWireProtocolSupport();
179 function disconnectHandler(self, type, ns, cmd, options, callback) {
180 // Topology is not connected, save the call in the provided store to be
181 // Executed at some point when the handler deems it's reconnected
182 if(!self.s.pool.isConnected() && self.s.disconnectHandler != null && !options.monitoring) {
183 self.s.disconnectHandler.add(type, ns, cmd, options, callback);
187 // If we have no connection error
188 if(!self.s.pool.isConnected()) {
189 callback(MongoError.create(f("no connection available to server %s", self.name)));
194 function monitoringProcess(self) {
196 // Pool was destroyed do not continue process
197 if(self.s.pool.isDestroyed()) return;
198 // Emit monitoring Process event
199 self.emit('monitoring', self);
200 // Perform ismaster call
202 var queryOptions = { numberToSkip: 0, numberToReturn: -1, checkKeys: false, slaveOk: true };
203 // Create a query instance
204 var query = new Query(self.s.bson, 'admin.$cmd', {ismaster:true}, queryOptions);
206 var start = new Date().getTime();
207 // Execute the ismaster query
208 self.s.pool.write(query, function(err, result) {
209 // Set initial lastIsMasterMS
210 self.lastIsMasterMS = new Date().getTime() - start;
211 if(self.s.pool.isDestroyed()) return;
212 // Update the ismaster view if we have a result
214 self.ismaster = result.result;
216 // Re-schedule the monitoring process
217 self.monitoringProcessId = setTimeout(monitoringProcess(self), self.s.monitoringInterval);
222 var eventHandler = function(self, event) {
223 return function(err) {
224 // Log information of received information if in info mode
225 if(self.s.logger.isInfo()) {
226 var object = err instanceof MongoError ? JSON.stringify(err) : {}
227 self.s.logger.info(f('server %s fired event %s out with message %s'
228 , self.name, event, object));
231 // Handle connect event
232 if(event == 'connect') {
233 // Issue an ismaster command at connect
235 var queryOptions = { numberToSkip: 0, numberToReturn: -1, checkKeys: false, slaveOk: true };
236 // Create a query instance
237 var query = new Query(self.s.bson, 'admin.$cmd', {ismaster:true, client: self.clientInfo}, queryOptions);
239 var start = new Date().getTime();
240 // Execute the ismaster query
241 self.s.pool.write(query, function(err, result) {
242 // Set initial lastIsMasterMS
243 self.lastIsMasterMS = new Date().getTime() - start;
246 if(self.listeners('error').length > 0) self.emit('error', err);
250 // Ensure no error emitted after initial connect when reconnecting
251 self.initalConnect = false;
253 self.ismaster = result.result;
255 // It's a proxy change the type so
256 // the wireprotocol will send $readPreference
257 if(self.ismaster.msg == 'isdbgrid') {
258 self._type = 'mongos';
260 // Add the correct wire protocol handler
261 self.wireProtocolHandler = configureWireProtocolHandler(self, self.ismaster);
262 // Have we defined self monitoring
263 if(self.s.monitoring) {
264 self.monitoringProcessId = setTimeout(monitoringProcess(self), self.s.monitoringInterval);
267 // Emit server description changed if something listening
268 sdam.emitServerDescriptionChanged(self, {
269 address: self.name, arbiters: [], hosts: [], passives: [], type: !self.s.inTopology ? 'Standalone' : sdam.getTopologyType(self)
272 // Emit topology description changed if something listening
273 sdam.emitTopologyDescriptionChanged(self, {
274 topologyType: 'Single', servers: [{address: self.name, arbiters: [], hosts: [], passives: [], type: 'Standalone'}]
277 // Log the ismaster if available
278 if(self.s.logger.isInfo()) {
279 self.s.logger.info(f('server %s connected with ismaster [%s]', self.name, JSON.stringify(self.ismaster)));
283 self.emit('connect', self);
285 } else if(event == 'error' || event == 'parseError'
286 || event == 'close' || event == 'timeout' || event == 'reconnect'
287 || event == 'attemptReconnect' || 'reconnectFailed') {
289 // Remove server instance from accounting
290 if(serverAccounting && ['close', 'timeout', 'error', 'parseError', 'reconnectFailed'].indexOf(event) != -1) {
291 // Emit toplogy opening event if not in topology
292 if(!self.s.inTopology) {
293 self.emit('topologyOpening', { topologyId: self.id });
296 delete servers[self.id];
299 // Reconnect failed return error
300 if(event == 'reconnectFailed') {
301 self.emit('reconnectFailed', err);
302 // Emit error if any listeners
303 if(self.listeners('error').length > 0) {
304 self.emit('error', err);
310 // On first connect fail
311 if(self.s.pool.state == 'disconnected' && self.initalConnect && ['close', 'timeout', 'error', 'parseError'].indexOf(event) != -1) {
312 self.initalConnect = false;
313 return self.emit('error', new MongoError(f('failed to connect to server [%s] on first connect', self.name)));
316 // Reconnect event, emit the server
317 if(event == 'reconnect') {
318 return self.emit(event, self);
322 self.emit(event, err);
328 * Initiate server connect
330 * @param {array} [options.auth=null] Array of auth options to apply on connect
332 Server.prototype.connect = function(options) {
334 options = options || {};
336 // Set the connections
337 if(serverAccounting) servers[this.id] = this;
339 // Do not allow connect to be called on anything that's not disconnected
340 if(self.s.pool && !self.s.pool.isDisconnected() && !self.s.pool.isDestroyed()) {
341 throw MongoError.create(f('server instance in invalid state %s', self.s.state));
345 self.s.pool = new Pool(assign(self.s.options, options, {bson: this.s.bson}));
348 self.s.pool.on('close', eventHandler(self, 'close'));
349 self.s.pool.on('error', eventHandler(self, 'error'));
350 self.s.pool.on('timeout', eventHandler(self, 'timeout'));
351 self.s.pool.on('parseError', eventHandler(self, 'parseError'));
352 self.s.pool.on('connect', eventHandler(self, 'connect'));
353 self.s.pool.on('reconnect', eventHandler(self, 'reconnect'));
354 self.s.pool.on('reconnectFailed', eventHandler(self, 'reconnectFailed'));
356 // Emit toplogy opening event if not in topology
357 if(!self.s.inTopology) {
358 this.emit('topologyOpening', { topologyId: self.id });
361 // Emit opening server event
362 self.emit('serverOpening', {
363 topologyId: self.s.topologyId != -1 ? self.s.topologyId : self.id,
367 // Connect with optional auth settings
369 self.s.pool.connect.apply(self.s.pool, options.auth);
371 self.s.pool.connect();
376 * Get the server description
380 Server.prototype.getDescription = function() {
381 var ismaster = this.ismaster || {};
383 type: sdam.getTopologyType(this),
387 // Add fields if available
388 if(ismaster.hosts) description.hosts = ismaster.hosts;
389 if(ismaster.arbiters) description.arbiters = ismaster.arbiters;
390 if(ismaster.passives) description.passives = ismaster.passives;
391 if(ismaster.setName) description.setName = ismaster.setName;
396 * Returns the last known ismaster document for this server
400 Server.prototype.lastIsMaster = function() {
401 return this.ismaster;
405 * Unref all connections belong to this server
408 Server.prototype.unref = function() {
413 * Figure out if the server is connected
417 Server.prototype.isConnected = function() {
418 if(!this.s.pool) return false;
419 return this.s.pool.isConnected();
423 * Figure out if the server instance was destroyed by calling destroy
427 Server.prototype.isDestroyed = function() {
428 if(!this.s.pool) return false;
429 return this.s.pool.isDestroyed();
432 function basicWriteValidations(self, options) {
433 if(!self.s.pool) return MongoError.create('server instance is not connected');
434 if(self.s.pool.isDestroyed()) return MongoError.create('server instance pool was destroyed');
437 function basicReadValidations(self, options) {
438 basicWriteValidations(self, options);
440 if(options.readPreference && !(options.readPreference instanceof ReadPreference)) {
441 throw new Error("readPreference must be an instance of ReadPreference");
448 * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
449 * @param {object} cmd The command hash
450 * @param {ReadPreference} [options.readPreference] Specify read preference if command supports it
451 * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
452 * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
453 * @param {Boolean} [options.fullResult=false] Return the full envelope instead of just the result document.
454 * @param {opResultCallback} callback A callback function
456 Server.prototype.command = function(ns, cmd, options, callback) {
458 if(typeof options == 'function') callback = options, options = {}, options = options || {};
459 var result = basicReadValidations(self, options);
460 if(result) return callback(result);
463 if(self.s.logger.isDebug()) self.s.logger.debug(f('executing command [%s] against %s', JSON.stringify({
464 ns: ns, cmd: cmd, options: debugOptions(debugFields, options)
467 // If we are not connected or have a disconnectHandler specified
468 if(disconnectHandler(self, 'command', ns, cmd, options, callback)) return;
470 // Check if we have collation support
471 if(this.ismaster && this.ismaster.maxWireVersion < 5 && cmd.collation) {
472 return callback(new MongoError(f('server %s does not support collation', this.name)));
479 checkKeys: typeof options.checkKeys == 'boolean' ? options.checkKeys: false,
480 serializeFunctions: typeof options.serializeFunctions == 'boolean' ? options.serializeFunctions : false,
481 ignoreUndefined: typeof options.ignoreUndefined == 'boolean' ? options.ignoreUndefined : false
484 // Create a query instance
485 var query = new Query(self.s.bson, ns, cmd, queryOptions);
486 // Set slave OK of the query
487 query.slaveOk = options.readPreference ? options.readPreference.slaveOk() : false;
491 raw: typeof options.raw == 'boolean' ? options.raw : false,
492 promoteLongs: typeof options.promoteLongs == 'boolean' ? options.promoteLongs : true,
493 promoteValues: typeof options.promoteValues == 'boolean' ? options.promoteValues : true,
494 promoteBuffers: typeof options.promoteBuffers == 'boolean' ? options.promoteBuffers : false,
496 monitoring: typeof options.monitoring == 'boolean' ? options.monitoring : false,
497 fullResult: typeof options.fullResult == 'boolean' ? options.fullResult : false,
498 requestId: query.requestId
501 // Write the operation to the pool
502 self.s.pool.write(query, writeOptions, callback);
506 * Insert one or more documents
508 * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
509 * @param {array} ops An array of documents to insert
510 * @param {boolean} [options.ordered=true] Execute in order or out of order
511 * @param {object} [options.writeConcern={}] Write concern for the operation
512 * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
513 * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
514 * @param {opResultCallback} callback A callback function
516 Server.prototype.insert = function(ns, ops, options, callback) {
518 if(typeof options == 'function') callback = options, options = {}, options = options || {};
519 var result = basicWriteValidations(self, options);
520 if(result) return callback(result);
522 // If we are not connected or have a disconnectHandler specified
523 if(disconnectHandler(self, 'insert', ns, ops, options, callback)) return;
525 // Setup the docs as an array
526 ops = Array.isArray(ops) ? ops : [ops];
529 return self.wireProtocolHandler.insert(self.s.pool, self.ismaster, ns, self.s.bson, ops, options, callback);
533 * Perform one or more update operations
535 * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
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 {opResultCallback} callback A callback function
543 Server.prototype.update = function(ns, ops, options, callback) {
545 if(typeof options == 'function') callback = options, options = {}, options = options || {};
546 var result = basicWriteValidations(self, options);
547 if(result) return callback(result);
549 // If we are not connected or have a disconnectHandler specified
550 if(disconnectHandler(self, 'update', ns, ops, options, callback)) return;
552 // Check if we have collation support
553 if(this.ismaster && this.ismaster.maxWireVersion < 5 && options.collation) {
554 return callback(new MongoError(f('server %s does not support collation', this.name)));
557 // Setup the docs as an array
558 ops = Array.isArray(ops) ? ops : [ops];
560 return self.wireProtocolHandler.update(self.s.pool, self.ismaster, ns, self.s.bson, ops, options, callback);
564 * Perform one or more remove operations
566 * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
567 * @param {array} ops An array of removes
568 * @param {boolean} [options.ordered=true] Execute in order or out of order
569 * @param {object} [options.writeConcern={}] Write concern for the operation
570 * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
571 * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
572 * @param {opResultCallback} callback A callback function
574 Server.prototype.remove = function(ns, ops, options, callback) {
576 if(typeof options == 'function') callback = options, options = {}, options = options || {};
577 var result = basicWriteValidations(self, options);
578 if(result) return callback(result);
580 // If we are not connected or have a disconnectHandler specified
581 if(disconnectHandler(self, 'remove', ns, ops, options, callback)) return;
583 // Check if we have collation support
584 if(this.ismaster && this.ismaster.maxWireVersion < 5 && options.collation) {
585 return callback(new MongoError(f('server %s does not support collation', this.name)));
588 // Setup the docs as an array
589 ops = Array.isArray(ops) ? ops : [ops];
591 return self.wireProtocolHandler.remove(self.s.pool, self.ismaster, ns, self.s.bson, ops, options, callback);
597 * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
598 * @param {{object}|{Long}} cmd Can be either a command returning a cursor or a cursorId
599 * @param {object} [options.batchSize=0] Batchsize for the operation
600 * @param {array} [options.documents=[]] Initial documents list for cursor
601 * @param {ReadPreference} [options.readPreference] Specify read preference if command supports it
602 * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
603 * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
604 * @param {opResultCallback} callback A callback function
606 Server.prototype.cursor = function(ns, cmd, cursorOptions) {
608 cursorOptions = cursorOptions || {};
609 // Set up final cursor type
610 var FinalCursor = cursorOptions.cursorFactory || s.Cursor;
612 return new FinalCursor(s.bson, ns, cmd, cursorOptions, this, s.options);
616 * Logout from a database
618 * @param {string} db The db we are logging out from
619 * @param {authResultCallback} callback A callback function
621 Server.prototype.logout = function(dbName, callback) {
622 this.s.pool.logout(dbName, callback);
626 * Authenticate using a specified mechanism
628 * @param {string} mechanism The Auth mechanism we are invoking
629 * @param {string} db The db we are invoking the mechanism against
630 * @param {...object} param Parameters for the specific mechanism
631 * @param {authResultCallback} callback A callback function
633 Server.prototype.auth = function(mechanism, db) {
636 // If we have the default mechanism we pick mechanism based on the wire
637 // protocol max version. If it's >= 3 then scram-sha1 otherwise mongodb-cr
638 if(mechanism == 'default' && self.ismaster && self.ismaster.maxWireVersion >= 3) {
639 mechanism = 'scram-sha-1';
640 } else if(mechanism == 'default') {
641 mechanism = 'mongocr';
644 // Slice all the arguments off
645 var args = Array.prototype.slice.call(arguments, 0);
649 var callback = args[args.length - 1];
651 // If we are not connected or have a disconnectHandler specified
652 if(disconnectHandler(self, 'auth', db, args, {}, callback)) {
656 // Do not authenticate if we are an arbiter
657 if(this.lastIsMaster() && this.lastIsMaster().arbiterOnly) {
658 return callback(null, true);
661 // Apply the arguments to the pool
662 self.s.pool.auth.apply(self.s.pool, args);
666 * Compare two server instances
668 * @param {Server} server Server to compare equality against
671 Server.prototype.equals = function(server) {
672 if(typeof server == 'string') return this.name == server;
673 if(server.name) return this.name == server.name;
678 * All raw connections
680 * @return {Connection[]}
682 Server.prototype.connections = function() {
683 return this.s.pool.allConnections();
691 Server.prototype.getServer = function() {
698 * @return {Connection}
700 Server.prototype.getConnection = function() {
701 return this.s.pool.get();
704 var listeners = ['close', 'error', 'timeout', 'parseError', 'connect'];
707 * Destroy the server connection
709 * @param {boolean} [options.emitClose=false] Emit close event on destroy
710 * @param {boolean} [options.emitDestroy=false] Emit destroy event on destroy
711 * @param {boolean} [options.force=false] Force destroy the pool
713 Server.prototype.destroy = function(options) {
714 options = options || {};
717 // Set the connections
718 if(serverAccounting) delete servers[this.id];
720 // Destroy the monitoring process if any
721 if(this.monitoringProcessId) {
722 clearTimeout(this.monitoringProcessId);
726 if(options.emitClose) {
727 self.emit('close', self);
730 // Emit destroy event
731 if(options.emitDestroy) {
732 self.emit('destroy', self);
735 // Remove all listeners
736 listeners.forEach(function(event) {
737 self.s.pool.removeAllListeners(event);
740 // Emit opening server event
741 if(self.listeners('serverClosed').length > 0) self.emit('serverClosed', {
742 topologyId: self.s.topologyId != -1 ? self.s.topologyId : self.id, address: self.name
745 // Emit toplogy opening event if not in topology
746 if(self.listeners('topologyClosed').length > 0 && !self.s.inTopology) {
747 self.emit('topologyClosed', { topologyId: self.id });
750 if(self.s.logger.isDebug()) {
751 self.s.logger.debug(f('destroy called on server %s', self.name));
755 this.s.pool.destroy(options.force);
759 * A server connect event, used to verify that the connection is up and running
761 * @event Server#connect
766 * A server reconnect event, used to verify that the server topology has reconnected
768 * @event Server#reconnect
773 * A server opening SDAM monitoring event
775 * @event Server#serverOpening
780 * A server closed SDAM monitoring event
782 * @event Server#serverClosed
787 * A server description SDAM change monitoring event
789 * @event Server#serverDescriptionChanged
794 * A topology open SDAM event
796 * @event Server#topologyOpening
801 * A topology closed SDAM event
803 * @event Server#topologyClosed
808 * A topology structure SDAM change event
810 * @event Server#topologyDescriptionChanged
815 * Server reconnect failed
817 * @event Server#reconnectFailed
822 * Server connection pool closed
824 * @event Server#close
829 * Server connection pool caused an error
831 * @event Server#error
836 * Server destroyed was called
838 * @event Server#destroy
842 module.exports = Server;