@@ -41,7 +41,12 @@ import {
4141 ConnectionPoolReadyEvent ,
4242 ConnectionReadyEvent
4343} from './connection_pool_events' ;
44- import { PoolClearedError , PoolClosedError , WaitQueueTimeoutError } from './errors' ;
44+ import {
45+ PoolClearedError ,
46+ PoolClearedOnNetworkError ,
47+ PoolClosedError ,
48+ WaitQueueTimeoutError
49+ } from './errors' ;
4550import { ConnectionPoolMetrics } from './metrics' ;
4651
4752/** @internal */
@@ -382,6 +387,9 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
382387 * @param connection - The connection to check in
383388 */
384389 checkIn ( connection : Connection ) : void {
390+ if ( ! this [ kCheckedOut ] . has ( connection ) ) {
391+ return ;
392+ }
385393 const poolClosed = this . closed ;
386394 const stale = this . connectionIsStale ( connection ) ;
387395 const willDestroy = ! ! ( poolClosed || stale || connection . closed ) ;
@@ -408,13 +416,19 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
408416 * Pool reset is handled by incrementing the pool's generation count. Any existing connection of a
409417 * previous generation will eventually be pruned during subsequent checkouts.
410418 */
411- clear ( serviceId ?: ObjectId ) : void {
419+ clear ( options : { serviceId ?: ObjectId ; interruptInUseConnections ?: boolean } = { } ) : void {
412420 if ( this . closed ) {
413421 return ;
414422 }
415423
416424 // 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+ }
418432 const sid = serviceId . toHexString ( ) ;
419433 const generation = this . serviceGenerations . get ( sid ) ;
420434 // Only need to worry if the generation exists, since it should
@@ -431,19 +445,42 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
431445 ) ;
432446 return ;
433447 }
434-
435448 // handle non load-balanced case
449+ const interruptInUseConnections = options . interruptInUseConnections ?? false ;
450+ const oldGeneration = this [ kGeneration ] ;
436451 this [ kGeneration ] += 1 ;
437452 const alreadyPaused = this [ kPoolState ] === PoolState . paused ;
438453 this [ kPoolState ] = PoolState . paused ;
439454
440455 this . clearMinPoolSizeTimer ( ) ;
441456 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 ) ) ;
443465 }
466+
444467 this . processWaitQueue ( ) ;
445468 }
446469
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+
447484 /** Close the pool */
448485 close ( callback : Callback < void > ) : void ;
449486 close ( options : CloseOptions , callback : Callback < void > ) : void ;
@@ -572,7 +609,12 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
572609 return ! ! ( this . options . maxIdleTimeMS && connection . idleTime > this . options . maxIdleTimeMS ) ;
573610 }
574611
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 {
576618 const isStale = this . connectionIsStale ( connection ) ;
577619 const isIdle = this . connectionIsIdle ( connection ) ;
578620 if ( ! isStale && ! isIdle && ! connection . closed ) {
@@ -658,7 +700,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
658700 return ;
659701 }
660702
661- this [ kConnections ] . prune ( connection => this . connectionIsPerished ( connection ) ) ;
703+ this [ kConnections ] . prune ( connection => this . destroyConnectionIfPerished ( connection ) ) ;
662704
663705 if (
664706 this . totalConnectionCount < minPoolSize &&
@@ -734,7 +776,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
734776 break ;
735777 }
736778
737- if ( ! this . connectionIsPerished ( connection ) ) {
779+ if ( ! this . destroyConnectionIfPerished ( connection ) ) {
738780 this [ kCheckedOut ] . add ( connection ) ;
739781 this . emit (
740782 ConnectionPool . CONNECTION_CHECKED_OUT ,
0 commit comments