shared.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. "use strict"
  2. var os = require('os'),
  3. f = require('util').format;
  4. /**
  5. * Emit event if it exists
  6. * @method
  7. */
  8. function emitSDAMEvent(self, event, description) {
  9. if(self.listeners(event).length > 0) {
  10. self.emit(event, description);
  11. }
  12. }
  13. // Get package.json variable
  14. var driverVersion = require(__dirname + '/../../package.json').version;
  15. var nodejsversion = f('Node.js %s, %s', process.version, os.endianness());
  16. var type = os.type();
  17. var name = process.platform;
  18. var architecture = process.arch;
  19. var release = os.release();
  20. function createClientInfo(options) {
  21. // Build default client information
  22. var clientInfo = options.clientInfo ? clone(options.clientInfo) : {
  23. driver: {
  24. name: "nodejs-core",
  25. version: driverVersion
  26. },
  27. os: {
  28. type: type,
  29. name: name,
  30. architecture: architecture,
  31. version: release
  32. }
  33. }
  34. // Is platform specified
  35. if(clientInfo.platform && clientInfo.platform.indexOf('mongodb-core') == -1) {
  36. clientInfo.platform = f('%s, mongodb-core: %s', clientInfo.platform, driverVersion);
  37. } else if(!clientInfo.platform){
  38. clientInfo.platform = nodejsversion;
  39. }
  40. // Do we have an application specific string
  41. if(options.appname) {
  42. // Cut at 128 bytes
  43. var buffer = new Buffer(options.appname);
  44. // Return the truncated appname
  45. var appname = buffer.length > 128 ? buffer.slice(0, 128).toString('utf8') : options.appname;
  46. // Add to the clientInfo
  47. clientInfo.application = { name: appname };
  48. }
  49. return clientInfo;
  50. }
  51. function clone(object) {
  52. return JSON.parse(JSON.stringify(object));
  53. }
  54. var getPreviousDescription = function(self) {
  55. if(!self.s.serverDescription) {
  56. self.s.serverDescription = {
  57. address: self.name,
  58. arbiters: [], hosts: [], passives: [], type: 'Unknown'
  59. }
  60. }
  61. return self.s.serverDescription;
  62. }
  63. var emitServerDescriptionChanged = function(self, description) {
  64. if(self.listeners('serverDescriptionChanged').length > 0) {
  65. // Emit the server description changed events
  66. self.emit('serverDescriptionChanged', {
  67. topologyId: self.s.topologyId != -1 ? self.s.topologyId : self.id, address: self.name,
  68. previousDescription: getPreviousDescription(self),
  69. newDescription: description
  70. });
  71. self.s.serverDescription = description;
  72. }
  73. }
  74. var getPreviousTopologyDescription = function(self) {
  75. if(!self.s.topologyDescription) {
  76. self.s.topologyDescription = {
  77. topologyType: 'Unknown',
  78. servers: [{
  79. address: self.name, arbiters: [], hosts: [], passives: [], type: 'Unknown'
  80. }]
  81. }
  82. }
  83. return self.s.topologyDescription;
  84. }
  85. var emitTopologyDescriptionChanged = function(self, description) {
  86. if(self.listeners('topologyDescriptionChanged').length > 0) {
  87. // Emit the server description changed events
  88. self.emit('topologyDescriptionChanged', {
  89. topologyId: self.s.topologyId != -1 ? self.s.topologyId : self.id, address: self.name,
  90. previousDescription: getPreviousTopologyDescription(self),
  91. newDescription: description
  92. });
  93. self.s.serverDescription = description;
  94. }
  95. }
  96. var changedIsMaster = function(self, currentIsmaster, ismaster) {
  97. var currentType = getTopologyType(self, currentIsmaster);
  98. var newType = getTopologyType(self, ismaster);
  99. if(newType != currentType) return true;
  100. return false;
  101. }
  102. var getTopologyType = function(self, ismaster) {
  103. if(!ismaster) {
  104. ismaster = self.ismaster;
  105. }
  106. if(!ismaster) return 'Unknown';
  107. if(ismaster.ismaster && !ismaster.hosts) return 'Standalone';
  108. if(ismaster.ismaster && ismaster.msg == 'isdbgrid') return 'Mongos';
  109. if(ismaster.ismaster) return 'RSPrimary';
  110. if(ismaster.secondary) return 'RSSecondary';
  111. if(ismaster.arbiterOnly) return 'RSArbiter';
  112. return 'Unknown';
  113. }
  114. var inquireServerState = function(self) {
  115. return function(callback) {
  116. if(self.s.state == 'destroyed') return;
  117. // Record response time
  118. var start = new Date().getTime();
  119. // emitSDAMEvent
  120. emitSDAMEvent(self, 'serverHeartbeatStarted', { connectionId: self.name });
  121. // Attempt to execute ismaster command
  122. self.command('admin.$cmd', { ismaster:true }, { monitoring:true }, function(err, r) {
  123. if(!err) {
  124. // Legacy event sender
  125. self.emit('ismaster', r, self);
  126. // Calculate latencyMS
  127. var latencyMS = new Date().getTime() - start;
  128. // Server heart beat event
  129. emitSDAMEvent(self, 'serverHeartbeatSucceeded', { durationMS: latencyMS, reply: r.result, connectionId: self.name });
  130. // Did the server change
  131. if(changedIsMaster(self, self.s.ismaster, r.result)) {
  132. // Emit server description changed if something listening
  133. emitServerDescriptionChanged(self, {
  134. address: self.name, arbiters: [], hosts: [], passives: [], type: !self.s.inTopology ? 'Standalone' : getTopologyType(self)
  135. });
  136. }
  137. // Updat ismaster view
  138. self.s.ismaster = r.result;
  139. // Set server response time
  140. self.s.isMasterLatencyMS = latencyMS;
  141. } else {
  142. emitSDAMEvent(self, 'serverHeartbeatFailed', { durationMS: latencyMS, failure: err, connectionId: self.name });
  143. }
  144. // Peforming an ismaster monitoring callback operation
  145. if(typeof callback == 'function') {
  146. return callback(err, r);
  147. }
  148. // Perform another sweep
  149. self.s.inquireServerStateTimeout = setTimeout(inquireServerState(self), self.s.haInterval);
  150. });
  151. };
  152. }
  153. // Object.assign method or polyfille
  154. var assign = Object.assign ? Object.assign : function assign(target) {
  155. if (target === undefined || target === null) {
  156. throw new TypeError('Cannot convert first argument to object');
  157. }
  158. var to = Object(target);
  159. for (var i = 1; i < arguments.length; i++) {
  160. var nextSource = arguments[i];
  161. if (nextSource === undefined || nextSource === null) {
  162. continue;
  163. }
  164. var keysArray = Object.keys(Object(nextSource));
  165. for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
  166. var nextKey = keysArray[nextIndex];
  167. var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
  168. if (desc !== undefined && desc.enumerable) {
  169. to[nextKey] = nextSource[nextKey];
  170. }
  171. }
  172. }
  173. return to;
  174. }
  175. //
  176. // Clone the options
  177. var cloneOptions = function(options) {
  178. var opts = {};
  179. for(var name in options) {
  180. opts[name] = options[name];
  181. }
  182. return opts;
  183. }
  184. module.exports.inquireServerState = inquireServerState
  185. module.exports.getTopologyType = getTopologyType;
  186. module.exports.emitServerDescriptionChanged = emitServerDescriptionChanged;
  187. module.exports.emitTopologyDescriptionChanged = emitTopologyDescriptionChanged;
  188. module.exports.cloneOptions = cloneOptions;
  189. module.exports.assign = assign;
  190. module.exports.createClientInfo = createClientInfo;
  191. module.exports.clone = clone;