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.

monitoring.js 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. 'use strict';
  2. const ServerDescription = require('./server_description').ServerDescription;
  3. const calculateDurationInMs = require('../utils').calculateDurationInMs;
  4. /**
  5. * Published when server description changes, but does NOT include changes to the RTT.
  6. *
  7. * @property {Object} topologyId A unique identifier for the topology
  8. * @property {ServerAddress} address The address (host/port pair) of the server
  9. * @property {ServerDescription} previousDescription The previous server description
  10. * @property {ServerDescription} newDescription The new server description
  11. */
  12. class ServerDescriptionChangedEvent {
  13. constructor(topologyId, address, previousDescription, newDescription) {
  14. Object.assign(this, { topologyId, address, previousDescription, newDescription });
  15. }
  16. }
  17. /**
  18. * Published when server is initialized.
  19. *
  20. * @property {Object} topologyId A unique identifier for the topology
  21. * @property {ServerAddress} address The address (host/port pair) of the server
  22. */
  23. class ServerOpeningEvent {
  24. constructor(topologyId, address) {
  25. Object.assign(this, { topologyId, address });
  26. }
  27. }
  28. /**
  29. * Published when server is closed.
  30. *
  31. * @property {ServerAddress} address The address (host/port pair) of the server
  32. * @property {Object} topologyId A unique identifier for the topology
  33. */
  34. class ServerClosedEvent {
  35. constructor(topologyId, address) {
  36. Object.assign(this, { topologyId, address });
  37. }
  38. }
  39. /**
  40. * Published when topology description changes.
  41. *
  42. * @property {Object} topologyId
  43. * @property {TopologyDescription} previousDescription The old topology description
  44. * @property {TopologyDescription} newDescription The new topology description
  45. */
  46. class TopologyDescriptionChangedEvent {
  47. constructor(topologyId, previousDescription, newDescription) {
  48. Object.assign(this, { topologyId, previousDescription, newDescription });
  49. }
  50. }
  51. /**
  52. * Published when topology is initialized.
  53. *
  54. * @param {Object} topologyId A unique identifier for the topology
  55. */
  56. class TopologyOpeningEvent {
  57. constructor(topologyId) {
  58. Object.assign(this, { topologyId });
  59. }
  60. }
  61. /**
  62. * Published when topology is closed.
  63. *
  64. * @param {Object} topologyId A unique identifier for the topology
  65. */
  66. class TopologyClosedEvent {
  67. constructor(topologyId) {
  68. Object.assign(this, { topologyId });
  69. }
  70. }
  71. /**
  72. * Fired when the server monitor’s ismaster command is started - immediately before
  73. * the ismaster command is serialized into raw BSON and written to the socket.
  74. *
  75. * @property {Object} connectionId The connection id for the command
  76. */
  77. class ServerHeartbeatStartedEvent {
  78. constructor(connectionId) {
  79. Object.assign(this, { connectionId });
  80. }
  81. }
  82. /**
  83. * Fired when the server monitor’s ismaster succeeds.
  84. *
  85. * @param {Number} duration The execution time of the event in ms
  86. * @param {Object} reply The command reply
  87. * @param {Object} connectionId The connection id for the command
  88. */
  89. class ServerHeartbeatSucceededEvent {
  90. constructor(duration, reply, connectionId) {
  91. Object.assign(this, { duration, reply, connectionId });
  92. }
  93. }
  94. /**
  95. * Fired when the server monitor’s ismaster fails, either with an “ok: 0” or a socket exception.
  96. *
  97. * @param {Number} duration The execution time of the event in ms
  98. * @param {MongoError|Object} failure The command failure
  99. * @param {Object} connectionId The connection id for the command
  100. */
  101. class ServerHeartbeatFailedEvent {
  102. constructor(duration, failure, connectionId) {
  103. Object.assign(this, { duration, failure, connectionId });
  104. }
  105. }
  106. /**
  107. * Performs a server check as described by the SDAM spec.
  108. *
  109. * NOTE: This method automatically reschedules itself, so that there is always an active
  110. * monitoring process
  111. *
  112. * @param {Server} server The server to monitor
  113. */
  114. function monitorServer(server) {
  115. // executes a single check of a server
  116. const checkServer = callback => {
  117. let start = process.hrtime();
  118. // emit a signal indicating we have started the heartbeat
  119. server.emit('serverHeartbeatStarted', new ServerHeartbeatStartedEvent(server.name));
  120. server.command(
  121. 'admin.$cmd',
  122. { ismaster: true },
  123. {
  124. monitoring: true,
  125. socketTimeout: server.s.options.connectionTimeout || 2000
  126. },
  127. function(err, result) {
  128. let duration = calculateDurationInMs(start);
  129. if (err) {
  130. server.emit(
  131. 'serverHeartbeatFailed',
  132. new ServerHeartbeatFailedEvent(duration, err, server.name)
  133. );
  134. return callback(err, null);
  135. }
  136. const isMaster = result.result;
  137. server.emit(
  138. 'serverHeartbeatSucceded',
  139. new ServerHeartbeatSucceededEvent(duration, isMaster, server.name)
  140. );
  141. return callback(null, isMaster);
  142. }
  143. );
  144. };
  145. const successHandler = isMaster => {
  146. server.s.monitoring = false;
  147. // emit an event indicating that our description has changed
  148. server.emit('descriptionReceived', new ServerDescription(server.description.address, isMaster));
  149. // schedule the next monitoring process
  150. server.s.monitorId = setTimeout(
  151. () => monitorServer(server),
  152. server.s.options.heartbeatFrequencyMS
  153. );
  154. };
  155. // run the actual monitoring loop
  156. server.s.monitoring = true;
  157. checkServer((err, isMaster) => {
  158. if (!err) {
  159. successHandler(isMaster);
  160. return;
  161. }
  162. // According to the SDAM specification's "Network error during server check" section, if
  163. // an ismaster call fails we reset the server's pool. If a server was once connected,
  164. // change its type to `Unknown` only after retrying once.
  165. // TODO: we need to reset the pool here
  166. return checkServer((err, isMaster) => {
  167. if (err) {
  168. server.s.monitoring = false;
  169. // revert to `Unknown` by emitting a default description with no isMaster
  170. server.emit('descriptionReceived', new ServerDescription(server.description.address));
  171. // do not reschedule monitoring in this case
  172. return;
  173. }
  174. successHandler(isMaster);
  175. });
  176. });
  177. }
  178. module.exports = {
  179. ServerDescriptionChangedEvent,
  180. ServerOpeningEvent,
  181. ServerClosedEvent,
  182. TopologyDescriptionChangedEvent,
  183. TopologyOpeningEvent,
  184. TopologyClosedEvent,
  185. ServerHeartbeatStartedEvent,
  186. ServerHeartbeatSucceededEvent,
  187. ServerHeartbeatFailedEvent,
  188. monitorServer
  189. };