3 var os = require('os'),
4 f = require('util').format;
7 * Emit event if it exists
10 function emitSDAMEvent(self, event, description) {
11 if(self.listeners(event).length > 0) {
12 self.emit(event, description);
16 // Get package.json variable
17 var driverVersion = require(__dirname + '/../../package.json').version;
18 var nodejsversion = f('Node.js %s, %s', process.version, os.endianness());
20 var name = process.platform;
21 var architecture = process.arch;
22 var release = os.release();
24 function createClientInfo(options) {
25 // Build default client information
26 var clientInfo = options.clientInfo ? clone(options.clientInfo) : {
29 version: driverVersion
34 architecture: architecture,
39 // Is platform specified
40 if(clientInfo.platform && clientInfo.platform.indexOf('mongodb-core') == -1) {
41 clientInfo.platform = f('%s, mongodb-core: %s', clientInfo.platform, driverVersion);
42 } else if(!clientInfo.platform){
43 clientInfo.platform = nodejsversion;
46 // Do we have an application specific string
49 var buffer = new Buffer(options.appname);
50 // Return the truncated appname
51 var appname = buffer.length > 128 ? buffer.slice(0, 128).toString('utf8') : options.appname;
52 // Add to the clientInfo
53 clientInfo.application = { name: appname };
59 function clone(object) {
60 return JSON.parse(JSON.stringify(object));
63 var getPreviousDescription = function(self) {
64 if(!self.s.serverDescription) {
65 self.s.serverDescription = {
67 arbiters: [], hosts: [], passives: [], type: 'Unknown'
71 return self.s.serverDescription;
74 var emitServerDescriptionChanged = function(self, description) {
75 if(self.listeners('serverDescriptionChanged').length > 0) {
76 // Emit the server description changed events
77 self.emit('serverDescriptionChanged', {
78 topologyId: self.s.topologyId != -1 ? self.s.topologyId : self.id, address: self.name,
79 previousDescription: getPreviousDescription(self),
80 newDescription: description
83 self.s.serverDescription = description;
87 var getPreviousTopologyDescription = function(self) {
88 if(!self.s.topologyDescription) {
89 self.s.topologyDescription = {
90 topologyType: 'Unknown',
92 address: self.name, arbiters: [], hosts: [], passives: [], type: 'Unknown'
97 return self.s.topologyDescription;
100 var emitTopologyDescriptionChanged = function(self, description) {
101 if(self.listeners('topologyDescriptionChanged').length > 0) {
102 // Emit the server description changed events
103 self.emit('topologyDescriptionChanged', {
104 topologyId: self.s.topologyId != -1 ? self.s.topologyId : self.id, address: self.name,
105 previousDescription: getPreviousTopologyDescription(self),
106 newDescription: description
109 self.s.serverDescription = description;
113 var changedIsMaster = function(self, currentIsmaster, ismaster) {
114 var currentType = getTopologyType(self, currentIsmaster);
115 var newType = getTopologyType(self, ismaster);
116 if(newType != currentType) return true;
120 var getTopologyType = function(self, ismaster) {
122 ismaster = self.ismaster;
125 if(!ismaster) return 'Unknown';
126 if(ismaster.ismaster && !ismaster.hosts) return 'Standalone';
127 if(ismaster.ismaster && ismaster.msg == 'isdbgrid') return 'Mongos';
128 if(ismaster.ismaster) return 'RSPrimary';
129 if(ismaster.secondary) return 'RSSecondary';
130 if(ismaster.arbiterOnly) return 'RSArbiter';
134 var inquireServerState = function(self) {
135 return function(callback) {
136 if(self.s.state == 'destroyed') return;
137 // Record response time
138 var start = new Date().getTime();
141 emitSDAMEvent(self, 'serverHeartbeatStarted', { connectionId: self.name });
143 // Attempt to execute ismaster command
144 self.command('admin.$cmd', { ismaster:true }, { monitoring:true }, function(err, r) {
146 // Legacy event sender
147 self.emit('ismaster', r, self);
149 // Calculate latencyMS
150 var latencyMS = new Date().getTime() - start;
152 // Server heart beat event
153 emitSDAMEvent(self, 'serverHeartbeatSucceeded', { durationMS: latencyMS, reply: r.result, connectionId: self.name });
155 // Did the server change
156 if(changedIsMaster(self, self.s.ismaster, r.result)) {
157 // Emit server description changed if something listening
158 emitServerDescriptionChanged(self, {
159 address: self.name, arbiters: [], hosts: [], passives: [], type: !self.s.inTopology ? 'Standalone' : getTopologyType(self)
163 // Updat ismaster view
164 self.s.ismaster = r.result;
166 // Set server response time
167 self.s.isMasterLatencyMS = latencyMS;
169 emitSDAMEvent(self, 'serverHeartbeatFailed', { durationMS: latencyMS, failure: err, connectionId: self.name });
172 // Peforming an ismaster monitoring callback operation
173 if(typeof callback == 'function') {
174 return callback(err, r);
177 // Perform another sweep
178 self.s.inquireServerStateTimeout = setTimeout(inquireServerState(self), self.s.haInterval);
183 // Object.assign method or polyfille
184 var assign = Object.assign ? Object.assign : function assign(target, firstSource) {
185 if (target === undefined || target === null) {
186 throw new TypeError('Cannot convert first argument to object');
189 var to = Object(target);
190 for (var i = 1; i < arguments.length; i++) {
191 var nextSource = arguments[i];
192 if (nextSource === undefined || nextSource === null) {
196 var keysArray = Object.keys(Object(nextSource));
197 for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
198 var nextKey = keysArray[nextIndex];
199 var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
200 if (desc !== undefined && desc.enumerable) {
201 to[nextKey] = nextSource[nextKey];
210 var cloneOptions = function(options) {
212 for(var name in options) {
213 opts[name] = options[name];
218 module.exports.inquireServerState = inquireServerState
219 module.exports.getTopologyType = getTopologyType;
220 module.exports.emitServerDescriptionChanged = emitServerDescriptionChanged;
221 module.exports.emitTopologyDescriptionChanged = emitTopologyDescriptionChanged;
222 module.exports.cloneOptions = cloneOptions;
223 module.exports.assign = assign;
224 module.exports.createClientInfo = createClientInfo;
225 module.exports.clone = clone;