/node_modules/mongodb-core/lib/topologies/shared.js

https://bitbucket.org/coleman333/smartsite · JavaScript · 334 lines · 242 code · 52 blank · 40 comment · 53 complexity · 79a8f638645d81efaa8de33f1b15dd57 MD5 · raw file

  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('../../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.msg == 'isdbgrid') return 'Mongos';
  108. if(ismaster.ismaster && !ismaster.hosts) return 'Standalone';
  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. //
  154. // Clone the options
  155. var cloneOptions = function(options) {
  156. var opts = {};
  157. for(var name in options) {
  158. opts[name] = options[name];
  159. }
  160. return opts;
  161. }
  162. function Interval(fn, time) {
  163. var timer = false;
  164. this.start = function () {
  165. if (!this.isRunning()) {
  166. timer = setInterval(fn, time);
  167. }
  168. return this;
  169. };
  170. this.stop = function () {
  171. clearInterval(timer);
  172. timer = false;
  173. return this;
  174. };
  175. this.isRunning = function () {
  176. return timer !== false;
  177. };
  178. }
  179. function Timeout(fn, time) {
  180. var timer = false;
  181. this.start = function () {
  182. if (!this.isRunning()) {
  183. timer = setTimeout(function() {
  184. fn();
  185. if (timer && timer._called === undefined) {
  186. // The artificial _called is set here for compatibility with node.js 0.10.x/0.12.x versions
  187. timer._called = true;
  188. }
  189. }, time);
  190. }
  191. return this;
  192. };
  193. this.stop = function () {
  194. clearTimeout(timer);
  195. timer = false;
  196. return this;
  197. };
  198. this.isRunning = function () {
  199. if(timer && timer._called) return false;
  200. return timer !== false;
  201. };
  202. }
  203. function diff(previous, current) {
  204. // Difference document
  205. var diff = {
  206. servers: []
  207. }
  208. // Previous entry
  209. if(!previous) {
  210. previous = { servers: [] };
  211. }
  212. // Check if we have any previous servers missing in the current ones
  213. for(var i = 0; i < previous.servers.length; i++) {
  214. var found = false;
  215. for(var j = 0; j < current.servers.length; j++) {
  216. if(current.servers[j].address.toLowerCase()
  217. === previous.servers[i].address.toLowerCase()) {
  218. found = true;
  219. break;
  220. }
  221. }
  222. if(!found) {
  223. // Add to the diff
  224. diff.servers.push({
  225. address: previous.servers[i].address,
  226. from: previous.servers[i].type,
  227. to: 'Unknown',
  228. });
  229. }
  230. }
  231. // Check if there are any severs that don't exist
  232. for(var j = 0; j < current.servers.length; j++) {
  233. var found = false;
  234. // Go over all the previous servers
  235. for(var i = 0; i < previous.servers.length; i++) {
  236. if(previous.servers[i].address.toLowerCase()
  237. === current.servers[j].address.toLowerCase()) {
  238. found = true;
  239. break;
  240. }
  241. }
  242. // Add the server to the diff
  243. if(!found) {
  244. diff.servers.push({
  245. address: current.servers[j].address,
  246. from: 'Unknown',
  247. to: current.servers[j].type,
  248. });
  249. }
  250. }
  251. // Got through all the servers
  252. for(var i = 0; i < previous.servers.length; i++) {
  253. var prevServer = previous.servers[i];
  254. // Go through all current servers
  255. for(var j = 0; j < current.servers.length; j++) {
  256. var currServer = current.servers[j];
  257. // Matching server
  258. if(prevServer.address.toLowerCase() === currServer.address.toLowerCase()) {
  259. // We had a change in state
  260. if(prevServer.type != currServer.type) {
  261. diff.servers.push({
  262. address: prevServer.address,
  263. from: prevServer.type,
  264. to: currServer.type
  265. });
  266. }
  267. }
  268. }
  269. }
  270. // Return difference
  271. return diff;
  272. }
  273. module.exports.inquireServerState = inquireServerState
  274. module.exports.getTopologyType = getTopologyType;
  275. module.exports.emitServerDescriptionChanged = emitServerDescriptionChanged;
  276. module.exports.emitTopologyDescriptionChanged = emitTopologyDescriptionChanged;
  277. module.exports.cloneOptions = cloneOptions;
  278. module.exports.createClientInfo = createClientInfo;
  279. module.exports.clone = clone;
  280. module.exports.diff = diff;
  281. module.exports.Interval = Interval;
  282. module.exports.Timeout = Timeout;