1
1
'use strict' ;
2
2
3
3
const { ServerType, STATE_CLOSED , STATE_CLOSING } = require ( './common' ) ;
4
- const { makeStateMachine, calculateDurationInMs } = require ( '../utils' ) ;
4
+ const { makeStateMachine, calculateDurationInMs, makeInterruptableAsyncInterval } = require ( '../utils' ) ;
5
5
const EventEmitter = require ( 'events' ) ;
6
6
const connect = require ( '../cmap/connect' ) ;
7
7
const { Connection } = require ( '../cmap/connection' ) ;
@@ -17,7 +17,6 @@ const kServer = Symbol('server');
17
17
const kMonitorId = Symbol ( 'monitorId' ) ;
18
18
const kConnection = Symbol ( 'connection' ) ;
19
19
const kCancellationToken = Symbol ( 'cancellationToken' ) ;
20
- const kLastCheckTime = Symbol ( 'lastCheckTime' ) ;
21
20
const kRTTPinger = Symbol ( 'rttPinger' ) ;
22
21
const kRoundTripTime = Symbol ( 'roundTripTime' ) ;
23
22
@@ -32,6 +31,10 @@ const stateTransition = makeStateMachine({
32
31
33
32
const INVALID_REQUEST_CHECK_STATES = new Set ( [ STATE_CLOSING , STATE_CLOSED , STATE_MONITORING ] ) ;
34
33
34
+ function isInCloseState ( monitor ) {
35
+ return monitor . s . state === STATE_CLOSED || monitor . s . state === STATE_CLOSING ;
36
+ }
37
+
35
38
class Monitor extends EventEmitter {
36
39
constructor ( server , options ) {
37
40
super ( options ) ;
@@ -40,6 +43,7 @@ class Monitor extends EventEmitter {
40
43
this [ kConnection ] = undefined ;
41
44
this [ kCancellationToken ] = new EventEmitter ( ) ;
42
45
this [ kCancellationToken ] . setMaxListeners ( Infinity ) ;
46
+ this [ kMonitorId ] = null ;
43
47
this . s = {
44
48
state : STATE_CLOSED
45
49
} ;
@@ -89,33 +93,26 @@ class Monitor extends EventEmitter {
89
93
return ;
90
94
}
91
95
92
- monitorServer ( this ) ;
96
+ // start
97
+ const heartbeatFrequencyMS = this . options . heartbeatFrequencyMS ;
98
+ const minHeartbeatFrequencyMS = this . options . minHeartbeatFrequencyMS ;
99
+ this [ kMonitorId ] = makeInterruptableAsyncInterval ( monitorServer ( this ) , {
100
+ interval : heartbeatFrequencyMS ,
101
+ minInterval : minHeartbeatFrequencyMS ,
102
+ immediate : true
103
+ } ) ;
93
104
}
94
105
95
106
requestCheck ( ) {
96
107
if ( INVALID_REQUEST_CHECK_STATES . has ( this . s . state ) ) {
97
108
return ;
98
109
}
99
110
100
- const heartbeatFrequencyMS = this . options . heartbeatFrequencyMS ;
101
- const minHeartbeatFrequencyMS = this . options . minHeartbeatFrequencyMS ;
102
- const remainingTime = heartbeatFrequencyMS - calculateDurationInMs ( this [ kLastCheckTime ] ) ;
103
- if ( remainingTime > minHeartbeatFrequencyMS && this [ kMonitorId ] ) {
104
- clearTimeout ( this [ kMonitorId ] ) ;
105
- this [ kMonitorId ] = undefined ;
106
-
107
- rescheduleMonitoring ( this , minHeartbeatFrequencyMS ) ;
108
- return ;
109
- }
110
-
111
- clearTimeout ( this [ kMonitorId ] ) ;
112
- this [ kMonitorId ] = undefined ;
113
-
114
- monitorServer ( this ) ;
111
+ this [ kMonitorId ] . wake ( ) ;
115
112
}
116
113
117
114
reset ( ) {
118
- if ( this . s . state === STATE_CLOSED || this . s . state === STATE_CLOSING ) {
115
+ if ( isInCloseState ( this ) ) {
119
116
return ;
120
117
}
121
118
@@ -124,12 +121,19 @@ class Monitor extends EventEmitter {
124
121
125
122
// restart monitor
126
123
stateTransition ( this , STATE_IDLE ) ;
124
+
125
+ // restart monitoring
127
126
const heartbeatFrequencyMS = this . options . heartbeatFrequencyMS ;
128
- this [ kMonitorId ] = setTimeout ( ( ) => this . requestCheck ( ) , heartbeatFrequencyMS ) ;
127
+ const minHeartbeatFrequencyMS = this . options . minHeartbeatFrequencyMS ;
128
+ this [ kMonitorId ] = makeInterruptableAsyncInterval ( monitorServer ( this ) , {
129
+ interval : heartbeatFrequencyMS ,
130
+ minInterval : minHeartbeatFrequencyMS ,
131
+ immediate : true
132
+ } ) ;
129
133
}
130
134
131
135
close ( ) {
132
- if ( this . s . state === STATE_CLOSED || this . s . state === STATE_CLOSING ) {
136
+ if ( isInCloseState ( this ) ) {
133
137
return ;
134
138
}
135
139
@@ -144,6 +148,11 @@ class Monitor extends EventEmitter {
144
148
145
149
function resetMonitorState ( monitor ) {
146
150
stateTransition ( monitor , STATE_CLOSING ) ;
151
+ if ( monitor [ kMonitorId ] ) {
152
+ monitor [ kMonitorId ] . stop ( ) ;
153
+ monitor [ kMonitorId ] = null ;
154
+ }
155
+
147
156
if ( monitor [ kRTTPinger ] ) {
148
157
monitor [ kRTTPinger ] . close ( ) ;
149
158
monitor [ kRTTPinger ] = undefined ;
@@ -214,7 +223,7 @@ function checkServer(monitor, callback) {
214
223
new ServerHeartbeatSucceededEvent ( duration , isMaster , monitor . address )
215
224
) ;
216
225
217
- // if we are streaming ismaster responses then we immediately issue another started
226
+ // if we are using the streaming protocol then we immediately issue another ` started`
218
227
// event, otherwise the "check" is complete and return to the main monitor loop
219
228
if ( isAwaitable && isMaster . topologyVersion ) {
220
229
monitor . emit ( 'serverHeartbeatStarted' , new ServerHeartbeatStartedEvent ( monitor . address ) ) ;
@@ -234,7 +243,7 @@ function checkServer(monitor, callback) {
234
243
235
244
// connecting does an implicit `ismaster`
236
245
connect ( monitor . connectOptions , monitor [ kCancellationToken ] , ( err , conn ) => {
237
- if ( conn && ( monitor . s . state === STATE_CLOSED || monitor . s . state === STATE_CLOSING ) ) {
246
+ if ( conn && isInCloseState ( monitor ) ) {
238
247
conn . destroy ( { force : true } ) ;
239
248
return ;
240
249
}
@@ -266,46 +275,40 @@ function checkServer(monitor, callback) {
266
275
}
267
276
268
277
function monitorServer ( monitor ) {
269
- stateTransition ( monitor , STATE_MONITORING ) ;
270
-
271
- // TODO: the next line is a legacy event, remove in v4
272
- process . nextTick ( ( ) => monitor . emit ( 'monitoring' , monitor [ kServer ] ) ) ;
273
-
274
- const heartbeatFrequencyMS = monitor . options . heartbeatFrequencyMS ;
275
- checkServer ( monitor , ( err , isMaster ) => {
276
- if ( err ) {
277
- if ( monitor [ kServer ] . description . type !== ServerType . Unknown ) {
278
- rescheduleMonitoring ( monitor ) ;
279
- return ;
278
+ return callback => {
279
+ stateTransition ( monitor , STATE_MONITORING ) ;
280
+ function done ( ) {
281
+ if ( ! isInCloseState ( monitor ) ) {
282
+ stateTransition ( monitor , STATE_IDLE ) ;
280
283
}
281
- }
282
284
283
- // if the check indicates streaming is supported, immediately reschedule monitoring
284
- if ( isMaster && isMaster . topologyVersion ) {
285
- rescheduleMonitoring ( monitor ) ;
286
- return ;
285
+ callback ( ) ;
287
286
}
288
287
289
- rescheduleMonitoring ( monitor , heartbeatFrequencyMS ) ;
290
- } ) ;
291
- }
288
+ // TODO: the next line is a legacy event, remove in v4
289
+ process . nextTick ( ( ) => monitor . emit ( 'monitoring' , monitor [ kServer ] ) ) ;
292
290
293
- function rescheduleMonitoring ( monitor , ms ) {
294
- if ( monitor . s . state === STATE_CLOSING || monitor . s . state === STATE_CLOSED ) {
295
- return ;
296
- }
291
+ checkServer ( monitor , ( err , isMaster ) => {
292
+ if ( err ) {
293
+ // otherwise an error occured on initial discovery, also bail
294
+ if ( monitor [ kServer ] . description . type === ServerType . Unknown ) {
295
+ monitor . emit ( 'resetServer' , err ) ;
296
+ return done ( ) ;
297
+ }
298
+ }
297
299
298
- stateTransition ( monitor , STATE_IDLE ) ;
299
- if ( monitor [ kMonitorId ] ) {
300
- clearTimeout ( monitor [ kMonitorId ] ) ;
301
- monitor [ kMonitorId ] = undefined ;
302
- }
300
+ // if the check indicates streaming is supported, immediately reschedule monitoring
301
+ if ( isMaster && isMaster . topologyVersion ) {
302
+ setTimeout ( ( ) => {
303
+ if ( ! isInCloseState ( monitor ) ) {
304
+ monitor [ kMonitorId ] . wake ( ) ;
305
+ }
306
+ } ) ;
307
+ }
303
308
304
- monitor [ kLastCheckTime ] = process . hrtime ( ) ;
305
- monitor [ kMonitorId ] = setTimeout ( ( ) => {
306
- monitor [ kMonitorId ] = undefined ;
307
- monitor . requestCheck ( ) ;
308
- } , ms ) ;
309
+ done ( ) ;
310
+ } ) ;
311
+ } ;
309
312
}
310
313
311
314
function makeTopologyVersion ( tv ) {
0 commit comments