diff --git a/docs/root/configuration/http_filters/router_filter.rst b/docs/root/configuration/http_filters/router_filter.rst index ba69cee9f2af1..0a72ecdc1b836 100644 --- a/docs/root/configuration/http_filters/router_filter.rst +++ b/docs/root/configuration/http_filters/router_filter.rst @@ -127,6 +127,9 @@ cancelled deadline-exceeded Envoy will attempt a retry if the gRPC status code in the response headers is "deadline-exceeded" (4) +internal + Envoy will attempt to retry if the gRPC status code in the response headers is "internal" (13) + resource-exhausted Envoy will attempt a retry if the gRPC status code in the response headers is "resource-exhausted" (8) diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst index d68c522b1bf57..75c2d1b4cd58a 100644 --- a/docs/root/intro/version_history.rst +++ b/docs/root/intro/version_history.rst @@ -7,6 +7,7 @@ Version history * router: added ability to configure arbitrary :ref:`retriable status codes. ` * router: added ability to set attempt count in upstream requests, see :ref:`virtual host's include request attempt count flag `. +* router: added internal :ref:`grpc-retry-on ` policy. * router: when :ref:`max_grpc_timeout ` is set, Envoy will now add or update the grpc-timeout header to reflect Envoy's expected timeout. * stats: added :ref:`stats_matcher ` to the bootstrap config for granular control of stat instantiation. diff --git a/include/envoy/router/router.h b/include/envoy/router/router.h index af70d06c6eda3..97e95101134dd 100644 --- a/include/envoy/router/router.h +++ b/include/envoy/router/router.h @@ -151,7 +151,8 @@ class RetryPolicy { static const uint32_t RETRY_ON_GRPC_DEADLINE_EXCEEDED = 0x40; static const uint32_t RETRY_ON_GRPC_RESOURCE_EXHAUSTED = 0x80; static const uint32_t RETRY_ON_GRPC_UNAVAILABLE = 0x100; - static const uint32_t RETRY_ON_RETRIABLE_STATUS_CODES = 0x200; + static const uint32_t RETRY_ON_GRPC_INTERNAL = 0x200; + static const uint32_t RETRY_ON_RETRIABLE_STATUS_CODES = 0x400; // clang-format on virtual ~RetryPolicy() {} diff --git a/source/common/http/headers.h b/source/common/http/headers.h index ec87280fac338..feff15b32a0ef 100644 --- a/source/common/http/headers.h +++ b/source/common/http/headers.h @@ -155,6 +155,7 @@ class HeaderValues { const std::string DeadlineExceeded{"deadline-exceeded"}; const std::string ResourceExhausted{"resource-exhausted"}; const std::string Unavailable{"unavailable"}; + const std::string Internal{"internal"}; } EnvoyRetryOnGrpcValues; struct { diff --git a/source/common/router/retry_state_impl.cc b/source/common/router/retry_state_impl.cc index b366eed4035fa..b58af360888a6 100644 --- a/source/common/router/retry_state_impl.cc +++ b/source/common/router/retry_state_impl.cc @@ -132,6 +132,8 @@ uint32_t RetryStateImpl::parseRetryGrpcOn(absl::string_view retry_grpc_on_header ret |= RetryPolicy::RETRY_ON_GRPC_RESOURCE_EXHAUSTED; } else if (retry_on == Http::Headers::get().EnvoyRetryOnGrpcValues.Unavailable) { ret |= RetryPolicy::RETRY_ON_GRPC_UNAVAILABLE; + } else if (retry_on == Http::Headers::get().EnvoyRetryOnGrpcValues.Internal) { + ret |= RetryPolicy::RETRY_ON_GRPC_INTERNAL; } } @@ -239,8 +241,8 @@ bool RetryStateImpl::wouldRetry(const Http::HeaderMap* response_headers, if (retry_on_ & (RetryPolicy::RETRY_ON_GRPC_CANCELLED | RetryPolicy::RETRY_ON_GRPC_DEADLINE_EXCEEDED | - RetryPolicy::RETRY_ON_GRPC_RESOURCE_EXHAUSTED | - RetryPolicy::RETRY_ON_GRPC_UNAVAILABLE) && + RetryPolicy::RETRY_ON_GRPC_RESOURCE_EXHAUSTED | RetryPolicy::RETRY_ON_GRPC_UNAVAILABLE | + RetryPolicy::RETRY_ON_GRPC_INTERNAL) && response_headers) { absl::optional status = Grpc::Common::getGrpcStatus(*response_headers); @@ -252,7 +254,9 @@ bool RetryStateImpl::wouldRetry(const Http::HeaderMap* response_headers, (status.value() == Grpc::Status::ResourceExhausted && (retry_on_ & RetryPolicy::RETRY_ON_GRPC_RESOURCE_EXHAUSTED)) || (status.value() == Grpc::Status::Unavailable && - (retry_on_ & RetryPolicy::RETRY_ON_GRPC_UNAVAILABLE))) { + (retry_on_ & RetryPolicy::RETRY_ON_GRPC_UNAVAILABLE)) || + (status.value() == Grpc::Status::Internal && + (retry_on_ & RetryPolicy::RETRY_ON_GRPC_INTERNAL))) { return true; } } diff --git a/test/common/router/retry_state_impl_test.cc b/test/common/router/retry_state_impl_test.cc index 2b74a45cd2fc6..c0146783f5fdd 100644 --- a/test/common/router/retry_state_impl_test.cc +++ b/test/common/router/retry_state_impl_test.cc @@ -242,6 +242,20 @@ TEST_F(RouterRetryStateImplTest, PolicyGrpcUnavilable) { EXPECT_EQ(RetryStatus::No, state_->shouldRetry(&response_headers, no_reset_, callback_)); } +TEST_F(RouterRetryStateImplTest, PolicyGrpcInternal) { + Http::TestHeaderMapImpl request_headers{{"x-envoy-retry-grpc-on", "internal"}}; + setup(request_headers); + EXPECT_TRUE(state_->enabled()); + + Http::TestHeaderMapImpl response_headers{{":status", "200"}, {"grpc-status", "13"}}; + expectTimerCreateAndEnable(); + EXPECT_EQ(RetryStatus::Yes, state_->shouldRetry(&response_headers, no_reset_, callback_)); + EXPECT_CALL(callback_ready_, ready()); + retry_timer_->callback_(); + + EXPECT_EQ(RetryStatus::No, state_->shouldRetry(&response_headers, no_reset_, callback_)); +} + TEST_F(RouterRetryStateImplTest, Policy5xxRemote200RemoteReset) { // Don't retry after reply start. Http::TestHeaderMapImpl request_headers{{"x-envoy-retry-on", "5xx"}};