@@ -3,6 +3,7 @@ import { clearTimeout, setTimeout } from 'timers';
33import { type Document , Long } from '../bson' ;
44import { connect } from '../cmap/connect' ;
55import { Connection , type ConnectionOptions } from '../cmap/connection' ;
6+ import { getFAASEnv } from '../cmap/handshake/client_metadata' ;
67import { LEGACY_HELLO_COMMAND } from '../constants' ;
78import { MongoError , MongoErrorLabel , MongoNetworkTimeoutError } from '../error' ;
89import { CancellationToken , TypedEventEmitter } from '../mongo_types' ;
@@ -44,6 +45,16 @@ function isInCloseState(monitor: Monitor) {
4445 return monitor . s . state === STATE_CLOSED || monitor . s . state === STATE_CLOSING ;
4546}
4647
48+ /** @public */
49+ export const ServerMonitoringMode = Object . freeze ( {
50+ auto : 'auto' ,
51+ poll : 'poll' ,
52+ stream : 'stream'
53+ } as const ) ;
54+
55+ /** @public */
56+ export type ServerMonitoringMode = ( typeof ServerMonitoringMode ) [ keyof typeof ServerMonitoringMode ] ;
57+
4758/** @internal */
4859export interface MonitorPrivate {
4960 state : string ;
@@ -55,6 +66,7 @@ export interface MonitorOptions
5566 connectTimeoutMS : number ;
5667 heartbeatFrequencyMS : number ;
5768 minHeartbeatFrequencyMS : number ;
69+ serverMonitoringMode : ServerMonitoringMode ;
5870}
5971
6072/** @public */
@@ -73,9 +85,16 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
7385 s : MonitorPrivate ;
7486 address : string ;
7587 options : Readonly <
76- Pick < MonitorOptions , 'connectTimeoutMS' | 'heartbeatFrequencyMS' | 'minHeartbeatFrequencyMS' >
88+ Pick <
89+ MonitorOptions ,
90+ | 'connectTimeoutMS'
91+ | 'heartbeatFrequencyMS'
92+ | 'minHeartbeatFrequencyMS'
93+ | 'serverMonitoringMode'
94+ >
7795 > ;
7896 connectOptions : ConnectionOptions ;
97+ isRunningInFaasEnv : boolean ;
7998 [ kServer ] : Server ;
8099 [ kConnection ] ?: Connection ;
81100 [ kCancellationToken ] : CancellationToken ;
@@ -103,8 +122,10 @@ export class Monitor extends TypedEventEmitter<MonitorEvents> {
103122 this . options = Object . freeze ( {
104123 connectTimeoutMS : options . connectTimeoutMS ?? 10000 ,
105124 heartbeatFrequencyMS : options . heartbeatFrequencyMS ?? 10000 ,
106- minHeartbeatFrequencyMS : options . minHeartbeatFrequencyMS ?? 500
125+ minHeartbeatFrequencyMS : options . minHeartbeatFrequencyMS ?? 500 ,
126+ serverMonitoringMode : options . serverMonitoringMode
107127 } ) ;
128+ this . isRunningInFaasEnv = getFAASEnv ( ) != null ;
108129
109130 const cancellationToken = this [ kCancellationToken ] ;
110131 // TODO: refactor this to pull it directly from the pool, requires new ConnectionPool integration
@@ -207,27 +228,38 @@ function resetMonitorState(monitor: Monitor) {
207228 monitor [ kConnection ] = undefined ;
208229}
209230
231+ function useStreamingProtocol ( monitor : Monitor , topologyVersion : TopologyVersion | null ) : boolean {
232+ // If we have no topology version we always poll no matter
233+ // what the user provided, since the server does not support
234+ // the streaming protocol.
235+ if ( topologyVersion == null ) return false ;
236+
237+ const serverMonitoringMode = monitor . options . serverMonitoringMode ;
238+ if ( serverMonitoringMode === ServerMonitoringMode . poll ) return false ;
239+ if ( serverMonitoringMode === ServerMonitoringMode . stream ) return true ;
240+
241+ // If we are in auto mode, we need to figure out if we're in a FaaS
242+ // environment or not and choose the appropriate mode.
243+ if ( monitor . isRunningInFaasEnv ) return false ;
244+ return true ;
245+ }
246+
210247function checkServer ( monitor : Monitor , callback : Callback < Document | null > ) {
211248 let start = now ( ) ;
212249 const topologyVersion = monitor [ kServer ] . description . topologyVersion ;
213- const isAwaitable = topologyVersion != null ;
250+ const isAwaitable = useStreamingProtocol ( monitor , topologyVersion ) ;
214251 monitor . emit (
215252 Server . SERVER_HEARTBEAT_STARTED ,
216253 new ServerHeartbeatStartedEvent ( monitor . address , isAwaitable )
217254 ) ;
218255
219- function failureHandler ( err : Error ) {
256+ function failureHandler ( err : Error , awaited : boolean ) {
220257 monitor [ kConnection ] ?. destroy ( { force : true } ) ;
221258 monitor [ kConnection ] = undefined ;
222259
223260 monitor . emit (
224261 Server . SERVER_HEARTBEAT_FAILED ,
225- new ServerHeartbeatFailedEvent (
226- monitor . address ,
227- calculateDurationInMs ( start ) ,
228- err ,
229- isAwaitable
230- )
262+ new ServerHeartbeatFailedEvent ( monitor . address , calculateDurationInMs ( start ) , err , awaited )
231263 ) ;
232264
233265 const error = ! ( err instanceof MongoError )
@@ -274,7 +306,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
274306
275307 connection . command ( ns ( 'admin.$cmd' ) , cmd , options , ( err , hello ) => {
276308 if ( err ) {
277- return failureHandler ( err ) ;
309+ return failureHandler ( err , isAwaitable ) ;
278310 }
279311
280312 if ( ! ( 'isWritablePrimary' in hello ) ) {
@@ -286,15 +318,14 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
286318 const duration =
287319 isAwaitable && rttPinger ? rttPinger . roundTripTime : calculateDurationInMs ( start ) ;
288320
289- const awaited = isAwaitable && hello . topologyVersion != null ;
290321 monitor . emit (
291322 Server . SERVER_HEARTBEAT_SUCCEEDED ,
292- new ServerHeartbeatSucceededEvent ( monitor . address , duration , hello , awaited )
323+ new ServerHeartbeatSucceededEvent ( monitor . address , duration , hello , isAwaitable )
293324 ) ;
294325
295- // if we are using the streaming protocol then we immediately issue another ` started`
296- // event, otherwise the "check" is complete and return to the main monitor loop
297- if ( awaited ) {
326+ // If we are using the streaming protocol then we immediately issue another ' started'
327+ // event, otherwise the "check" is complete and return to the main monitor loop.
328+ if ( isAwaitable ) {
298329 monitor . emit (
299330 Server . SERVER_HEARTBEAT_STARTED ,
300331 new ServerHeartbeatStartedEvent ( monitor . address , true )
@@ -316,7 +347,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
316347 if ( err ) {
317348 monitor [ kConnection ] = undefined ;
318349
319- failureHandler ( err ) ;
350+ failureHandler ( err , false ) ;
320351 return ;
321352 }
322353
@@ -337,7 +368,7 @@ function checkServer(monitor: Monitor, callback: Callback<Document | null>) {
337368 monitor . address ,
338369 calculateDurationInMs ( start ) ,
339370 conn . hello ,
340- false
371+ useStreamingProtocol ( monitor , conn . hello ?. topologyVersion )
341372 )
342373 ) ;
343374
@@ -370,7 +401,7 @@ function monitorServer(monitor: Monitor) {
370401 }
371402
372403 // if the check indicates streaming is supported, immediately reschedule monitoring
373- if ( hello && hello . topologyVersion ) {
404+ if ( useStreamingProtocol ( monitor , hello ? .topologyVersion ) ) {
374405 setTimeout ( ( ) => {
375406 if ( ! isInCloseState ( monitor ) ) {
376407 monitor [ kMonitorId ] ?. wake ( ) ;
0 commit comments