gRPC Connectivity Semantics and API

Original text: github.com/grpc/grpc/b…

This article mainly describes the connection semantics of gRPC channels and the corresponding impact on RPC, and then discusses the API.

States of Connectivity

GRPC Channels provides an abstraction of the clients and Servers interaction. The client channel object can be constructed with a bit more information using a DNS name. Channels encapsulates a number of functions, including name resolution, establishing TCP connections (including Retries and Backoff), and TLS handshakes. Channels can also handle errors on established connections and reconnect, or in the case of HTTP/2 GO_AWAY, reresolve the name and reconnect.

To hide all the details of this activity from the users of the gRPC API (that is, the application code) while exposing meaningful information about the channel state, we use a state machine with five state representations defined as follows:

CONNECTING:

Indicates that the channel is trying to establish a connection and is waiting for progress on one of the steps involved in name resolution, TCP connection establishment, or TLS handshake. It can be used as the initial state when creating a channel.

READY:

Indicates that the channel has successfully established a connection through a TLS handshake (or equivalent) and protocol-level (HTTP/2, etc.) handshake, and that all subsequent communication attempts have been successful (or are waiting without any known failures).

TRANSIENT_FAILURE:

Indicates that the channel has some transient failure (for example, TCP three-way handshake timeout or socket error). A channel in this state will eventually switch to the CONNECTING state and try to establish a connection again.

Since retries are done exponentially backoff, a channel that fails to connect will initially spend very little time in this state, but will spend more and more time in this state as the number of repeated attempts and failures increases. For many non-fatal failures (for example, TCP connection attempts timed out because the server is not yet available), channels can spend an increasing amount of time in that state.

IDLE:

This state indicates that the channel did not even attempt to create a connection due to a lack of new or pending RPCS. New RPCS may be created in this state. Any attempt to start an RPC on a channel pushes the channel from this state to the CONNECTING state.

If there is already no RPC activity on the channel within the specified IDLE_TIMEOUT period, that is, no new (new) or pending (or active) RPCS during this period, A channel in the READY or CONNECTING state changes to IDLE. Normally, the default value of IDLE_TIMEOUT is 300 seconds.

In addition, channels that have received GOAWAY should also switch to IDLE when there are no active or pending RPCs to avoid connection overload on servers trying to disconnect.

SHUTDOWN:

Indicates that the channel has started to close. Any new RPCs should fail immediately. Pending RPCs may continue to run until the application cancels them. A channel may enter this state because an application explicitly requests that it be closed, or because an unrecoverable error occurred during an attempt to connect communication. (As of December 6, 2015, no known errors (when connecting or communicating) have been classified as unrecoverable errors.)

Once a channel is SHUTDOWN, it never leaves. In other words, SHUTDOWN is the end of the state machine.

State transition table

The following table lists the valid state transitions and their causes. An empty cell indicates that the corresponding state transition is not allowed.

From/To CONNECTING READY TRANSIENT_FAILURE IDLE SHUTDOWN
CONNECTING Incremental progress during connection establishment All steps needed to establish a connection succeeded Any failure in any of the steps needed to establish connection No RPC activity on channel for IDLE_TIMEOUT Shutdown triggered by application.
READY Incremental successful communication on established channel. Any failure encountered while expecting successful communication on established channel. No RPC activity on channel for IDLE_TIMEOUT OR upon receiving a GOAWAY while there are no pending RPCs. Shutdown triggered by application.
TRANSIENT_FAILURE Wait time required to implement (exponential) backoff is over. Shutdown triggered by application.
IDLE Any new RPC activity on the channel Shutdown triggered by application.
SHUTDOWN

Channel State API

All gRPC libraries should expose a channel-level API method to poll a channel for its current state.

In C++, this method is called GetState, and it returns an enumerated value that identifies one of the five legal states. If a channel is currently IDLE, it also accepts the Boolean value try_to_connect for CONNECTING. The boolean should act as if an RPC occurred, so it should also reset IDLE_TIMEOUT. grpc_connectivity_state GetState(bool try_to_connect);

All gRPC libraries should also expose an API so that applications (users of the gRPC API) can be notified when a channel state changes. Because state changes can be fast and compete with any such notifications, notifications should only notify the user that some state change has occurred, leaving it up to the user to poll the channel for the current state. The asynchronous version of the API is as follows: bool WaitForStateChange(GRPC_connectivity_state source_state, gPR_timespec deadline);

Return true if the state is not source_state or false if the cutoff time expires. An Asynchronous- and futures-based API should have a corresponding method that allows the application to be notified when the channel state changes.

Notice that notifications are sent whenever there is a transition from any state to any other state.

That is, the rules for legitimate state transitions require transition from CONNECTING to TRANSIENT_FAILURE and return to each recoverable connection, even if the corresponding exponential retreat does not need to wait before retrying. The combined effect is that applications may receive seemingly spurious notifications of status changes. For example, an application waiting for a state change on a channel in the CONNECTING state might receive notification of a state change, but find that it is still in CONNECTING state when polling the current state. This is because the channel may spend very little time in TRANSIENT_FAILURE state.