@@ -18,7 +18,7 @@ import {GoogleError} from 'google-gax';
1818import  *  as  assert  from  'assert' ; 
1919
2020import  { logger }  from  './logger' ; 
21- import  { Deferred ,  requestTag  as  generateClientId }  from  './util' ; 
21+ import  { Deferred ,  requestTag  as  generateTag }  from  './util' ; 
2222
2323export  const  CLIENT_TERMINATED_ERROR_MSG  = 
2424  'The client has already been terminated' ; 
@@ -78,6 +78,11 @@ export class ClientPool<T extends object> {
7878   */ 
7979  private  readonly  terminateDeferred  =  new  Deferred < void > ( ) ; 
8080
81+   /** 
82+    * A unique identifier for this object, for inclusion in log messages. 
83+    */ 
84+   private  readonly  instanceId  =  'cpl'  +  generateTag ( ) ; 
85+ 
8186  /** 
8287   * @param  concurrentOperationLimit The number of operations that each client 
8388   * can handle. 
@@ -137,16 +142,16 @@ export class ClientPool<T extends object> {
137142    if  ( selectedClient )  { 
138143      const  selectedClientId  =  this . clientIdByClient . get ( selectedClient ) ; 
139144      logger ( 
140-         ' ClientPool. acquire' , 
145+         ` ClientPool[ ${ this . instanceId } ]. acquire` , 
141146        requestTag , 
142147        'Re-using existing client [%s] with %s remaining operations' , 
143148        selectedClientId , 
144149        this . concurrentOperationLimit  -  selectedClientRequestCount , 
145150      ) ; 
146151    }  else  { 
147-       const  newClientId  =  'cli'  +  generateClientId ( ) ; 
152+       const  newClientId  =  'cli'  +  generateTag ( ) ; 
148153      logger ( 
149-         ' ClientPool. acquire' , 
154+         ` ClientPool[ ${ this . instanceId } ]. acquire` , 
150155        requestTag , 
151156        'Creating a new client [%s] (requiresGrpc: %s)' , 
152157        newClientId , 
@@ -176,8 +181,10 @@ export class ClientPool<T extends object> {
176181   * @internal  
177182   */ 
178183  private  async  release ( requestTag : string ,  client : T ) : Promise < void >  { 
184+     const  clientId  =  this . clientIdByClient . get ( client ) ; 
179185    const  metadata  =  this . activeClients . get ( client ) ; 
180186    assert ( metadata  &&  metadata . activeRequestCount  >  0 ,  'No active requests' ) ; 
187+ 
181188    this . activeClients . set ( client ,  { 
182189      grpcEnabled : metadata . grpcEnabled , 
183190      activeRequestCount : metadata . activeRequestCount  -  1 , 
@@ -186,26 +193,39 @@ export class ClientPool<T extends object> {
186193      this . terminateDeferred . resolve ( ) ; 
187194    } 
188195
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-       ) ; 
196+     const  gcDetermination  =  this . shouldGarbageCollectClient ( client ) ; 
197+     logger ( 
198+       `ClientPool[${ this . instanceId }  ].release` , 
199+       requestTag , 
200+       'Releasing client [%s] (gc=%s)' , 
201+       clientId , 
202+       gcDetermination 
203+     ) ; 
204+ 
205+     if  ( ! gcDetermination . shouldGarbageCollectClient )  { 
206+       return ; 
208207    } 
208+ 
209+     logger ( 
210+       `ClientPool[${ this . instanceId }  ].release` , 
211+       requestTag , 
212+       'Garbage collecting client [%s] (%s)' , 
213+       clientId , 
214+       this . lazyLogStringForAllClientIds 
215+     ) ; 
216+ 
217+     const  activeClientDeleted  =  this . activeClients . delete ( client ) ; 
218+     this . failedClients . delete ( client ) ; 
219+     await  this . clientDestructor ( client ) ; 
220+ 
221+     logger ( 
222+       `ClientPool[${ this . instanceId }  ].release` , 
223+       requestTag , 
224+       'Garbage collected client [%s] activeClientDeleted=%s (%s)' , 
225+       clientId , 
226+       activeClientDeleted , 
227+       this . lazyLogStringForAllClientIds 
228+     ) ; 
209229  } 
210230
211231  /** 
@@ -214,23 +234,36 @@ export class ClientPool<T extends object> {
214234   * @private  
215235   * @internal  
216236   */ 
217-   private  shouldGarbageCollectClient ( client : T ) : boolean  { 
237+   private  shouldGarbageCollectClient ( 
238+     client : T 
239+   ) : ShouldGarbageCollectClientResult  { 
218240    const  clientMetadata  =  this . activeClients . get ( client ) ! ; 
219241
220242    if  ( clientMetadata . activeRequestCount  !==  0 )  { 
221243      // Don't garbage collect clients that have active requests. 
222-       return  false ; 
244+       return  new  ClientHasActiveRequests ( { 
245+         shouldGarbageCollectClient : false , 
246+         clientActiveRequestCount : clientMetadata . activeRequestCount , 
247+       } ) ; 
223248    } 
224249
225250    if  ( this . grpcEnabled  !==  clientMetadata . grpcEnabled )  { 
226251      // We are transitioning to GRPC. Garbage collect REST clients. 
227-       return  true ; 
252+       return  new  PoolIsTransitioningToGrpc ( { 
253+         shouldGarbageCollectClient : true , 
254+         clientActiveRequestCount : clientMetadata . activeRequestCount , 
255+         poolGrpcEnabled : this . grpcEnabled , 
256+         clientGrpcEnabled : clientMetadata . grpcEnabled , 
257+       } ) ; 
228258    } 
229259
230260    // Idle clients that have received RST_STREAM errors are always garbage 
231261    // collected. 
232262    if  ( this . failedClients . has ( client ) )  { 
233-       return  true ; 
263+       return  new  ClientIsFailed ( { 
264+         shouldGarbageCollectClient : true , 
265+         clientActiveRequestCount : clientMetadata . activeRequestCount , 
266+       } ) ; 
234267    } 
235268
236269    // Otherwise, only garbage collect if we have too much idle capacity (e.g. 
@@ -240,9 +273,17 @@ export class ClientPool<T extends object> {
240273      idleCapacityCount  += 
241274        this . concurrentOperationLimit  -  metadata . activeRequestCount ; 
242275    } 
243-     return  ( 
244-       idleCapacityCount  >  this . maxIdleClients  *  this . concurrentOperationLimit 
245-     ) ; 
276+ 
277+     const  maxIdleCapacityCount  = 
278+       this . maxIdleClients  *  this . concurrentOperationLimit ; 
279+     return  new  IdleCapacity ( { 
280+       shouldGarbageCollectClient : idleCapacityCount  >  maxIdleCapacityCount , 
281+       clientActiveRequestCount : clientMetadata . activeRequestCount , 
282+       idleCapacityCount : idleCapacityCount , 
283+       maxIdleCapacityCount : maxIdleCapacityCount , 
284+       maxIdleClients : this . maxIdleClients , 
285+       concurrentOperationLimit : this . concurrentOperationLimit , 
286+     } ) ; 
246287  } 
247288
248289  /** 
@@ -333,14 +374,20 @@ export class ClientPool<T extends object> {
333374    // Wait for all pending operations to complete before terminating. 
334375    if  ( this . opCount  >  0 )  { 
335376      logger ( 
336-         ' ClientPool. terminate' , 
377+         ` ClientPool[ ${ this . instanceId } ]. terminate` , 
337378        /* requestTag= */  null , 
338379        'Waiting for %s pending operations to complete before terminating (%s)' , 
339380        this . opCount , 
340381        this . lazyLogStringForAllClientIds , 
341382      ) ; 
342383      await  this . terminateDeferred . promise ; 
343384    } 
385+     logger ( 
386+       `ClientPool[${ this . instanceId }  ].terminate` , 
387+       /* requestTag= */  null , 
388+       'Closing all active clients (%s)' , 
389+       this . lazyLogStringForAllClientIds 
390+     ) ; 
344391    for  ( const  [ client ]  of  this . activeClients )  { 
345392      this . activeClients . delete ( client ) ; 
346393      await  this . clientDestructor ( client ) ; 
@@ -354,12 +401,12 @@ export class ClientPool<T extends object> {
354401 * failed clients. 
355402 */ 
356403class  LazyLogStringForAllClientIds < T  extends  object >  { 
357-   private  readonly  activeClients : Map < T ,  unknown > ; 
404+   private  readonly  activeClients : Map < T ,  { activeRequestCount :  number } > ; 
358405  private  readonly  failedClients : Set < T > ; 
359406  private  readonly  clientIdByClient : WeakMap < T ,  string > ; 
360407
361408  constructor ( config : { 
362-     activeClients : Map < T ,  unknown > ; 
409+     activeClients : Map < T ,  { activeRequestCount :  number } > ; 
363410    failedClients : Set < T > ; 
364411    clientIdByClient : WeakMap < T ,  string > ; 
365412  } )  { 
@@ -369,20 +416,135 @@ class LazyLogStringForAllClientIds<T extends object> {
369416  } 
370417
371418  toString ( ) : string  { 
419+     const  activeClientsDescription  =  Array . from ( this . activeClients . entries ( ) ) 
420+       . map ( 
421+         ( [ client ,  metadata ] )  => 
422+           `${ this . clientIdByClient . get ( client ) }  =${ metadata . activeRequestCount }  ` 
423+       ) 
424+       . sort ( ) 
425+       . join ( ', ' ) ; 
426+     const  failedClientsDescription  =  Array . from ( this . failedClients ) 
427+       . map ( client  =>  `${ this . clientIdByClient . get ( client ) }  ` ) 
428+       . sort ( ) 
429+       . join ( ', ' ) ; 
430+ 
372431    return  ( 
373432      `${ this . activeClients . size }   active clients: {`  + 
374-       this . logStringFromClientIds ( this . activeClients . keys ( ) )  + 
433+       activeClientsDescription  + 
375434      '}, '  + 
376435      `${ this . failedClients . size }   failed clients: {`  + 
377-       this . logStringFromClientIds ( this . failedClients )  + 
436+       failedClientsDescription  + 
378437      '}' 
379438    ) ; 
380439  } 
440+ } 
381441
382-   private  logStringFromClientIds ( clients : Iterable < T > ) : string  { 
383-     return  Array . from ( clients ) 
384-       . map ( client  =>  this . clientIdByClient . get ( client )  ??  '<unknown>' ) 
385-       . sort ( ) 
386-       . join ( ', ' ) ; 
442+ /** 
443+  * Minimum data to be included in the objects returned from 
444+  * ClientPool.shouldGarbageCollectClient(). 
445+  */ 
446+ abstract  class  BaseShouldGarbageCollectClientResult  { 
447+   abstract  readonly  name : string ; 
448+   abstract  readonly  shouldGarbageCollectClient : boolean ; 
449+   abstract  readonly  clientActiveRequestCount : number ; 
450+ 
451+   /** 
452+    * Return a terse, one-line string representation. This makes it easy to 
453+    * grep through log output to find the logged values. 
454+    */ 
455+   toString ( ) : string  { 
456+     const  propertyStrings : string [ ]  =  [ ] ; 
457+     for  ( const  propertyName  of  Object . getOwnPropertyNames ( this ) )  { 
458+       const  propertyValue  =  this [ propertyName  as  keyof  typeof  this ] ; 
459+       propertyStrings . push ( `${ propertyName }  =${ propertyValue }  ` ) ; 
460+     } 
461+     return  '{'  +  propertyStrings . join ( ', ' )  +  '}' ; 
462+   } 
463+ } 
464+ 
465+ class  ClientHasActiveRequests  extends  BaseShouldGarbageCollectClientResult  { 
466+   override  readonly  name  =  'ClientHasActiveRequests'  as  const ; 
467+   override  readonly  shouldGarbageCollectClient : false ; 
468+   override  readonly  clientActiveRequestCount : number ; 
469+ 
470+   constructor ( args : { 
471+     shouldGarbageCollectClient : false ; 
472+     clientActiveRequestCount : number ; 
473+   } )  { 
474+     super ( ) ; 
475+     this . shouldGarbageCollectClient  =  args . shouldGarbageCollectClient ; 
476+     this . clientActiveRequestCount  =  args . clientActiveRequestCount ; 
387477  } 
388478} 
479+ 
480+ class  PoolIsTransitioningToGrpc  extends  BaseShouldGarbageCollectClientResult  { 
481+   override  readonly  name  =  'PoolIsTransitioningToGrpc'  as  const ; 
482+   override  readonly  shouldGarbageCollectClient : true ; 
483+   override  readonly  clientActiveRequestCount : 0 ; 
484+   readonly  poolGrpcEnabled : boolean ; 
485+   readonly  clientGrpcEnabled : boolean ; 
486+ 
487+   constructor ( args : { 
488+     shouldGarbageCollectClient : true ; 
489+     clientActiveRequestCount : 0 ; 
490+     poolGrpcEnabled : boolean ; 
491+     clientGrpcEnabled : boolean ; 
492+   } )  { 
493+     super ( ) ; 
494+     this . shouldGarbageCollectClient  =  args . shouldGarbageCollectClient ; 
495+     this . clientActiveRequestCount  =  args . clientActiveRequestCount ; 
496+     this . poolGrpcEnabled  =  args . poolGrpcEnabled ; 
497+     this . clientGrpcEnabled  =  args . clientGrpcEnabled ; 
498+   } 
499+ } 
500+ 
501+ class  ClientIsFailed  extends  BaseShouldGarbageCollectClientResult  { 
502+   override  readonly  name  =  'ClientIsFailed'  as  const ; 
503+   override  readonly  shouldGarbageCollectClient : true ; 
504+   override  readonly  clientActiveRequestCount : 0 ; 
505+ 
506+   constructor ( args : { 
507+     shouldGarbageCollectClient : true ; 
508+     clientActiveRequestCount : 0 ; 
509+   } )  { 
510+     super ( ) ; 
511+     this . shouldGarbageCollectClient  =  args . shouldGarbageCollectClient ; 
512+     this . clientActiveRequestCount  =  args . clientActiveRequestCount ; 
513+   } 
514+ } 
515+ 
516+ class  IdleCapacity  extends  BaseShouldGarbageCollectClientResult  { 
517+   override  readonly  name  =  'IdleCapacity'  as  const ; 
518+   override  readonly  shouldGarbageCollectClient : boolean ; 
519+   override  readonly  clientActiveRequestCount : 0 ; 
520+   readonly  idleCapacityCount : number ; 
521+   readonly  maxIdleCapacityCount : number ; 
522+   readonly  maxIdleClients : number ; 
523+   readonly  concurrentOperationLimit : number ; 
524+ 
525+   constructor ( args : { 
526+     shouldGarbageCollectClient : boolean ; 
527+     clientActiveRequestCount : 0 ; 
528+     idleCapacityCount : number ; 
529+     maxIdleCapacityCount : number ; 
530+     maxIdleClients : number ; 
531+     concurrentOperationLimit : number ; 
532+   } )  { 
533+     super ( ) ; 
534+     this . shouldGarbageCollectClient  =  args . shouldGarbageCollectClient ; 
535+     this . clientActiveRequestCount  =  args . clientActiveRequestCount ; 
536+     this . idleCapacityCount  =  args . idleCapacityCount ; 
537+     this . maxIdleCapacityCount  =  args . maxIdleCapacityCount ; 
538+     this . maxIdleClients  =  args . maxIdleClients ; 
539+     this . concurrentOperationLimit  =  args . concurrentOperationLimit ; 
540+   } 
541+ } 
542+ 
543+ /** 
544+  * The set of return types from ClientPool.shouldGarbageCollectClient(). 
545+  */ 
546+ type  ShouldGarbageCollectClientResult  = 
547+   |  ClientHasActiveRequests 
548+   |  PoolIsTransitioningToGrpc 
549+   |  ClientIsFailed 
550+   |  IdleCapacity ; 
0 commit comments