@@ -51,6 +51,7 @@ import { ResolvingCall } from './resolving-call';
5151import { getNextCallNumber } from './call-number' ;
5252import { restrictControlPlaneStatusCode } from './control-plane-status' ;
5353import { MessageBufferTracker , RetryingCall , RetryThrottler } from './retrying-call' ;
54+ import { BaseSubchannelWrapper , ConnectivityStateListener , SubchannelInterface } from './subchannel-interface' ;
5455
5556/**
5657 * See https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_args
@@ -84,6 +85,33 @@ const RETRY_THROTTLER_MAP: Map<string, RetryThrottler> = new Map();
8485const DEFAULT_RETRY_BUFFER_SIZE_BYTES = 1 << 24 ; // 16 MB
8586const DEFAULT_PER_RPC_RETRY_BUFFER_SIZE_BYTES = 1 << 20 ; // 1 MB
8687
88+ class ChannelSubchannelWrapper extends BaseSubchannelWrapper implements SubchannelInterface {
89+ private stateListeners : ConnectivityStateListener [ ] = [ ] ;
90+ private refCount = 0 ;
91+ constructor ( childSubchannel : SubchannelInterface , private channel : InternalChannel ) {
92+ super ( childSubchannel ) ;
93+ childSubchannel . addConnectivityStateListener ( ( subchannel , previousState , newState , keepaliveTime ) => {
94+ channel . throttleKeepalive ( keepaliveTime ) ;
95+ for ( const listener of this . stateListeners ) {
96+ listener ( this , previousState , newState , keepaliveTime ) ;
97+ }
98+ } ) ;
99+ }
100+
101+ ref ( ) : void {
102+ this . child . ref ( ) ;
103+ this . refCount += 1 ;
104+ }
105+
106+ unref ( ) : void {
107+ this . child . unref ( ) ;
108+ this . refCount -= 1 ;
109+ if ( this . refCount <= 0 ) {
110+ this . channel . removeWrappedSubchannel ( this ) ;
111+ }
112+ }
113+ }
114+
87115export class InternalChannel {
88116
89117 private resolvingLoadBalancer : ResolvingLoadBalancer ;
@@ -116,8 +144,10 @@ export class InternalChannel {
116144 * configSelector becomes set or the channel state becomes anything other
117145 * than TRANSIENT_FAILURE.
118146 */
119- private currentResolutionError : StatusObject | null = null ;
120- private retryBufferTracker : MessageBufferTracker ;
147+ private currentResolutionError : StatusObject | null = null ;
148+ private retryBufferTracker : MessageBufferTracker ;
149+ private keepaliveTime : number ;
150+ private wrappedSubchannels : Set < ChannelSubchannelWrapper > = new Set ( ) ;
121151
122152 // Channelz info
123153 private readonly channelzEnabled : boolean = true ;
@@ -190,6 +220,7 @@ export class InternalChannel {
190220 options [ 'grpc.retry_buffer_size' ] ?? DEFAULT_RETRY_BUFFER_SIZE_BYTES ,
191221 options [ 'grpc.per_rpc_retry_buffer_size' ] ?? DEFAULT_PER_RPC_RETRY_BUFFER_SIZE_BYTES
192222 ) ;
223+ this . keepaliveTime = options [ 'grpc.keepalive_time_ms' ] ?? - 1 ;
193224 const channelControlHelper : ChannelControlHelper = {
194225 createSubchannel : (
195226 subchannelAddress : SubchannelAddress ,
@@ -201,10 +232,13 @@ export class InternalChannel {
201232 Object . assign ( { } , this . options , subchannelArgs ) ,
202233 this . credentials
203234 ) ;
235+ subchannel . throttleKeepalive ( this . keepaliveTime ) ;
204236 if ( this . channelzEnabled ) {
205237 this . channelzTrace . addTrace ( 'CT_INFO' , 'Created subchannel or used existing subchannel' , subchannel . getChannelzRef ( ) ) ;
206238 }
207- return subchannel ;
239+ const wrappedSubchannel = new ChannelSubchannelWrapper ( subchannel , this ) ;
240+ this . wrappedSubchannels . add ( wrappedSubchannel ) ;
241+ return wrappedSubchannel ;
208242 } ,
209243 updateState : ( connectivityState : ConnectivityState , picker : Picker ) => {
210244 this . currentPicker = picker ;
@@ -369,6 +403,19 @@ export class InternalChannel {
369403 }
370404 }
371405
406+ throttleKeepalive ( newKeepaliveTime : number ) {
407+ if ( newKeepaliveTime > this . keepaliveTime ) {
408+ this . keepaliveTime = newKeepaliveTime ;
409+ for ( const wrappedSubchannel of this . wrappedSubchannels ) {
410+ wrappedSubchannel . throttleKeepalive ( newKeepaliveTime ) ;
411+ }
412+ }
413+ }
414+
415+ removeWrappedSubchannel ( wrappedSubchannel : ChannelSubchannelWrapper ) {
416+ this . wrappedSubchannels . delete ( wrappedSubchannel ) ;
417+ }
418+
372419 doPick ( metadata : Metadata , extraPickInfo : { [ key : string ] : string } ) {
373420 return this . currentPicker . pick ( { metadata : metadata , extraPickInfo : extraPickInfo } ) ;
374421 }
0 commit comments