1
+ import { setTimeout } from 'node:timers/promises' ;
2
+
3
+ import { expect } from 'chai' ;
4
+ import * as sinon from 'sinon' ;
5
+
6
+ import {
7
+ Connection ,
8
+ type MongoClient ,
9
+ promiseWithResolvers ,
10
+ type ServerHeartbeatSucceededEvent
11
+ } from '../../mongodb' ;
1
12
import { loadSpecTests } from '../../spec' ;
2
13
import { runUnifiedSuite } from '../../tools/unified-spec-runner/runner' ;
3
14
@@ -8,3 +19,105 @@ describe('SDAM Unified Tests (Node Driver)', function () {
8
19
) ;
9
20
runUnifiedSuite ( clonedAndAlteredSpecTests ) ;
10
21
} ) ;
22
+
23
+ describe ( 'Monitoring rtt tests' , function ( ) {
24
+ let client : MongoClient ;
25
+ let heartbeatDurations : Record < string , number [ ] > ;
26
+ const HEARTBEATS_TO_COLLECT_PER_NODE = 65 ;
27
+ const IGNORE_SIZE = 5 ;
28
+ const DELAY_MS = 10 ;
29
+
30
+ beforeEach ( function ( ) {
31
+ heartbeatDurations = Object . create ( null ) ;
32
+ } ) ;
33
+
34
+ afterEach ( async function ( ) {
35
+ if ( client ) {
36
+ await client . close ( ) ;
37
+ }
38
+ sinon . restore ( ) ;
39
+ } ) ;
40
+
41
+ for ( const serverMonitoringMode of [ 'poll' , 'stream' ] ) {
42
+ context ( `when serverMonitoringMode is set to '${ serverMonitoringMode } '` , function ( ) {
43
+ context ( 'after collecting a number of heartbeats' , function ( ) {
44
+ beforeEach ( async function ( ) {
45
+ client = this . configuration . newClient ( {
46
+ heartbeatFrequencyMS : 100 ,
47
+ serverMonitoringMode
48
+ } ) ;
49
+
50
+ // make sendCommand delay for DELAY_MS ms to ensure that the actual time between sending
51
+ // a heartbeat and receiving a response don't drop below 1ms. This is done since our
52
+ // testing is colocated with its mongo deployment so network latency is very low
53
+ const stub = sinon
54
+ // @ts -expect-error accessing private method
55
+ . stub ( Connection . prototype , 'sendCommand' )
56
+ . callsFake ( async function * ( ...args ) {
57
+ await setTimeout ( DELAY_MS ) ;
58
+ yield * stub . wrappedMethod . call ( this , ...args ) ;
59
+ } ) ;
60
+ await client . connect ( ) ;
61
+
62
+ const { promise, resolve } = promiseWithResolvers < void > ( ) ;
63
+ client . on ( 'serverHeartbeatSucceeded' , ( ev : ServerHeartbeatSucceededEvent ) => {
64
+ heartbeatDurations [ ev . connectionId ] ??= [ ] ;
65
+ if (
66
+ heartbeatDurations [ ev . connectionId ] . length <
67
+ HEARTBEATS_TO_COLLECT_PER_NODE + IGNORE_SIZE
68
+ )
69
+ heartbeatDurations [ ev . connectionId ] . push ( ev . duration ) ;
70
+
71
+ // We ignore the first few heartbeats since the problem reported in NODE-6172 showed that the
72
+ // first few heartbeats were recorded properly
73
+ if (
74
+ Object . keys ( heartbeatDurations ) . length === client . topology . s . servers . size &&
75
+ Object . values ( heartbeatDurations ) . every (
76
+ d => d . length === HEARTBEATS_TO_COLLECT_PER_NODE + IGNORE_SIZE
77
+ )
78
+ ) {
79
+ client . removeAllListeners ( 'serverHeartbeatSucceeded' ) ;
80
+ resolve ( ) ;
81
+ }
82
+ } ) ;
83
+ await promise ;
84
+ } ) ;
85
+
86
+ it (
87
+ 'heartbeat duration is not incorrectly reported as zero on ServerHeartbeatSucceededEvents' ,
88
+ {
89
+ metadata : {
90
+ requires : { topology : '!load-balanced' }
91
+ } ,
92
+ test : async function ( ) {
93
+ for ( const durations of Object . values ( heartbeatDurations ) ) {
94
+ const relevantDurations = durations . slice ( IGNORE_SIZE ) ;
95
+ expect ( relevantDurations ) . to . have . length . gt ( 0 ) ;
96
+ const averageDuration =
97
+ relevantDurations . reduce ( ( acc , x ) => acc + x ) / relevantDurations . length ;
98
+ expect ( averageDuration ) . to . be . gt ( DELAY_MS ) ;
99
+ }
100
+ }
101
+ }
102
+ ) ;
103
+
104
+ it ( 'ServerDescription.roundTripTime is not incorrectly reported as zero' , {
105
+ metadata : {
106
+ requires : { topology : '!load-balanced' }
107
+ } ,
108
+ test : async function ( ) {
109
+ for ( const [ server , durations ] of Object . entries ( heartbeatDurations ) ) {
110
+ const relevantDurations = durations . slice ( IGNORE_SIZE ) ;
111
+ expect ( relevantDurations ) . to . have . length . gt ( 0 ) ;
112
+ const averageDuration =
113
+ relevantDurations . reduce ( ( acc , x ) => acc + x ) / relevantDurations . length ;
114
+ const rtt = client . topology . description . servers . get ( server ) . roundTripTime ;
115
+ expect ( rtt ) . to . not . equal ( 0 ) ;
116
+ expect ( rtt ) . to . be . approximately ( averageDuration , 3 ) ;
117
+ }
118
+ }
119
+ } ) ;
120
+ } ) ;
121
+ } ) ;
122
+ }
123
+ } ) ;
0 commit comments