@@ -41,7 +41,12 @@ import {
41
41
ConnectionPoolReadyEvent ,
42
42
ConnectionReadyEvent
43
43
} from './connection_pool_events' ;
44
- import { PoolClearedError , PoolClosedError , WaitQueueTimeoutError } from './errors' ;
44
+ import {
45
+ PoolClearedError ,
46
+ PoolClearedOnNetworkError ,
47
+ PoolClosedError ,
48
+ WaitQueueTimeoutError
49
+ } from './errors' ;
45
50
import { ConnectionPoolMetrics } from './metrics' ;
46
51
47
52
/** @internal */
@@ -382,6 +387,9 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
382
387
* @param connection - The connection to check in
383
388
*/
384
389
checkIn ( connection : Connection ) : void {
390
+ if ( ! this [ kCheckedOut ] . has ( connection ) ) {
391
+ return ;
392
+ }
385
393
const poolClosed = this . closed ;
386
394
const stale = this . connectionIsStale ( connection ) ;
387
395
const willDestroy = ! ! ( poolClosed || stale || connection . closed ) ;
@@ -408,13 +416,19 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
408
416
* Pool reset is handled by incrementing the pool's generation count. Any existing connection of a
409
417
* previous generation will eventually be pruned during subsequent checkouts.
410
418
*/
411
- clear ( serviceId ?: ObjectId ) : void {
419
+ clear ( options : { serviceId ?: ObjectId ; interruptInUseConnections ?: boolean } = { } ) : void {
412
420
if ( this . closed ) {
413
421
return ;
414
422
}
415
423
416
424
// handle load balanced case
417
- if ( this . loadBalanced && serviceId ) {
425
+ if ( this . loadBalanced ) {
426
+ const { serviceId } = options ;
427
+ if ( ! serviceId ) {
428
+ throw new MongoRuntimeError (
429
+ 'ConnectionPool.clear() called in load balanced mode with no serviceId.'
430
+ ) ;
431
+ }
418
432
const sid = serviceId . toHexString ( ) ;
419
433
const generation = this . serviceGenerations . get ( sid ) ;
420
434
// Only need to worry if the generation exists, since it should
@@ -431,19 +445,42 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
431
445
) ;
432
446
return ;
433
447
}
434
-
435
448
// handle non load-balanced case
449
+ const interruptInUseConnections = options . interruptInUseConnections ?? false ;
450
+ const oldGeneration = this [ kGeneration ] ;
436
451
this [ kGeneration ] += 1 ;
437
452
const alreadyPaused = this [ kPoolState ] === PoolState . paused ;
438
453
this [ kPoolState ] = PoolState . paused ;
439
454
440
455
this . clearMinPoolSizeTimer ( ) ;
441
456
if ( ! alreadyPaused ) {
442
- this . emit ( ConnectionPool . CONNECTION_POOL_CLEARED , new ConnectionPoolClearedEvent ( this ) ) ;
457
+ this . emit (
458
+ ConnectionPool . CONNECTION_POOL_CLEARED ,
459
+ new ConnectionPoolClearedEvent ( this , { interruptInUseConnections } )
460
+ ) ;
461
+ }
462
+
463
+ if ( interruptInUseConnections ) {
464
+ process . nextTick ( ( ) => this . interruptInUseConnections ( oldGeneration ) ) ;
443
465
}
466
+
444
467
this . processWaitQueue ( ) ;
445
468
}
446
469
470
+ /**
471
+ * Closes all stale in-use connections in the pool with a resumable PoolClearedOnNetworkError.
472
+ *
473
+ * Only connections where `connection.generation <= minGeneration` are killed.
474
+ */
475
+ private interruptInUseConnections ( minGeneration : number ) {
476
+ for ( const connection of this [ kCheckedOut ] ) {
477
+ if ( connection . generation <= minGeneration ) {
478
+ this . checkIn ( connection ) ;
479
+ connection . onError ( new PoolClearedOnNetworkError ( this ) ) ;
480
+ }
481
+ }
482
+ }
483
+
447
484
/** Close the pool */
448
485
close ( callback : Callback < void > ) : void ;
449
486
close ( options : CloseOptions , callback : Callback < void > ) : void ;
@@ -572,7 +609,12 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
572
609
return ! ! ( this . options . maxIdleTimeMS && connection . idleTime > this . options . maxIdleTimeMS ) ;
573
610
}
574
611
575
- private connectionIsPerished ( connection : Connection ) {
612
+ /**
613
+ * Destroys a connection if the connection is perished.
614
+ *
615
+ * @returns `true` if the connection was destroyed, `false` otherwise.
616
+ */
617
+ private destroyConnectionIfPerished ( connection : Connection ) : boolean {
576
618
const isStale = this . connectionIsStale ( connection ) ;
577
619
const isIdle = this . connectionIsIdle ( connection ) ;
578
620
if ( ! isStale && ! isIdle && ! connection . closed ) {
@@ -658,7 +700,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
658
700
return ;
659
701
}
660
702
661
- this [ kConnections ] . prune ( connection => this . connectionIsPerished ( connection ) ) ;
703
+ this [ kConnections ] . prune ( connection => this . destroyConnectionIfPerished ( connection ) ) ;
662
704
663
705
if (
664
706
this . totalConnectionCount < minPoolSize &&
@@ -734,7 +776,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
734
776
break ;
735
777
}
736
778
737
- if ( ! this . connectionIsPerished ( connection ) ) {
779
+ if ( ! this . destroyConnectionIfPerished ( connection ) ) {
738
780
this [ kCheckedOut ] . add ( connection ) ;
739
781
this . emit (
740
782
ConnectionPool . CONNECTION_CHECKED_OUT ,
0 commit comments