diff --git a/A117-ring-hash-exit-idle.md b/A117-ring-hash-exit-idle.md new file mode 100644 index 000000000..054f4acde --- /dev/null +++ b/A117-ring-hash-exit-idle.md @@ -0,0 +1,85 @@ +A117: Ring Hash Exit Idle Behavior Changes +---- +* Author(s): dfawley@ +* Approver(s): markdroth@, ejona@ +* Implemented in: +* Last updated: 2026-05-15 +* Discussion at: https://groups.google.com/g/grpc-io/c/ByCPdvi5lrQ + +## Abstract + +Update the Ring Hash LB policy to create connections when explictly requested by +the channel to do so, instead of ignoring the request. + +## Background + +The Ring Hash LB policy, as defined in [gRFC A42][A42], does not create +connections upon creation. Even though it is not specified, all implementations +of gRPC also ignored calls to the "exit idle" / "request connection" method. + +It has been observed that this behavior can cause problems, however. +Specifically, when using the gRPC [connectivity API][], if an application +requests a channel using ring hash to connect, but ring hash ignores the +incoming request from the channel, the application will never see a change to +the state of the channel. Further, if this channel's connectivity state is used +to compute the health of a server using that channel, that can cause serious +problems, as an unhealthy server will not receive the traffic it needs to make +ring hash connect. + +### Related Proposals: + +* [gRFC A42: xDS Ring Hash LB Policy][A42] +* [gRFC A76: Improvements to the Ring Hash LB Policy][A76] +* [gRFC A61: IPv4 and IPv6 Dualstack Backend Support][A61rh] + +## Proposal + +This proposal makes two behavior changes: + +1. Ring Hash should enter the CONNECTING state if it is currently in the IDLE + state and the channel requests it to exit idle mode, instead of ignoring the + request. + + This will cause the eager-connection logic described in [gRFC A61][A61rh] to + trigger: + + > when the aggregated connectivity state is either TRANSIENT_FAILURE or + > CONNECTING, the ring_hash policy proactively triggers connection attempts + > across all of the subchannels, even without seeing any picks. + +2. New connection attempts not triggered by an RPC should begin with a random + endpoint, ignoring endpoint weights. This is a behavior change from [gRFC + A61][A61rh]'s TRANSIENT_FAILURE / CONNECTING behavior, which currently states + the choice of endpoints is up to the implementation: + + > It does not matter which IDLE endpoint is chosen; that is left up to the + > implementation to determine. + + Using a randomized endpoint will ensure that load is distributed evenly + across available backends if many individual clients are instructed to + connect at the same time. + +### Temporary environment variable protection + +Even though this is a behavior change, as the behavior is triggered by the +application, this feature does not need to be protected by a standardized +environment variable. Individual langauge implementations may wish to provide +their language-specific mechanism to revert the behavior change temporarily in +the event that issues are encountered by users. + +## Rationale + +Another option is to change nothing in the Ring Hash policy and require users to +consider "IDLE" as a "healthy" state in their application. This is still +surprising / unintuitive behavior, and it would be too hard to communicate this +to all users. + +## Implementation + +The implementation should be done in all languages that support Ring Hash. + + +[A42]: A42-xds-ring-hash-lb-policy.md +[A76]: A76-ring-hash-improvements.md +[A61rh]: A61-IPv4-IPv6-dualstack-backends.md#ring-hash +[connectivity API]: https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md diff --git a/A42-xds-ring-hash-lb-policy.md b/A42-xds-ring-hash-lb-policy.md index 90726cf22..df24c1a45 100644 --- a/A42-xds-ring-hash-lb-policy.md +++ b/A42-xds-ring-hash-lb-policy.md @@ -6,7 +6,8 @@ A42: xDS Ring Hash LB Policy * Implemented in: Java (C-core and Go in progress) * Last updated: 2021-06-04 * Discussion at: https://groups.google.com/g/grpc-io/c/_Z_oiWVXf6k -* Updated by: [A61: IPv4 and IPv6 Dualstack Backend Support](A61-IPv4-IPv6-dualstack-backends.md) +* Updated by: [A61: IPv4 and IPv6 Dualstack Backend Support](A61-IPv4-IPv6-dualstack-backends.md), [A76: Improvements to the Ring Hash LB Policy](A76-ring-hash-improvements.md), and [A117: Ring Hash Exit Idle Behavior Changes](A117-ring-hash-exit-idle.md) + ## Abstract diff --git a/A61-IPv4-IPv6-dualstack-backends.md b/A61-IPv4-IPv6-dualstack-backends.md index 6eb690b41..8243f0145 100644 --- a/A61-IPv4-IPv6-dualstack-backends.md +++ b/A61-IPv4-IPv6-dualstack-backends.md @@ -6,6 +6,7 @@ A61: IPv4 and IPv6 Dualstack Backend Support * Implemented in: C-core * Last updated: 2025-03-06 * Discussion at: https://groups.google.com/g/grpc-io/c/VjORlKP97cE/m/ihqyN32TAQAJ +* Updated by: [A76: Improvements to the Ring Hash LB Policy](A76-ring-hash-improvements.md) and [A117: Ring Hash Exit Idle Behavior Changes][A117] ## Abstract @@ -43,7 +44,7 @@ addresses per endpoint in xDS. We will support the new xDS APIs being added for that effort as well. Note that this change has implications for session affinity behavior in xDS. -### Related Proposals: +### Related Proposals: * [Support for dual stack EDS endpoints in Envoy][envoy-design] * [gRFC A17: Client-Side Health Checking][A17] * [gRFC A27: xDS-Based Global Load Balancing][A27] @@ -596,10 +597,13 @@ if the aggregated connectivity state is TRANSIENT_FAILURE or CONNECTING and there are no endpoints in CONNECTING state, the ring_hash policy will choose one of the endpoints in IDLE state (if any) to trigger a connection attempt on. It does not matter which IDLE endpoint is chosen; that is -left up to the implementation to determine. One possible implementation -of this is shown in the following pseudo-code: +left up to the implementation to determine. (Update: gRFC [A117][] now +specifies a _random_ endpoint should be chosen.) One possible implementation of +this is shown in the following pseudo-code: ``` +# Updated to show a randomized starting location per gRFC A117. +endpoints = shuffle(endpoints); if (aggregated_state_is_connecting_or_transient_failure) { first_idle_index = -1; for (i = 0; i < endpoints.size(); ++i) { @@ -961,3 +965,4 @@ N/A [RFC-8305]: https://www.rfc-editor.org/rfc/rfc8305 [A62]: A62-pick-first.md [backoff-spec]: https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md +[A117]: A117-ring-hash-exit-idle.md