Summary
The BoundServer API has no way to configure a per-connection maximum age. This means servers cannot proactively evict long-lived HTTP/2 connections via GOAWAY, which is recommended for load-balanced deployments (prevents connection pinning, enables zero-downtime rolling restarts).
Tonic provides this via its MaxConnectionAge tower layer. We would like parity in connectrpc.rs.
Proposed API
```rust
impl BoundServer {
/// Send GOAWAY after a connection has been alive for `duration`.
///
/// A jitter of up to 10% is applied automatically to prevent thundering-herd
/// reconnects in large fleets.
pub fn max_connection_age(mut self, duration: Duration) -> Self { ... }
/// Grace period after the first GOAWAY before forcibly closing the connection.
///
/// During the grace window the server sends a second GOAWAY, waits for
/// in-flight streams to complete, then drops the connection. Defaults to
/// some reasonable value (e.g. 5 s) if not set.
pub fn max_connection_age_grace(mut self, duration: Duration) -> Self { ... }
}
```
Why this matters
In Kubernetes and other orchestrated environments, services rely on max-connection-age to ensure clients reconnect periodically, distributing load across pod restarts. Without it, the operator must implement this at the service-kit / middleware layer, duplicating logic that properly belongs in the transport.
Prior art
- tonic:
tonic::transport::Server::max_connection_age (wraps a tower layer)
- grpc-go:
keepalive.ServerParameters.MaxConnectionAge
- envoy:
max_connection_duration in HTTP/2 options
References
- RFC 9113 §9.3 (HTTP/2 GOAWAY semantics)
Summary
The
BoundServerAPI has no way to configure a per-connection maximum age. This means servers cannot proactively evict long-lived HTTP/2 connections via GOAWAY, which is recommended for load-balanced deployments (prevents connection pinning, enables zero-downtime rolling restarts).Tonic provides this via its
MaxConnectionAgetower layer. We would like parity in connectrpc.rs.Proposed API
```rust
impl BoundServer {
/// Send GOAWAY after a connection has been alive for `duration`.
///
/// A jitter of up to 10% is applied automatically to prevent thundering-herd
/// reconnects in large fleets.
pub fn max_connection_age(mut self, duration: Duration) -> Self { ... }
}
```
Why this matters
In Kubernetes and other orchestrated environments, services rely on max-connection-age to ensure clients reconnect periodically, distributing load across pod restarts. Without it, the operator must implement this at the service-kit / middleware layer, duplicating logic that properly belongs in the transport.
Prior art
tonic::transport::Server::max_connection_age(wraps a tower layer)keepalive.ServerParameters.MaxConnectionAgemax_connection_durationin HTTP/2 optionsReferences