Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http: add core retry policy to route retry policy conversion utility #17803

Merged
merged 16 commits into from
Sep 28, 2021
1 change: 1 addition & 0 deletions envoy/http/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ envoy_cc_library(
"//envoy/event:dispatcher_interface",
"//envoy/tracing:http_tracer_interface",
"//source/common/protobuf",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
"@envoy_api//envoy/config/route/v3:pkg_cc_proto",
],
)
Expand Down
48 changes: 48 additions & 0 deletions envoy/http/async_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,24 @@
#include <chrono>
#include <memory>

#include "envoy/config/core/v3/base.pb.h"
Shikugawa marked this conversation as resolved.
Show resolved Hide resolved
#include "envoy/config/route/v3/route_components.pb.h"
#include "envoy/event/dispatcher.h"
#include "envoy/http/message.h"
#include "envoy/stream_info/stream_info.h"
#include "envoy/tracing/http_tracer.h"

#include "source/common/protobuf/protobuf.h"
#include "source/common/protobuf/utility.h"

#include "absl/types/optional.h"

namespace Envoy {
namespace Http {
namespace {
static constexpr uint32_t RetryInitialDelayMilliseconds = 1000;
Shikugawa marked this conversation as resolved.
Show resolved Hide resolved
static constexpr uint32_t RetryMaxDelayMilliseconds = 10 * 1000;
} // namespace

/**
* Supports sending an HTTP request message and receiving a response asynchronously.
Expand Down Expand Up @@ -219,6 +225,43 @@ class AsyncClient {
return *this;
}

StreamOptions& setRetryPolicy(const envoy::config::core::v3::RetryPolicy& p,
Shikugawa marked this conversation as resolved.
Show resolved Hide resolved
std::string&& retry_on) {
Shikugawa marked this conversation as resolved.
Show resolved Hide resolved
envoy::config::route::v3::RetryPolicy route_retry_policy;

uint64_t base_interval_ms = RetryInitialDelayMilliseconds;
uint64_t max_interval_ms = RetryMaxDelayMilliseconds;

if (p.has_retry_back_off()) {
const auto& core_back_off = p.retry_back_off();

base_interval_ms = PROTOBUF_GET_MS_REQUIRED(core_back_off, base_interval);
max_interval_ms =
PROTOBUF_GET_MS_OR_DEFAULT(core_back_off, max_interval, base_interval_ms * 10);

if (max_interval_ms < base_interval_ms) {
Shikugawa marked this conversation as resolved.
Show resolved Hide resolved
max_interval_ms = base_interval_ms * 10;
}
}

route_retry_policy.mutable_num_retries()->set_value(
PROTOBUF_GET_WRAPPED_OR_DEFAULT(p, num_retries, 1));

auto* route_mutable_back_off = route_retry_policy.mutable_retry_back_off();

route_mutable_back_off->mutable_base_interval()->CopyFrom(
Protobuf::util::TimeUtil::MillisecondsToDuration(base_interval_ms));
route_mutable_back_off->mutable_max_interval()->CopyFrom(
Protobuf::util::TimeUtil::MillisecondsToDuration(max_interval_ms));

// set all the other fields with appropriate values.
route_retry_policy.set_retry_on(retry_on);
route_retry_policy.mutable_per_try_timeout()->CopyFrom(
route_retry_policy.retry_back_off().max_interval());

return setRetryPolicy(route_retry_policy);
}

// For gmock test
bool operator==(const StreamOptions& src) const {
return timeout == src.timeout && buffer_body_for_retry == src.buffer_body_for_retry &&
Expand Down Expand Up @@ -286,6 +329,11 @@ class AsyncClient {
StreamOptions::setRetryPolicy(p);
return *this;
}
RequestOptions& setRetryPolicy(const envoy::config::core::v3::RetryPolicy& p,
std::string&& retry_on) {
Shikugawa marked this conversation as resolved.
Show resolved Hide resolved
StreamOptions::setRetryPolicy(p, std::move(retry_on));
return *this;
}
RequestOptions& setParentSpan(Tracing::Span& parent_span) {
parent_span_ = &parent_span;
return *this;
Expand Down
66 changes: 2 additions & 64 deletions source/extensions/filters/http/common/jwks_fetcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,60 +16,6 @@ namespace Extensions {
namespace HttpFilters {
namespace Common {
namespace {
// Parameters of the jittered backoff strategy.
static constexpr uint32_t RetryInitialDelayMilliseconds = 1000;
static constexpr uint32_t RetryMaxDelayMilliseconds = 10 * 1000;

/**
* @details construct an envoy.config.route.v3.RetryPolicy protobuf message
* from a less feature rich envoy.config.core.v3.RetryPolicy one.
*
* this is about limiting the user's possibilities.
* just doing truncated exponential backoff
*
* the upstream.use_retry feature flag will need to be turned on (default)
* for this to work.
*
* @param retry policy from the RemoteJwks proto
* @return a retry policy usable by the http async client.
*/
envoy::config::route::v3::RetryPolicy
adaptRetryPolicy(const envoy::config::core::v3::RetryPolicy& core_retry_policy) {
envoy::config::route::v3::RetryPolicy route_retry_policy;

uint64_t base_interval_ms = RetryInitialDelayMilliseconds;
uint64_t max_interval_ms = RetryMaxDelayMilliseconds;

if (core_retry_policy.has_retry_back_off()) {
const auto& core_back_off = core_retry_policy.retry_back_off();

base_interval_ms = PROTOBUF_GET_MS_REQUIRED(core_back_off, base_interval);

max_interval_ms =
PROTOBUF_GET_MS_OR_DEFAULT(core_back_off, max_interval, base_interval_ms * 10);

if (max_interval_ms < base_interval_ms) {
throw EnvoyException("max_interval must be greater than or equal to the base_interval");
}
}

route_retry_policy.mutable_num_retries()->set_value(
PROTOBUF_GET_WRAPPED_OR_DEFAULT(core_retry_policy, num_retries, 1));

auto* route_mutable_back_off = route_retry_policy.mutable_retry_back_off();

route_mutable_back_off->mutable_base_interval()->CopyFrom(
Protobuf::util::TimeUtil::MillisecondsToDuration(base_interval_ms));
route_mutable_back_off->mutable_max_interval()->CopyFrom(
Protobuf::util::TimeUtil::MillisecondsToDuration(max_interval_ms));

// set all the other fields with appropriate values.
route_retry_policy.set_retry_on("5xx,gateway-error,connect-failure,reset");
route_retry_policy.mutable_per_try_timeout()->CopyFrom(
route_retry_policy.retry_back_off().max_interval());

return route_retry_policy;
}

class JwksFetcherImpl : public JwksFetcher,
public Logger::Loggable<Logger::Id::filter>,
Expand All @@ -78,10 +24,6 @@ class JwksFetcherImpl : public JwksFetcher,
JwksFetcherImpl(Upstream::ClusterManager& cm, const RemoteJwks& remote_jwks)
: cm_(cm), remote_jwks_(remote_jwks) {
ENVOY_LOG(trace, "{}", __func__);

if (remote_jwks_.has_retry_policy()) {
route_retry_policy_ = adaptRetryPolicy(remote_jwks_.retry_policy());
}
}

~JwksFetcherImpl() override { cancel(); }
Expand Down Expand Up @@ -122,7 +64,8 @@ class JwksFetcherImpl : public JwksFetcher,
.setChildSpanName("JWT Remote PubKey Fetch");

if (remote_jwks_.has_retry_policy()) {
options.setRetryPolicy(route_retry_policy_.value());
options.setRetryPolicy(remote_jwks_.retry_policy(),
"5xx,gateway-error,connect-failure,reset");
options.setBufferBodyForRetry(true);
}

Expand Down Expand Up @@ -178,11 +121,6 @@ class JwksFetcherImpl : public JwksFetcher,
const RemoteJwks& remote_jwks_;
Http::AsyncClient::Request* request_{};

// http async client uses richer semantics than the ones allowed in RemoteJwks
// envoy.config.route.v3.RetryPolicy vs envoy.config.core.v3.RetryPolicy
// mapping is done in constructor and reused.
absl::optional<envoy::config::route::v3::RetryPolicy> route_retry_policy_{absl::nullopt};

void reset() {
request_ = nullptr;
receiver_ = nullptr;
Expand Down