@@ -176,8 +176,10 @@ export class ClientPool<T extends object> {
176176 * @internal
177177 */
178178 private async release ( requestTag : string , client : T ) : Promise < void > {
179+ const clientId = this . clientIdByClient . get ( client ) ;
179180 const metadata = this . activeClients . get ( client ) ;
180181 assert ( metadata && metadata . activeRequestCount > 0 , 'No active requests' ) ;
182+
181183 this . activeClients . set ( client , {
182184 grpcEnabled : metadata . grpcEnabled ,
183185 activeRequestCount : metadata . activeRequestCount - 1 ,
@@ -186,26 +188,38 @@ export class ClientPool<T extends object> {
186188 this . terminateDeferred . resolve ( ) ;
187189 }
188190
189- if ( this . shouldGarbageCollectClient ( client ) ) {
190- const clientId = this . clientIdByClient . get ( client ) ;
191- logger (
192- 'ClientPool.release' ,
193- requestTag ,
194- 'Garbage collecting client [%s] (%s)' ,
195- clientId ,
196- this . lazyLogStringForAllClientIds
197- ) ;
198- this . activeClients . delete ( client ) ;
199- this . failedClients . delete ( client ) ;
200- await this . clientDestructor ( client ) ;
201- logger (
202- 'ClientPool.release' ,
203- requestTag ,
204- 'Garbage collected client [%s] (%s)' ,
205- clientId ,
206- this . lazyLogStringForAllClientIds
207- ) ;
191+ const gcDetermination = this . shouldGarbageCollectClient ( client ) ;
192+ logger (
193+ 'ClientPool.release' ,
194+ requestTag ,
195+ 'Releasing client [%s] (gc=%s)' ,
196+ clientId ,
197+ gcDetermination
198+ ) ;
199+
200+ if ( ! gcDetermination . shouldGarbageCollectClient ) {
201+ return ;
208202 }
203+
204+ logger (
205+ 'ClientPool.release' ,
206+ requestTag ,
207+ 'Garbage collecting client [%s] (%s)' ,
208+ clientId ,
209+ this . lazyLogStringForAllClientIds
210+ ) ;
211+
212+ this . activeClients . delete ( client ) ;
213+ this . failedClients . delete ( client ) ;
214+ await this . clientDestructor ( client ) ;
215+
216+ logger (
217+ 'ClientPool.release' ,
218+ requestTag ,
219+ 'Garbage collected client [%s] (%s)' ,
220+ clientId ,
221+ this . lazyLogStringForAllClientIds
222+ ) ;
209223 }
210224
211225 /**
@@ -214,23 +228,36 @@ export class ClientPool<T extends object> {
214228 * @private
215229 * @internal
216230 */
217- private shouldGarbageCollectClient ( client : T ) : boolean {
231+ private shouldGarbageCollectClient (
232+ client : T
233+ ) : ShouldGarbageCollectClientResult {
218234 const clientMetadata = this . activeClients . get ( client ) ! ;
219235
220236 if ( clientMetadata . activeRequestCount !== 0 ) {
221237 // Don't garbage collect clients that have active requests.
222- return false ;
238+ return new ClientHasActiveRequests ( {
239+ shouldGarbageCollectClient : false ,
240+ clientActiveRequestCount : clientMetadata . activeRequestCount ,
241+ } ) ;
223242 }
224243
225244 if ( this . grpcEnabled !== clientMetadata . grpcEnabled ) {
226245 // We are transitioning to GRPC. Garbage collect REST clients.
227- return true ;
246+ return new PoolIsTransitioningToGrpc ( {
247+ shouldGarbageCollectClient : true ,
248+ clientActiveRequestCount : clientMetadata . activeRequestCount ,
249+ poolGrpcEnabled : this . grpcEnabled ,
250+ clientGrpcEnabled : clientMetadata . grpcEnabled ,
251+ } ) ;
228252 }
229253
230254 // Idle clients that have received RST_STREAM errors are always garbage
231255 // collected.
232256 if ( this . failedClients . has ( client ) ) {
233- return true ;
257+ return new ClientIsFailed ( {
258+ shouldGarbageCollectClient : true ,
259+ clientActiveRequestCount : clientMetadata . activeRequestCount ,
260+ } ) ;
234261 }
235262
236263 // Otherwise, only garbage collect if we have too much idle capacity (e.g.
@@ -240,9 +267,17 @@ export class ClientPool<T extends object> {
240267 idleCapacityCount +=
241268 this . concurrentOperationLimit - metadata . activeRequestCount ;
242269 }
243- return (
244- idleCapacityCount > this . maxIdleClients * this . concurrentOperationLimit
245- ) ;
270+
271+ const maxIdleCapacityCount =
272+ this . maxIdleClients * this . concurrentOperationLimit ;
273+ return new IdleCapacity ( {
274+ shouldGarbageCollectClient : idleCapacityCount > maxIdleCapacityCount ,
275+ clientActiveRequestCount : clientMetadata . activeRequestCount ,
276+ idleCapacityCount : idleCapacityCount ,
277+ maxIdleCapacityCount : maxIdleCapacityCount ,
278+ maxIdleClients : this . maxIdleClients ,
279+ concurrentOperationLimit : this . concurrentOperationLimit ,
280+ } ) ;
246281 }
247282
248283 /**
@@ -386,3 +421,113 @@ class LazyLogStringForAllClientIds<T extends object> {
386421 . join ( ', ' ) ;
387422 }
388423}
424+
425+ /**
426+ * Minimum data to be included in the objects returned from
427+ * ClientPool.shouldGarbageCollectClient().
428+ */
429+ abstract class BaseShouldGarbageCollectClientResult {
430+ abstract readonly name : string ;
431+ abstract readonly shouldGarbageCollectClient : boolean ;
432+ abstract readonly clientActiveRequestCount : number ;
433+
434+ /**
435+ * Return a terse, one-line string representation. This makes it easy to
436+ * grep through log output to find the logged values.
437+ */
438+ toString ( ) : string {
439+ const propertyStrings : string [ ] = [ ] ;
440+ for ( const propertyName of Object . getOwnPropertyNames ( this ) ) {
441+ const propertyValue = this [ propertyName as keyof typeof this ] ;
442+ propertyStrings . push ( `${ propertyName } =${ propertyValue } ` ) ;
443+ }
444+ return '{' + propertyStrings . join ( ', ' ) + '}' ;
445+ }
446+ }
447+
448+ class ClientHasActiveRequests extends BaseShouldGarbageCollectClientResult {
449+ override readonly name = 'ClientHasActiveRequests' as const ;
450+ override readonly shouldGarbageCollectClient : false ;
451+ override readonly clientActiveRequestCount : number ;
452+
453+ constructor ( args : {
454+ shouldGarbageCollectClient : false ;
455+ clientActiveRequestCount : number ;
456+ } ) {
457+ super ( ) ;
458+ this . shouldGarbageCollectClient = args . shouldGarbageCollectClient ;
459+ this . clientActiveRequestCount = args . clientActiveRequestCount ;
460+ }
461+ }
462+
463+ class PoolIsTransitioningToGrpc extends BaseShouldGarbageCollectClientResult {
464+ override readonly name = 'PoolIsTransitioningToGrpc' as const ;
465+ override readonly shouldGarbageCollectClient : true ;
466+ override readonly clientActiveRequestCount : 0 ;
467+ readonly poolGrpcEnabled : boolean ;
468+ readonly clientGrpcEnabled : boolean ;
469+
470+ constructor ( args : {
471+ shouldGarbageCollectClient : true ;
472+ clientActiveRequestCount : 0 ;
473+ poolGrpcEnabled : boolean ;
474+ clientGrpcEnabled : boolean ;
475+ } ) {
476+ super ( ) ;
477+ this . shouldGarbageCollectClient = args . shouldGarbageCollectClient ;
478+ this . clientActiveRequestCount = args . clientActiveRequestCount ;
479+ this . poolGrpcEnabled = args . poolGrpcEnabled ;
480+ this . clientGrpcEnabled = args . clientGrpcEnabled ;
481+ }
482+ }
483+
484+ class ClientIsFailed extends BaseShouldGarbageCollectClientResult {
485+ override readonly name = 'ClientIsFailed' as const ;
486+ override readonly shouldGarbageCollectClient : true ;
487+ override readonly clientActiveRequestCount : 0 ;
488+
489+ constructor ( args : {
490+ shouldGarbageCollectClient : true ;
491+ clientActiveRequestCount : 0 ;
492+ } ) {
493+ super ( ) ;
494+ this . shouldGarbageCollectClient = args . shouldGarbageCollectClient ;
495+ this . clientActiveRequestCount = args . clientActiveRequestCount ;
496+ }
497+ }
498+
499+ class IdleCapacity extends BaseShouldGarbageCollectClientResult {
500+ override readonly name = 'IdleCapacity' as const ;
501+ override readonly shouldGarbageCollectClient : boolean ;
502+ override readonly clientActiveRequestCount : 0 ;
503+ readonly idleCapacityCount : number ;
504+ readonly maxIdleCapacityCount : number ;
505+ readonly maxIdleClients : number ;
506+ readonly concurrentOperationLimit : number ;
507+
508+ constructor ( args : {
509+ shouldGarbageCollectClient : boolean ;
510+ clientActiveRequestCount : 0 ;
511+ idleCapacityCount : number ;
512+ maxIdleCapacityCount : number ;
513+ maxIdleClients : number ;
514+ concurrentOperationLimit : number ;
515+ } ) {
516+ super ( ) ;
517+ this . shouldGarbageCollectClient = args . shouldGarbageCollectClient ;
518+ this . clientActiveRequestCount = args . clientActiveRequestCount ;
519+ this . idleCapacityCount = args . idleCapacityCount ;
520+ this . maxIdleCapacityCount = args . maxIdleCapacityCount ;
521+ this . maxIdleClients = args . maxIdleClients ;
522+ this . concurrentOperationLimit = args . concurrentOperationLimit ;
523+ }
524+ }
525+
526+ /**
527+ * The set of return types from ClientPool.shouldGarbageCollectClient().
528+ */
529+ type ShouldGarbageCollectClientResult =
530+ | ClientHasActiveRequests
531+ | PoolIsTransitioningToGrpc
532+ | ClientIsFailed
533+ | IdleCapacity ;
0 commit comments