@@ -125,10 +125,13 @@ class ChannelSubchannelWrapper
125
125
) => {
126
126
channel . throttleKeepalive ( keepaliveTime ) ;
127
127
} ;
128
- childSubchannel . addConnectivityStateListener ( this . subchannelStateListener ) ;
129
128
}
130
129
131
130
ref ( ) : void {
131
+ if ( this . refCount === 0 ) {
132
+ this . child . addConnectivityStateListener ( this . subchannelStateListener ) ;
133
+ this . channel . addWrappedSubchannel ( this ) ;
134
+ }
132
135
this . child . ref ( ) ;
133
136
this . refCount += 1 ;
134
137
}
@@ -160,6 +163,25 @@ class ShutdownPicker implements Picker {
160
163
}
161
164
162
165
export const SUBCHANNEL_ARGS_EXCLUDE_KEY_PREFIX = 'grpc.internal.no_subchannel' ;
166
+ class ChannelzInfoTracker {
167
+ readonly trace = new ChannelzTrace ( ) ;
168
+ readonly callTracker = new ChannelzCallTracker ( ) ;
169
+ readonly childrenTracker = new ChannelzChildrenTracker ( ) ;
170
+ state : ConnectivityState = ConnectivityState . IDLE ;
171
+ constructor ( private target : string ) { }
172
+
173
+ getChannelzInfoCallback ( ) : ( ) => ChannelInfo {
174
+ return ( ) => {
175
+ return {
176
+ target : this . target ,
177
+ state : this . state ,
178
+ trace : this . trace ,
179
+ callTracker : this . callTracker ,
180
+ children : this . childrenTracker . getChildLists ( )
181
+ } ;
182
+ } ;
183
+ }
184
+ }
163
185
164
186
export class InternalChannel {
165
187
private readonly resolvingLoadBalancer : ResolvingLoadBalancer ;
@@ -181,9 +203,10 @@ export class InternalChannel {
181
203
* event loop open while there are any pending calls for the channel that
182
204
* have not yet been assigned to specific subchannels. In other words,
183
205
* the invariant is that callRefTimer is reffed if and only if pickQueue
184
- * is non-empty.
206
+ * is non-empty. In addition, the timer is null while the state is IDLE or
207
+ * SHUTDOWN and there are no pending calls.
185
208
*/
186
- private readonly callRefTimer : NodeJS . Timeout ;
209
+ private callRefTimer : NodeJS . Timeout | null = null ;
187
210
private configSelector : ConfigSelector | null = null ;
188
211
/**
189
212
* This is the error from the name resolver if it failed most recently. It
@@ -205,11 +228,8 @@ export class InternalChannel {
205
228
206
229
// Channelz info
207
230
private readonly channelzEnabled : boolean = true ;
208
- private readonly originalTarget : string ;
209
231
private readonly channelzRef : ChannelRef ;
210
- private readonly channelzTrace : ChannelzTrace ;
211
- private readonly callTracker = new ChannelzCallTracker ( ) ;
212
- private readonly childrenTracker = new ChannelzChildrenTracker ( ) ;
232
+ private readonly channelzInfoTracker : ChannelzInfoTracker ;
213
233
214
234
/**
215
235
* Randomly generated ID to be passed to the config selector, for use by
@@ -238,7 +258,7 @@ export class InternalChannel {
238
258
throw new TypeError ( 'Channel options must be an object' ) ;
239
259
}
240
260
}
241
- this . originalTarget = target ;
261
+ this . channelzInfoTracker = new ChannelzInfoTracker ( target ) ;
242
262
const originalTargetUri = parseUri ( target ) ;
243
263
if ( originalTargetUri === null ) {
244
264
throw new Error ( `Could not parse target name "${ target } "` ) ;
@@ -252,21 +272,17 @@ export class InternalChannel {
252
272
) ;
253
273
}
254
274
255
- this . callRefTimer = setInterval ( ( ) => { } , MAX_TIMEOUT_TIME ) ;
256
- this . callRefTimer . unref ?.( ) ;
257
-
258
275
if ( this . options [ 'grpc.enable_channelz' ] === 0 ) {
259
276
this . channelzEnabled = false ;
260
277
}
261
278
262
- this . channelzTrace = new ChannelzTrace ( ) ;
263
279
this . channelzRef = registerChannelzChannel (
264
280
target ,
265
- ( ) => this . getChannelzInfo ( ) ,
281
+ this . channelzInfoTracker . getChannelzInfoCallback ( ) ,
266
282
this . channelzEnabled
267
283
) ;
268
284
if ( this . channelzEnabled ) {
269
- this . channelzTrace . addTrace ( 'CT_INFO' , 'Channel created' ) ;
285
+ this . channelzInfoTracker . trace . addTrace ( 'CT_INFO' , 'Channel created' ) ;
270
286
}
271
287
272
288
if ( this . options [ 'grpc.default_authority' ] ) {
@@ -312,7 +328,7 @@ export class InternalChannel {
312
328
) ;
313
329
subchannel . throttleKeepalive ( this . keepaliveTime ) ;
314
330
if ( this . channelzEnabled ) {
315
- this . channelzTrace . addTrace (
331
+ this . channelzInfoTracker . trace . addTrace (
316
332
'CT_INFO' ,
317
333
'Created subchannel or used existing subchannel' ,
318
334
subchannel . getChannelzRef ( )
@@ -322,7 +338,6 @@ export class InternalChannel {
322
338
subchannel ,
323
339
this
324
340
) ;
325
- this . wrappedSubchannels . add ( wrappedSubchannel ) ;
326
341
return wrappedSubchannel ;
327
342
} ,
328
343
updateState : ( connectivityState : ConnectivityState , picker : Picker ) => {
@@ -345,12 +360,12 @@ export class InternalChannel {
345
360
} ,
346
361
addChannelzChild : ( child : ChannelRef | SubchannelRef ) => {
347
362
if ( this . channelzEnabled ) {
348
- this . childrenTracker . refChild ( child ) ;
363
+ this . channelzInfoTracker . childrenTracker . refChild ( child ) ;
349
364
}
350
365
} ,
351
366
removeChannelzChild : ( child : ChannelRef | SubchannelRef ) => {
352
367
if ( this . channelzEnabled ) {
353
- this . childrenTracker . unrefChild ( child ) ;
368
+ this . channelzInfoTracker . childrenTracker . unrefChild ( child ) ;
354
369
}
355
370
} ,
356
371
} ;
@@ -372,7 +387,7 @@ export class InternalChannel {
372
387
RETRY_THROTTLER_MAP . delete ( this . getTarget ( ) ) ;
373
388
}
374
389
if ( this . channelzEnabled ) {
375
- this . channelzTrace . addTrace (
390
+ this . channelzInfoTracker . trace . addTrace (
376
391
'CT_INFO' ,
377
392
'Address resolution succeeded'
378
393
) ;
@@ -395,7 +410,7 @@ export class InternalChannel {
395
410
} ,
396
411
status => {
397
412
if ( this . channelzEnabled ) {
398
- this . channelzTrace . addTrace (
413
+ this . channelzInfoTracker . trace . addTrace (
399
414
'CT_WARNING' ,
400
415
'Address resolution failed with code ' +
401
416
status . code +
@@ -447,16 +462,6 @@ export class InternalChannel {
447
462
this . lastActivityTimestamp = new Date ( ) ;
448
463
}
449
464
450
- private getChannelzInfo ( ) : ChannelInfo {
451
- return {
452
- target : this . originalTarget ,
453
- state : this . connectivityState ,
454
- trace : this . channelzTrace ,
455
- callTracker : this . callTracker ,
456
- children : this . childrenTracker . getChildLists ( ) ,
457
- } ;
458
- }
459
-
460
465
private trace ( text : string , verbosityOverride ?: LogVerbosity ) {
461
466
trace (
462
467
verbosityOverride ?? LogVerbosity . DEBUG ,
@@ -466,6 +471,9 @@ export class InternalChannel {
466
471
}
467
472
468
473
private callRefTimerRef ( ) {
474
+ if ( ! this . callRefTimer ) {
475
+ this . callRefTimer = setInterval ( ( ) => { } , MAX_TIMEOUT_TIME )
476
+ }
469
477
// If the hasRef function does not exist, always run the code
470
478
if ( ! this . callRefTimer . hasRef ?.( ) ) {
471
479
this . trace (
@@ -479,15 +487,15 @@ export class InternalChannel {
479
487
}
480
488
481
489
private callRefTimerUnref ( ) {
482
- // If the hasRef function does not exist, always run the code
483
- if ( ! this . callRefTimer . hasRef || this . callRefTimer . hasRef ( ) ) {
490
+ // If the timer or the hasRef function does not exist, always run the code
491
+ if ( ! this . callRefTimer ? .hasRef || this . callRefTimer . hasRef ( ) ) {
484
492
this . trace (
485
493
'callRefTimer.unref | configSelectionQueue.length=' +
486
494
this . configSelectionQueue . length +
487
495
' pickQueue.length=' +
488
496
this . pickQueue . length
489
497
) ;
490
- this . callRefTimer . unref ?.( ) ;
498
+ this . callRefTimer ? .unref ?.( ) ;
491
499
}
492
500
}
493
501
@@ -516,12 +524,13 @@ export class InternalChannel {
516
524
ConnectivityState [ newState ]
517
525
) ;
518
526
if ( this . channelzEnabled ) {
519
- this . channelzTrace . addTrace (
527
+ this . channelzInfoTracker . trace . addTrace (
520
528
'CT_INFO' ,
521
529
'Connectivity state change to ' + ConnectivityState [ newState ]
522
530
) ;
523
531
}
524
532
this . connectivityState = newState ;
533
+ this . channelzInfoTracker . state = newState ;
525
534
const watchersCopy = this . connectivityStateWatchers . slice ( ) ;
526
535
for ( const watcherObject of watchersCopy ) {
527
536
if ( newState !== watcherObject . currentState ) {
@@ -546,6 +555,10 @@ export class InternalChannel {
546
555
}
547
556
}
548
557
558
+ addWrappedSubchannel ( wrappedSubchannel : ChannelSubchannelWrapper ) {
559
+ this . wrappedSubchannels . add ( wrappedSubchannel ) ;
560
+ }
561
+
549
562
removeWrappedSubchannel ( wrappedSubchannel : ChannelSubchannelWrapper ) {
550
563
this . wrappedSubchannels . delete ( wrappedSubchannel ) ;
551
564
}
@@ -598,6 +611,10 @@ export class InternalChannel {
598
611
clearTimeout ( this . idleTimer ) ;
599
612
this . idleTimer = null ;
600
613
}
614
+ if ( this . callRefTimer ) {
615
+ clearInterval ( this . callRefTimer ) ;
616
+ this . callRefTimer = null ;
617
+ }
601
618
}
602
619
603
620
private startIdleTimeout ( timeoutMs : number ) {
@@ -641,17 +658,17 @@ export class InternalChannel {
641
658
642
659
private onCallStart ( ) {
643
660
if ( this . channelzEnabled ) {
644
- this . callTracker . addCallStarted ( ) ;
661
+ this . channelzInfoTracker . callTracker . addCallStarted ( ) ;
645
662
}
646
663
this . callCount += 1 ;
647
664
}
648
665
649
666
private onCallEnd ( status : StatusObject ) {
650
667
if ( this . channelzEnabled ) {
651
668
if ( status . code === Status . OK ) {
652
- this . callTracker . addCallSucceeded ( ) ;
669
+ this . channelzInfoTracker . callTracker . addCallSucceeded ( ) ;
653
670
} else {
654
- this . callTracker . addCallFailed ( ) ;
671
+ this . channelzInfoTracker . callTracker . addCallFailed ( ) ;
655
672
}
656
673
}
657
674
this . callCount -= 1 ;
@@ -755,7 +772,9 @@ export class InternalChannel {
755
772
call . cancelWithStatus ( Status . UNAVAILABLE , 'Channel closed before call started' ) ;
756
773
}
757
774
this . pickQueue = [ ] ;
758
- clearInterval ( this . callRefTimer ) ;
775
+ if ( this . callRefTimer ) {
776
+ clearInterval ( this . callRefTimer ) ;
777
+ }
759
778
if ( this . idleTimer ) {
760
779
clearTimeout ( this . idleTimer ) ;
761
780
}
0 commit comments