diff --git a/PACKAGE-COMPARISON.md b/PACKAGE-COMPARISON.md index 8bea1fd0..fa0ea319 100644 --- a/PACKAGE-COMPARISON.md +++ b/PACKAGE-COMPARISON.md @@ -36,6 +36,7 @@ In addition, all channel arguments defined in [this header file](https://github. - `grpc.default_authority` - `grpc.keepalive_time_ms` - `grpc.keepalive_timeout_ms` + - `grpc.keepalive_permit_without_calls` - `grpc.service_config` - `grpc.max_concurrent_streams` - `grpc.initial_reconnect_backoff_ms` diff --git a/packages/grpc-js/src/channel-options.ts b/packages/grpc-js/src/channel-options.ts index f316a58a..df6cbb2b 100644 --- a/packages/grpc-js/src/channel-options.ts +++ b/packages/grpc-js/src/channel-options.ts @@ -25,6 +25,7 @@ export interface ChannelOptions { 'grpc.default_authority'?: string; 'grpc.keepalive_time_ms'?: number; 'grpc.keepalive_timeout_ms'?: number; + 'grpc.keepalive_permit_without_calls'?: number; 'grpc.service_config'?: string; 'grpc.max_concurrent_streams'?: number; 'grpc.initial_reconnect_backoff_ms'?: number; @@ -49,6 +50,7 @@ export const recognizedOptions = { 'grpc.default_authority': true, 'grpc.keepalive_time_ms': true, 'grpc.keepalive_timeout_ms': true, + 'grpc.keepalive_permit_without_calls': true, 'grpc.service_config': true, 'grpc.max_concurrent_streams': true, 'grpc.initial_reconnect_backoff_ms': true, diff --git a/packages/grpc-js/src/subchannel.ts b/packages/grpc-js/src/subchannel.ts index b9535769..3c607144 100644 --- a/packages/grpc-js/src/subchannel.ts +++ b/packages/grpc-js/src/subchannel.ts @@ -180,6 +180,10 @@ export class Subchannel { * Timer reference tracking when the most recent ping will be considered lost */ private keepaliveTimeoutId: NodeJS.Timer; + /** + * Indicates whether keepalive pings should be sent without any active calls + */ + private keepaliveWithoutCalls: boolean = false; /** * Tracks calls with references to this subchannel @@ -226,6 +230,11 @@ export class Subchannel { if ('grpc.keepalive_timeout_ms' in options) { this.keepaliveTimeoutMs = options['grpc.keepalive_timeout_ms']!; } + if ('grpc.keepalive_permit_without_calls' in options) { + this.keepaliveWithoutCalls = options['grpc.keepalive_permit_without_calls'] === 1; + } else { + this.keepaliveWithoutCalls = false; + } this.keepaliveIntervalId = setTimeout(() => {}, 0); clearTimeout(this.keepaliveIntervalId); this.keepaliveTimeoutId = setTimeout(() => {}, 0); @@ -532,6 +541,9 @@ export class Subchannel { listener(); } }); + if (this.keepaliveWithoutCalls) { + this.startKeepalivePings(); + } break; case ConnectivityState.CONNECTING: this.startBackoff(); @@ -602,7 +614,9 @@ export class Subchannel { if (this.session) { this.session.ref(); } - this.startKeepalivePings(); + if (!this.keepaliveWithoutCalls) { + this.startKeepalivePings(); + } } this.callRefcount += 1; } @@ -620,7 +634,9 @@ export class Subchannel { if (this.session) { this.session.unref(); } - this.stopKeepalivePings(); + if (!this.keepaliveWithoutCalls) { + this.stopKeepalivePings(); + } this.checkBothRefcounts(); } }