@@ -407,63 +407,72 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
407
407
* @see docs.mongodb.org/manual/reference/connection-string/
408
408
*/
409
409
async connect ( ) : Promise < this> {
410
- if ( this . topology && this . topology . isConnected ( ) ) {
411
- return this ;
412
- }
413
-
414
410
if ( this . connectionLock ) {
415
411
return this . connectionLock ;
416
412
}
417
413
418
- this . connectionLock = ( async ( ) => {
419
- const options = this [ kOptions ] ;
414
+ try {
415
+ this . connectionLock = this . _connect ( ) ;
416
+ await this . connectionLock ;
417
+ } finally {
418
+ // release
419
+ this . connectionLock = undefined ;
420
+ }
420
421
421
- if ( typeof options . srvHost === 'string' ) {
422
- const hosts = await resolveSRVRecord ( options ) ;
422
+ return this ;
423
+ }
423
424
424
- for ( const [ index , host ] of hosts . entries ( ) ) {
425
- options . hosts [ index ] = host ;
426
- }
427
- }
425
+ /**
426
+ * Create a topology to open the connection, must be locked to avoid topology leaks in concurrency scenario.
427
+ * Locking is enforced by the connect method.
428
+ *
429
+ * When decorators available put implementation back to original connect methods
430
+ * and enforce locking via a dedicated decorator.
431
+ * @see https://github.com/mongodb/node-mongodb-native/pull/3596#discussion_r1134211500
432
+ */
433
+ private async _connect ( ) : Promise < this> {
434
+ if ( this . topology && this . topology . isConnected ( ) ) {
435
+ return this ;
436
+ }
428
437
429
- const topology = new Topology ( options . hosts , options ) ;
430
- // Events can be emitted before initialization is complete so we have to
431
- // save the reference to the topology on the client ASAP if the event handlers need to access it
432
- this . topology = topology ;
433
- topology . client = this ;
438
+ const options = this [ kOptions ] ;
434
439
435
- topology . once ( Topology . OPEN , ( ) => this . emit ( 'open' , this ) ) ;
440
+ if ( typeof options . srvHost === 'string' ) {
441
+ const hosts = await resolveSRVRecord ( options ) ;
436
442
437
- for ( const event of MONGO_CLIENT_EVENTS ) {
438
- topology . on ( event , ( ... args : any [ ] ) => this . emit ( event , ... ( args as any ) ) ) ;
443
+ for ( const [ index , host ] of hosts . entries ( ) ) {
444
+ options . hosts [ index ] = host ;
439
445
}
446
+ }
440
447
441
- const topologyConnect = async ( ) => {
442
- try {
443
- await promisify ( callback => topology . connect ( options , callback ) ) ( ) ;
444
- } catch ( error ) {
445
- topology . close ( { force : true } ) ;
446
- throw error ;
447
- }
448
- } ;
449
-
450
- if ( this . autoEncrypter ) {
451
- const initAutoEncrypter = promisify ( callback => this . autoEncrypter ?. init ( callback ) ) ;
452
- await initAutoEncrypter ( ) ;
453
- await topologyConnect ( ) ;
454
- await options . encrypter . connectInternalClient ( ) ;
455
- } else {
456
- await topologyConnect ( ) ;
457
- }
448
+ const topology = new Topology ( options . hosts , options ) ;
449
+ // Events can be emitted before initialization is complete so we have to
450
+ // save the reference to the topology on the client ASAP if the event handlers need to access it
451
+ this . topology = topology ;
452
+ topology . client = this ;
458
453
459
- return this ;
460
- } ) ( ) ;
454
+ topology . once ( Topology . OPEN , ( ) => this . emit ( 'open' , this ) ) ;
461
455
462
- try {
463
- await this . connectionLock ;
464
- } finally {
465
- // release
466
- this . connectionLock = undefined ;
456
+ for ( const event of MONGO_CLIENT_EVENTS ) {
457
+ topology . on ( event , ( ...args : any [ ] ) => this . emit ( event , ...( args as any ) ) ) ;
458
+ }
459
+
460
+ const topologyConnect = async ( ) => {
461
+ try {
462
+ await promisify ( callback => topology . connect ( options , callback ) ) ( ) ;
463
+ } catch ( error ) {
464
+ topology . close ( { force : true } ) ;
465
+ throw error ;
466
+ }
467
+ } ;
468
+
469
+ if ( this . autoEncrypter ) {
470
+ const initAutoEncrypter = promisify ( callback => this . autoEncrypter ?. init ( callback ) ) ;
471
+ await initAutoEncrypter ( ) ;
472
+ await topologyConnect ( ) ;
473
+ await options . encrypter . connectInternalClient ( ) ;
474
+ } else {
475
+ await topologyConnect ( ) ;
467
476
}
468
477
469
478
return this ;
0 commit comments