1
1
'use strict' ;
2
2
3
+ const ServerDescription = require ( './server_description' ) . ServerDescription ;
4
+ const calculateDurationInMs = require ( '../utils' ) . calculateDurationInMs ;
5
+
3
6
/**
4
7
* Published when server description changes, but does NOT include changes to the RTT.
5
8
*
@@ -88,7 +91,7 @@ class ServerHeartbeatStartedEvent {
88
91
/**
89
92
* Fired when the server monitor’s ismaster succeeds.
90
93
*
91
- * @param {Number } duration The execution time of the event
94
+ * @param {Number } duration The execution time of the event in ms
92
95
* @param {Object } reply The command reply
93
96
* @param {Object } connectionId The connection id for the command
94
97
*/
@@ -101,16 +104,104 @@ class ServerHeartbeatSucceededEvent {
101
104
/**
102
105
* Fired when the server monitor’s ismaster fails, either with an “ok: 0” or a socket exception.
103
106
*
104
- * @param {Number } duration The execution time of the event
107
+ * @param {Number } duration The execution time of the event in ms
105
108
* @param {MongoError|Object } failure The command failure
106
109
* @param {Object } connectionId The connection id for the command
107
110
*/
108
- class ServerHearbeatFailedEvent {
111
+ class ServerHeartbeatFailedEvent {
109
112
constructor ( duration , failure , connectionId ) {
110
113
Object . assign ( this , { duration, failure, connectionId } ) ;
111
114
}
112
115
}
113
116
117
+ /**
118
+ * Performs a server check as described by the SDAM spec.
119
+ *
120
+ * NOTE: This method automatically reschedules itself, so that there is always an active
121
+ * monitoring process
122
+ *
123
+ * @param {Server } server The server to monitor
124
+ */
125
+ function monitorServer ( server ) {
126
+ // executes a single check of a server
127
+ const checkServer = callback => {
128
+ let start = process . hrtime ( ) ;
129
+
130
+ // emit a signal indicating we have started the heartbeat
131
+ server . emit ( 'serverHeartbeatStarted' , new ServerHeartbeatStartedEvent ( server . name ) ) ;
132
+
133
+ server . command (
134
+ 'admin.$cmd' ,
135
+ { ismaster : true } ,
136
+ {
137
+ monitoring : true ,
138
+ socketTimeout : server . s . options . connectionTimeout || 2000
139
+ } ,
140
+ function ( err , result ) {
141
+ let duration = calculateDurationInMs ( start ) ;
142
+
143
+ if ( err ) {
144
+ server . emit (
145
+ 'serverHeartbeatFailed' ,
146
+ new ServerHeartbeatFailedEvent ( duration , err , server . name )
147
+ ) ;
148
+
149
+ return callback ( err , null ) ;
150
+ }
151
+
152
+ const isMaster = result . result ;
153
+ server . emit (
154
+ 'serverHeartbeatSucceded' ,
155
+ new ServerHeartbeatSucceededEvent ( duration , isMaster , server . name )
156
+ ) ;
157
+
158
+ return callback ( null , isMaster ) ;
159
+ }
160
+ ) ;
161
+ } ;
162
+
163
+ const successHandler = isMaster => {
164
+ server . s . monitoring = false ;
165
+
166
+ // emit an event indicating that our description has changed
167
+ server . emit ( 'descriptionReceived' , new ServerDescription ( server . description . address , isMaster ) ) ;
168
+
169
+ // schedule the next monitoring process
170
+ server . s . monitorId = setTimeout (
171
+ ( ) => monitorServer ( server ) ,
172
+ server . s . options . heartbeatFrequencyMS
173
+ ) ;
174
+ } ;
175
+
176
+ // run the actual monitoring loop
177
+ server . s . monitoring = true ;
178
+ checkServer ( ( err , isMaster ) => {
179
+ if ( err ) {
180
+ // According to the SDAM specification's "Network error during server check" section, if
181
+ // an ismaster call fails we reset the server's pool. If a server was once connected,
182
+ // change its type to `Unknown` only after retrying once.
183
+
184
+ // TODO: we need to reset the pool here
185
+
186
+ return checkServer ( ( err , isMaster ) => {
187
+ if ( err ) {
188
+ server . s . monitoring = false ;
189
+
190
+ // we revert to an `Unknown` by emitting a default description with no isMaster
191
+ server . emit ( 'descriptionReceived' , new ServerDescription ( server . description . address ) ) ;
192
+
193
+ // we do not reschedule monitoring in this case
194
+ return ;
195
+ }
196
+
197
+ successHandler ( isMaster ) ;
198
+ } ) ;
199
+ }
200
+
201
+ successHandler ( isMaster ) ;
202
+ } ) ;
203
+ }
204
+
114
205
module . exports = {
115
206
ServerDescriptionChangedEvent,
116
207
ServerOpeningEvent,
@@ -120,5 +211,6 @@ module.exports = {
120
211
TopologyClosedEvent,
121
212
ServerHeartbeatStartedEvent,
122
213
ServerHeartbeatSucceededEvent,
123
- ServerHearbeatFailedEvent
214
+ ServerHeartbeatFailedEvent,
215
+ monitorServer
124
216
} ;
0 commit comments