From 28216c2164e323e02c6b26730189c6d0783d3e52 Mon Sep 17 00:00:00 2001 From: Hirokazu Hata Date: Mon, 16 Mar 2026 15:32:02 +0900 Subject: [PATCH] Add HTTP/2 keepalive options to ConnectionOptions Add http2_keep_alive_interval, keep_alive_timeout, and keep_alive_while_idle fields to ConnectionOptions in gax, and expose them through each gRPC service crate's config structs so users can configure HTTP/2 PING-based keepalive to detect dead connections promptly. --- artifact-registry/src/client.rs | 9 +++++++++ bigquery/src/client.rs | 21 +++++++++++++++++++++ foundation/gax/src/conn.rs | 15 +++++++++++++++ spanner/src/admin/client.rs | 3 +++ spanner/src/admin/mod.rs | 6 ++++++ spanner/src/client.rs | 9 +++++++++ 6 files changed, 63 insertions(+) diff --git a/artifact-registry/src/client.rs b/artifact-registry/src/client.rs index 42bb7f82..cebcc052 100644 --- a/artifact-registry/src/client.rs +++ b/artifact-registry/src/client.rs @@ -15,6 +15,9 @@ pub struct ClientConfig { pub token_source_provider: Box, pub timeout: Option, pub connect_timeout: Option, + pub http2_keep_alive_interval: Option, + pub keep_alive_timeout: Option, + pub keep_alive_while_idle: Option, } #[cfg(feature = "auth")] @@ -56,6 +59,9 @@ impl Default for ClientConfig { token_source_provider: Box::new(NoopTokenSourceProvider {}), timeout: Some(Duration::from_secs(30)), connect_timeout: Some(Duration::from_secs(30)), + http2_keep_alive_interval: None, + keep_alive_timeout: None, + keep_alive_while_idle: None, } } } @@ -70,6 +76,9 @@ impl Client { let conn_options = ConnectionOptions { timeout: config.timeout, connect_timeout: config.connect_timeout, + http2_keep_alive_interval: config.http2_keep_alive_interval, + keep_alive_timeout: config.keep_alive_timeout, + keep_alive_while_idle: config.keep_alive_while_idle, }; let conn_pool = ConnectionManager::new( 1, diff --git a/bigquery/src/client.rs b/bigquery/src/client.rs index 8c68d148..14f1ccf6 100644 --- a/bigquery/src/client.rs +++ b/bigquery/src/client.rs @@ -148,6 +148,9 @@ pub struct ChannelConfig { num_channels: usize, connect_timeout: Option, timeout: Option, + http2_keep_alive_interval: Option, + keep_alive_timeout: Option, + keep_alive_while_idle: Option, } impl ChannelConfig { @@ -163,6 +166,18 @@ impl ChannelConfig { self.timeout = Some(value); self } + pub fn with_http2_keep_alive_interval(mut self, value: Duration) -> Self { + self.http2_keep_alive_interval = Some(value); + self + } + pub fn with_keep_alive_timeout(mut self, value: Duration) -> Self { + self.keep_alive_timeout = Some(value); + self + } + pub fn with_keep_alive_while_idle(mut self, value: bool) -> Self { + self.keep_alive_while_idle = Some(value); + self + } async fn into_connection_manager( self, @@ -174,6 +189,9 @@ impl ChannelConfig { &ConnectionOptions { timeout: self.timeout, connect_timeout: self.connect_timeout, + http2_keep_alive_interval: self.http2_keep_alive_interval, + keep_alive_timeout: self.keep_alive_timeout, + keep_alive_while_idle: self.keep_alive_while_idle, }, ) .await @@ -186,6 +204,9 @@ impl Default for ChannelConfig { num_channels: 4, connect_timeout: Some(Duration::from_secs(30)), timeout: None, + http2_keep_alive_interval: None, + keep_alive_timeout: None, + keep_alive_while_idle: None, } } } diff --git a/foundation/gax/src/conn.rs b/foundation/gax/src/conn.rs index e25357af..81871213 100644 --- a/foundation/gax/src/conn.rs +++ b/foundation/gax/src/conn.rs @@ -98,6 +98,9 @@ where pub struct ConnectionOptions { pub timeout: Option, pub connect_timeout: Option, + pub http2_keep_alive_interval: Option, + pub keep_alive_timeout: Option, + pub keep_alive_while_idle: Option, } impl ConnectionOptions { @@ -110,6 +113,18 @@ impl ConnectionOptions { Some(t) => endpoint.connect_timeout(t), None => endpoint, }; + endpoint = match self.http2_keep_alive_interval { + Some(d) => endpoint.http2_keep_alive_interval(d), + None => endpoint, + }; + endpoint = match self.keep_alive_timeout { + Some(d) => endpoint.keep_alive_timeout(d), + None => endpoint, + }; + endpoint = match self.keep_alive_while_idle { + Some(v) => endpoint.keep_alive_while_idle(v), + None => endpoint, + }; endpoint } } diff --git a/spanner/src/admin/client.rs b/spanner/src/admin/client.rs index 4f733b11..03887899 100644 --- a/spanner/src/admin/client.rs +++ b/spanner/src/admin/client.rs @@ -35,6 +35,9 @@ async fn internal_client(config: &AdminClientConfig) -> Result<(Channel, Operati let conn_options = ConnectionOptions { timeout: Some(config.timeout), connect_timeout: Some(config.connect_timeout), + http2_keep_alive_interval: config.http2_keep_alive_interval, + keep_alive_timeout: config.keep_alive_timeout, + keep_alive_while_idle: config.keep_alive_while_idle, }; let conn_pool = ConnectionManager::new(1, SPANNER, AUDIENCE, &config.environment, &conn_options).await?; let conn = conn_pool.conn(); diff --git a/spanner/src/admin/mod.rs b/spanner/src/admin/mod.rs index 9eac58be..1f44ec19 100644 --- a/spanner/src/admin/mod.rs +++ b/spanner/src/admin/mod.rs @@ -22,6 +22,9 @@ pub struct AdminClientConfig { pub timeout: Duration, /// Timeout for establishing a new gRPC connection. pub connect_timeout: Duration, + pub http2_keep_alive_interval: Option, + pub keep_alive_timeout: Option, + pub keep_alive_while_idle: Option, } impl Default for AdminClientConfig { @@ -33,6 +36,9 @@ impl Default for AdminClientConfig { }, timeout: Duration::from_secs(30), connect_timeout: Duration::from_secs(30), + http2_keep_alive_interval: None, + keep_alive_timeout: None, + keep_alive_while_idle: None, } } } diff --git a/spanner/src/client.rs b/spanner/src/client.rs index 1c453a5a..280a83a5 100644 --- a/spanner/src/client.rs +++ b/spanner/src/client.rs @@ -58,6 +58,9 @@ pub struct ChannelConfig { pub num_channels: usize, pub connect_timeout: Duration, pub timeout: Duration, + pub http2_keep_alive_interval: Option, + pub keep_alive_timeout: Option, + pub keep_alive_while_idle: Option, } impl Default for ChannelConfig { @@ -66,6 +69,9 @@ impl Default for ChannelConfig { num_channels: 4, connect_timeout: Duration::from_secs(30), timeout: Duration::from_secs(30), + http2_keep_alive_interval: None, + keep_alive_timeout: None, + keep_alive_while_idle: None, } } } @@ -197,6 +203,9 @@ impl Client { let options = ConnectionOptions { timeout: Some(config.channel_config.timeout), connect_timeout: Some(config.channel_config.connect_timeout), + http2_keep_alive_interval: config.channel_config.http2_keep_alive_interval, + keep_alive_timeout: config.channel_config.keep_alive_timeout, + keep_alive_while_idle: config.channel_config.keep_alive_while_idle, }; let conn_pool = ConnectionManager::new(pool_size, &config.environment, config.endpoint.as_str(), &options).await?;