1
- import { promisify } from 'util' ;
2
-
3
1
import type { BSONSerializeOptions , Document } from '../bson' ;
4
2
import type { MongoCredentials } from '../cmap/auth/mongo_credentials' ;
5
3
import type { ConnectionEvents } from '../cmap/connection' ;
@@ -44,6 +42,7 @@ import {
44
42
makeStateMachine ,
45
43
now ,
46
44
ns ,
45
+ promiseWithResolvers ,
47
46
shuffle ,
48
47
TimeoutController
49
48
} from '../utils' ;
@@ -105,7 +104,8 @@ export interface ServerSelectionRequest {
105
104
mongoLogger : MongoLogger | undefined ;
106
105
transaction ?: Transaction ;
107
106
startTime : number ;
108
- callback : ServerSelectionCallback ;
107
+ resolve : ( server : Server ) => void ;
108
+ reject : ( error : MongoError ) => void ;
109
109
[ kCancelled ] ?: boolean ;
110
110
timeoutController : TimeoutController ;
111
111
operationName : string ;
@@ -215,6 +215,9 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
215
215
216
216
client ! : MongoClient ;
217
217
218
+ /** @internal */
219
+ private connectionLock ?: Promise < Topology > ;
220
+
218
221
/** @event */
219
222
static readonly SERVER_OPENING = SERVER_OPENING ;
220
223
/** @event */
@@ -238,11 +241,6 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
238
241
/** @event */
239
242
static readonly TIMEOUT = TIMEOUT ;
240
243
241
- selectServerAsync : (
242
- selector : string | ReadPreference | ServerSelector ,
243
- options : SelectServerOptions
244
- ) => Promise < Server > ;
245
-
246
244
/**
247
245
* @param seedlist - a list of HostAddress instances to connect to
248
246
*/
@@ -254,14 +252,6 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
254
252
super ( ) ;
255
253
256
254
this . client = client ;
257
- this . selectServerAsync = promisify (
258
- (
259
- selector : string | ReadPreference | ServerSelector ,
260
- options : SelectServerOptions ,
261
- callback : ( e : Error , r : Server ) => void
262
- ) => this . selectServer ( selector , options , callback as any )
263
- ) ;
264
-
265
255
// Options should only be undefined in tests, MongoClient will always have defined options
266
256
options = options ?? {
267
257
hosts : [ HostAddress . fromString ( 'localhost:27017' ) ] ,
@@ -351,6 +341,7 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
351
341
352
342
this . on ( Topology . TOPOLOGY_DESCRIPTION_CHANGED , this . s . detectShardedTopology ) ;
353
343
}
344
+ this . connectionLock = undefined ;
354
345
}
355
346
356
347
private detectShardedTopology ( event : TopologyDescriptionChangedEvent ) {
@@ -411,17 +402,22 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
411
402
}
412
403
413
404
/** Initiate server connect */
414
- connect ( callback : Callback ) : void ;
415
- connect ( options : ConnectOptions , callback : Callback ) : void ;
416
- connect ( options ?: ConnectOptions | Callback , callback ?: Callback ) : void {
417
- if ( typeof options === 'function' ) ( callback = options ) , ( options = { } ) ;
405
+ async connect ( options ?: ConnectOptions ) : Promise < Topology > {
406
+ this . connectionLock ??= this . _connect ( options ) ;
407
+ try {
408
+ await this . connectionLock ;
409
+ return this ;
410
+ } finally {
411
+ this . connectionLock = undefined ;
412
+ }
413
+
414
+ return this ;
415
+ }
416
+
417
+ private async _connect ( options ?: ConnectOptions ) : Promise < Topology > {
418
418
options = options ?? { } ;
419
419
if ( this . s . state === STATE_CONNECTED ) {
420
- if ( typeof callback === 'function' ) {
421
- callback ( ) ;
422
- }
423
-
424
- return ;
420
+ return this ;
425
421
}
426
422
427
423
stateTransition ( this , STATE_CONNECTING ) ;
@@ -459,40 +455,33 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
459
455
}
460
456
}
461
457
462
- const exitWithError = ( error : Error ) =>
463
- callback ? callback ( error ) : this . emit ( Topology . ERROR , error ) ;
464
-
465
458
const readPreference = options . readPreference ?? ReadPreference . primary ;
466
459
const selectServerOptions = { operationName : 'ping' , ...options } ;
467
- this . selectServer (
468
- readPreferenceServerSelector ( readPreference ) ,
469
- selectServerOptions ,
470
- ( err , server ) => {
471
- if ( err ) {
472
- this . close ( ) ;
473
- return exitWithError ( err ) ;
474
- }
475
-
476
- const skipPingOnConnect = this . s . options [ Symbol . for ( '@@mdb.skipPingOnConnect' ) ] === true ;
477
- if ( ! skipPingOnConnect && server && this . s . credentials ) {
478
- server . command ( ns ( 'admin.$cmd' ) , { ping : 1 } , { } ) . then ( ( ) => {
479
- stateTransition ( this , STATE_CONNECTED ) ;
480
- this . emit ( Topology . OPEN , this ) ;
481
- this . emit ( Topology . CONNECT , this ) ;
482
-
483
- callback ?.( undefined , this ) ;
484
- } , exitWithError ) ;
485
-
486
- return ;
487
- }
460
+ try {
461
+ const server = await this . selectServer (
462
+ readPreferenceServerSelector ( readPreference ) ,
463
+ selectServerOptions
464
+ ) ;
488
465
466
+ const skipPingOnConnect = this . s . options [ Symbol . for ( '@@mdb.skipPingOnConnect' ) ] === true ;
467
+ if ( ! skipPingOnConnect && server && this . s . credentials ) {
468
+ await server . command ( ns ( 'admin.$cmd' ) , { ping : 1 } , { } ) ;
489
469
stateTransition ( this , STATE_CONNECTED ) ;
490
470
this . emit ( Topology . OPEN , this ) ;
491
471
this . emit ( Topology . CONNECT , this ) ;
492
472
493
- callback ?. ( undefined , this ) ;
473
+ return this ;
494
474
}
495
- ) ;
475
+
476
+ stateTransition ( this , STATE_CONNECTED ) ;
477
+ this . emit ( Topology . OPEN , this ) ;
478
+ this . emit ( Topology . CONNECT , this ) ;
479
+
480
+ return this ;
481
+ } catch ( error ) {
482
+ this . close ( ) ;
483
+ throw error ;
484
+ }
496
485
}
497
486
498
487
/** Close this topology */
@@ -533,11 +522,10 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
533
522
* @param callback - The callback used to indicate success or failure
534
523
* @returns An instance of a `Server` meeting the criteria of the predicate provided
535
524
*/
536
- selectServer (
525
+ async selectServer (
537
526
selector : string | ReadPreference | ServerSelector ,
538
- options : SelectServerOptions ,
539
- callback : Callback < Server >
540
- ) : void {
527
+ options : SelectServerOptions
528
+ ) : Promise < Server > {
541
529
let serverSelector ;
542
530
if ( typeof selector !== 'function' ) {
543
531
if ( typeof selector === 'string' ) {
@@ -588,16 +576,17 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
588
576
)
589
577
) ;
590
578
}
591
- callback ( undefined , transaction . server ) ;
592
- return ;
579
+ return transaction . server ;
593
580
}
594
581
582
+ const { promise : serverPromise , resolve, reject } = promiseWithResolvers < Server > ( ) ;
595
583
const waitQueueMember : ServerSelectionRequest = {
596
584
serverSelector,
597
585
topologyDescription : this . description ,
598
586
mongoLogger : this . client . mongoLogger ,
599
587
transaction,
600
- callback,
588
+ resolve,
589
+ reject,
601
590
timeoutController : new TimeoutController ( options . serverSelectionTimeoutMS ) ,
602
591
startTime : now ( ) ,
603
592
operationName : options . operationName ,
@@ -628,13 +617,14 @@ export class Topology extends TypedEventEmitter<TopologyEvents> {
628
617
)
629
618
) ;
630
619
}
631
- waitQueueMember . callback ( timeoutError ) ;
620
+ waitQueueMember . reject ( timeoutError ) ;
632
621
} ) ;
633
622
634
623
this [ kWaitQueue ] . push ( waitQueueMember ) ;
635
624
processWaitQueue ( this ) ;
636
- }
637
625
626
+ return serverPromise ;
627
+ }
638
628
/**
639
629
* Update the internal TopologyDescription with a ServerDescription
640
630
*
@@ -883,7 +873,7 @@ function updateServers(topology: Topology, incomingServerDescription?: ServerDes
883
873
}
884
874
}
885
875
886
- function drainWaitQueue ( queue : List < ServerSelectionRequest > , err ? : MongoDriverError ) {
876
+ function drainWaitQueue ( queue : List < ServerSelectionRequest > , drainError : MongoDriverError ) {
887
877
while ( queue . length ) {
888
878
const waitQueueMember = queue . shift ( ) ;
889
879
if ( ! waitQueueMember ) {
@@ -893,25 +883,23 @@ function drainWaitQueue(queue: List<ServerSelectionRequest>, err?: MongoDriverEr
893
883
waitQueueMember . timeoutController . clear ( ) ;
894
884
895
885
if ( ! waitQueueMember [ kCancelled ] ) {
896
- if ( err ) {
897
- if (
898
- waitQueueMember . mongoLogger ?. willLog (
899
- MongoLoggableComponent . SERVER_SELECTION ,
900
- SeverityLevel . DEBUG
886
+ if (
887
+ waitQueueMember . mongoLogger ?. willLog (
888
+ MongoLoggableComponent . SERVER_SELECTION ,
889
+ SeverityLevel . DEBUG
890
+ )
891
+ ) {
892
+ waitQueueMember . mongoLogger ?. debug (
893
+ MongoLoggableComponent . SERVER_SELECTION ,
894
+ new ServerSelectionFailedEvent (
895
+ waitQueueMember . serverSelector ,
896
+ waitQueueMember . topologyDescription ,
897
+ drainError ,
898
+ waitQueueMember . operationName
901
899
)
902
- ) {
903
- waitQueueMember . mongoLogger ?. debug (
904
- MongoLoggableComponent . SERVER_SELECTION ,
905
- new ServerSelectionFailedEvent (
906
- waitQueueMember . serverSelector ,
907
- waitQueueMember . topologyDescription ,
908
- err ,
909
- waitQueueMember . operationName
910
- )
911
- ) ;
912
- }
900
+ ) ;
913
901
}
914
- waitQueueMember . callback ( err ) ;
902
+ waitQueueMember . reject ( drainError ) ;
915
903
}
916
904
}
917
905
}
@@ -946,7 +934,7 @@ function processWaitQueue(topology: Topology) {
946
934
previousServer ? [ previousServer ] : [ ]
947
935
)
948
936
: serverDescriptions ;
949
- } catch ( e ) {
937
+ } catch ( selectorError ) {
950
938
waitQueueMember . timeoutController . clear ( ) ;
951
939
if (
952
940
topology . client . mongoLogger ?. willLog (
@@ -959,12 +947,12 @@ function processWaitQueue(topology: Topology) {
959
947
new ServerSelectionFailedEvent (
960
948
waitQueueMember . serverSelector ,
961
949
topology . description ,
962
- e ,
950
+ selectorError ,
963
951
waitQueueMember . operationName
964
952
)
965
953
) ;
966
954
}
967
- waitQueueMember . callback ( e ) ;
955
+ waitQueueMember . reject ( selectorError ) ;
968
956
continue ;
969
957
}
970
958
@@ -1007,7 +995,7 @@ function processWaitQueue(topology: Topology) {
1007
995
}
1008
996
1009
997
if ( ! selectedServer ) {
1010
- const error = new MongoServerSelectionError (
998
+ const serverSelectionError = new MongoServerSelectionError (
1011
999
'server selection returned a server description but the server was not found in the topology' ,
1012
1000
topology . description
1013
1001
) ;
@@ -1022,12 +1010,12 @@ function processWaitQueue(topology: Topology) {
1022
1010
new ServerSelectionFailedEvent (
1023
1011
waitQueueMember . serverSelector ,
1024
1012
topology . description ,
1025
- error ,
1013
+ serverSelectionError ,
1026
1014
waitQueueMember . operationName
1027
1015
)
1028
1016
) ;
1029
1017
}
1030
- waitQueueMember . callback ( error ) ;
1018
+ waitQueueMember . reject ( serverSelectionError ) ;
1031
1019
return ;
1032
1020
}
1033
1021
const transaction = waitQueueMember . transaction ;
@@ -1053,7 +1041,7 @@ function processWaitQueue(topology: Topology) {
1053
1041
)
1054
1042
) ;
1055
1043
}
1056
- waitQueueMember . callback ( undefined , selectedServer ) ;
1044
+ waitQueueMember . resolve ( selectedServer ) ;
1057
1045
}
1058
1046
1059
1047
if ( topology [ kWaitQueue ] . length > 0 ) {
0 commit comments