Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions site/content/en/latest/tasks/traffic/grpc-timeouts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
---
title: "GRPC Timeouts"
---

Unlike [HTTPRoute][], the Gateway API [GRPCRoute][] resource does not (yet) expose a native
`timeouts` field — see the upstream tracking issue [Support Request Timeouts for GRPCRoute][gapi-3139].
Until that lands, Envoy Gateway lets you configure timeouts for gRPC traffic with a
[BackendTrafficPolicy][] that targets the `GRPCRoute`.

The default request timeout is 15 seconds in Envoy Proxy, which will terminate long-lived
streaming RPCs. The relevant `spec.timeout.http` fields are:

- **requestTimeout**: the maximum duration for the entire response to be received from the
upstream. This bounds **unary** RPCs. Set it to `"0s"` to disable it for **streaming** RPCs,
which otherwise would be cut off once the timeout elapses.
- **maxStreamDuration**: the maximum duration of a stream, measured from when the request is sent
until the response stream is fully consumed. It does not apply to non-streaming requests. Set it
to `"0s"` to allow streams to run indefinitely.
- **streamIdleTimeout**: the amount of time a stream may exist with no upstream or downstream
activity. Use this to reclaim idle streams without capping a healthy long-lived stream's total
duration.

## Prerequisites

{{< boilerplate prerequisites >}}

Follow the [GRPC Routing](../grpc-routing) task to set up a `Gateway` and a `GRPCRoute` named
`yages` before configuring timeouts.

__Note:__ A `GRPCRoute` can have at most one `BackendTrafficPolicy` attached to it; a second policy
targeting the same route is rejected as `Conflicted`. The two examples below are therefore
alternatives that reuse the same policy name (`grpc-timeouts`) — pick the one that matches your
workload. Re-applying with the same `metadata.name` updates the existing policy rather than creating
a conflicting second one.

## Unary RPCs

Set `requestTimeout` to bound the duration of unary RPCs. Here, unary calls that take longer than
5 seconds are terminated with a timeout.

{{< tabpane text=true >}}
{{% tab header="Apply from stdin" %}}

```shell
cat <<EOF | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
name: grpc-timeouts
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: GRPCRoute
name: yages
timeout:
http:
requestTimeout: "5s"
EOF
```

{{% /tab %}}
{{% tab header="Apply from file" %}}
Save and apply the following resource to your cluster:

```yaml
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
name: grpc-timeouts
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: GRPCRoute
name: yages
timeout:
http:
requestTimeout: "5s"
```

{{% /tab %}}
{{< /tabpane >}}

## Streaming RPCs

For server-streaming, client-streaming, or bidirectional-streaming RPCs, `requestTimeout` would
terminate the stream once it elapses. Disable it with `"0s"` and, if you want an upper bound, use
`maxStreamDuration` (or `streamIdleTimeout` to reclaim only idle streams). The example below lets
streams run indefinitely while reclaiming streams that are idle for more than 1 hour.

{{< tabpane text=true >}}
{{% tab header="Apply from stdin" %}}

```shell
cat <<EOF | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
name: grpc-timeouts
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: GRPCRoute
name: yages
Comment on lines +101 to +104

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid applying two policies to the same GRPCRoute

When readers follow this task top-to-bottom, the unary example has already created a BackendTrafficPolicy targeting GRPCRoute/yages, and this streaming example creates a second policy for that same route. Envoy Gateway processes BackendTrafficPolicies in creation order and resolveBackendTrafficPolicyRouteTargetRef rejects a later policy that targets an already-attached route as Conflicted, so grpc-stream-timeout will not be accepted and the streaming timeout settings won't take effect unless the reader deletes/reuses the first policy or uses a separate route.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 2c85563. Both examples now reuse a single BackendTrafficPolicy named grpc-timeouts, and I added a note in Prerequisites that a GRPCRoute accepts only one BackendTrafficPolicy (a second is rejected as Conflicted), so the two examples are alternatives and re-applying updates the same policy rather than creating a conflicting second one.

timeout:
http:
# Disable the per-request timeout so long-lived streams are not cut off.
requestTimeout: "0s"
# Allow streams to run indefinitely; set a non-zero value to cap them.
maxStreamDuration: "0s"
# Reclaim streams with no activity for more than 1 hour.
streamIdleTimeout: "1h"
EOF
```

{{% /tab %}}
{{% tab header="Apply from file" %}}
Save and apply the following resource to your cluster:

```yaml
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
name: grpc-timeouts
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: GRPCRoute
name: yages
timeout:
http:
# Disable the per-request timeout so long-lived streams are not cut off.
requestTimeout: "0s"
# Allow streams to run indefinitely; set a non-zero value to cap them.
maxStreamDuration: "0s"
# Reclaim streams with no activity for more than 1 hour.
streamIdleTimeout: "1h"
```

{{% /tab %}}
{{< /tabpane >}}

## Verification

First confirm the policy is accepted:

```shell
kubectl get backendtrafficpolicy/grpc-timeouts -o yaml
```

The status should reflect `Accepted=True` on the targeted `GRPCRoute` ancestor.

Then confirm the timeout is actually programmed into the Envoy route config with
[egctl](../../operations/egctl):

```shell
egctl config envoy-proxy route \
--labels gateway.envoyproxy.io/owning-gateway-name=eg,gateway.envoyproxy.io/owning-gateway-namespace=default \
-o yaml | grep -A2 -E 'timeout|maxStreamDuration'
```

For the **unary** example you should see the route's `timeout` set to the configured
`requestTimeout` (e.g. `timeout: 5s`). For the **streaming** example you should see
`timeout: 0s` (disabled) together with `maxStreamDuration` on the route action.

To exercise the timeout end-to-end you need a gRPC backend that can delay or stream (the
sample `yages` echo server used in the [GRPC Routing](../grpc-routing) task returns
immediately). Against such a backend, a unary call that exceeds `requestTimeout` returns
gRPC status `DEADLINE_EXCEEDED` (HTTP `504`), for example with
[grpcurl](https://github.com/fullstorydev/grpcurl):

```shell
grpcurl -plaintext -authority=grpc-example.com ${GATEWAY_HOST}:80 <your.slow.Method>
# ERROR:
# Code: DeadlineExceeded
```

[HTTPRoute]: https://gateway-api.sigs.k8s.io/reference/api-types/httproute/
[GRPCRoute]: https://gateway-api.sigs.k8s.io/reference/api-types/grpcroute/
[BackendTrafficPolicy]: ../../../api/extension_types#backendtrafficpolicy
[gapi-3139]: https://github.com/kubernetes-sigs/gateway-api/issues/3139