-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
router: allow propagating attempt count in header #4536
Changes from all commits
f0ba9c9
c232954
6b5196c
748454d
a9a0e7e
b7e7c30
8c34b6c
089f360
2d277e2
5ccf298
ccba26f
9139c59
5d04f1e
14b863d
d969b65
dbea290
2ce5c0e
265a86d
94f8890
9956e67
71d07e5
66d46e7
8605e5d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,7 +25,7 @@ option (gogoproto.equal_all) = true; | |
// host header. This allows a single listener to service multiple top level domain path trees. Once | ||
// a virtual host is selected based on the domain, the routes are processed in order to see which | ||
// upstream cluster to route to or whether to perform a redirect. | ||
// [#comment:next free field: 14] | ||
// [#comment:next free field: 15] | ||
message VirtualHost { | ||
// The logical name of the virtual host. This is used when emitting certain | ||
// statistics but is not relevant for routing. | ||
|
@@ -109,6 +109,16 @@ message VirtualHost { | |
// specific; see the :ref:`HTTP filter documentation <config_http_filters>` | ||
// for if and how it is utilized. | ||
map<string, google.protobuf.Struct> per_filter_config = 12; | ||
|
||
// Decides whether the :ref:`x-envoy-attempt-count | ||
// <config_http_filters_router_x-envoy-attempt-count>` header should be included | ||
// in the upstream request. Setting this option will cause it to override any existing header | ||
// value, so in the case of two Envoys on the request path with this option enabled, the upstream | ||
// will see the attempt count as perceived by the second Envoy. Defaults to false. | ||
// This header is unaffected by the | ||
// :ref:`suppress_envoy_headers | ||
// <envoy_api_field_config.filter.http.router.v2.Router.suppress_envoy_headers>` flag. | ||
bool include_request_attempt_count = 14; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: update next free id in comment above. |
||
} | ||
|
||
// A route is both a specification of how to match a request as well as an indication of what to do | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,8 @@ Version history | |
|
||
1.9.0 (pending) | ||
=============== | ||
* router: added ability to set attempt count in upstream requests, see :ref:`virtual host's include request | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: alpha order. |
||
attempt count flag <envoy_api_field_route.VirtualHost.include_request_attempt_count>`. | ||
* stats: added :ref:`stats_matcher <envoy_api_field_config.metrics.v2.StatsConfig.stats_matcher>` to the bootstrap config for granular control of stat instantiation. | ||
* fault: removed integer percentage support. | ||
* router: when :ref:`max_grpc_timeout <envoy_api_field_route.RouteAction.max_grpc_timeout>` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -345,6 +345,11 @@ class VirtualHost { | |
template <class Derived> const Derived* perFilterConfigTyped(const std::string& name) const { | ||
return dynamic_cast<const Derived*>(perFilterConfig(name)); | ||
} | ||
|
||
/** | ||
* @return bool whether to include the request count header in upstream requests. | ||
*/ | ||
virtual bool includeAttemptCount() const PURE; | ||
}; | ||
|
||
/** | ||
|
@@ -612,7 +617,14 @@ class RouteEntry : public ResponseEntry { | |
*/ | ||
template <class Derived> const Derived* perFilterConfigTyped(const std::string& name) const { | ||
return dynamic_cast<const Derived*>(perFilterConfig(name)); | ||
} | ||
}; | ||
|
||
/** | ||
* True if the virtual host this RouteEntry belongs to is configured to include the attempt | ||
* count header. | ||
* @return bool whether x-envoy-attempt-count should be included on the upstream request. | ||
*/ | ||
virtual bool includeAttemptCount() const PURE; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I only see configuration for virtual host. Is this used? Do we need route configuration also? If we don't want this now can you comment that this is pulled from the virtual host config currently? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's used since I plumb through the option from the vhost. I'll add a comment Re whether we want it on the route configuration: at least for our purposes we want to enable it on per domain/vhost level, so I'd defer per route configuration until somebody needs it |
||
}; | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -296,6 +296,11 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::HeaderMap& headers, bool e | |
headers.removeEnvoyUpstreamRequestTimeoutAltResponse(); | ||
} | ||
|
||
include_attempt_count_ = route_entry_->includeAttemptCount(); | ||
if (include_attempt_count_) { | ||
headers.insertEnvoyAttemptCount().value(attempt_count_); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My one drive by comment is it's worth documenting how this will work when you have 2 levels of Envoys. It's not uncommon in production to have [L1 Envoy][L2 Envoy][upstream]. Granted, it's often a bad idea to have both levels doing retries as more often than not you end up with exponential retries (L2 retries 3 times, fails, L1 retries to new L1, L2 retries 3 times fails, etc) so I don't think it's a bad thing for the last Envoy in the chain to force-overwritten, but I think it's worth calling out it replaces any existing header. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds reasonable, I'll update the documentation |
||
} | ||
|
||
route_entry_->finalizeRequestHeaders(headers, callbacks_->streamInfo(), | ||
!config_.suppress_envoy_headers_); | ||
FilterUtility::setUpstreamScheme(headers, *cluster_); | ||
|
@@ -762,13 +767,18 @@ bool Filter::setupRetry(bool end_stream) { | |
|
||
void Filter::doRetry() { | ||
is_retry_ = true; | ||
attempt_count_++; | ||
Http::ConnectionPool::Instance* conn_pool = getConnPool(); | ||
if (!conn_pool) { | ||
sendNoHealthyUpstreamResponse(); | ||
cleanup(); | ||
return; | ||
} | ||
|
||
if (include_attempt_count_) { | ||
downstream_headers_->insertEnvoyAttemptCount().value(attempt_count_); | ||
} | ||
|
||
ASSERT(response_timeout_ || timeout_.global_timeout_.count() == 0); | ||
ASSERT(!upstream_request_); | ||
upstream_request_.reset(new UpstreamRequest(*this, *conn_pool)); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -109,6 +109,8 @@ TEST_P(IntegrationTest, RouterUpstreamResponseBeforeRequestComplete) { | |
|
||
TEST_P(IntegrationTest, Retry) { testRetry(); } | ||
|
||
TEST_P(IntegrationTest, RetryAttemptCount) { testRetryAttemptCountHeader(); } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we are lacking unit test on making sure the configuration is wired up correctly, probably in test/common/router/config_impl_test.cc. Less clear to me is if we should also have unit testing at the router level in test/common/router/router_test.cc |
||
|
||
TEST_P(IntegrationTest, RetryHostPredicateFilter) { testRetryHostPredicateFilter(); } | ||
|
||
TEST_P(IntegrationTest, RetryPriority) { testRetryPriority(); } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How this play with
suppress_envoy_headers
in router.proto?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now it doesn't interact with it at all, not sure if it would be more confusing to have two different flags affect whether it's enabled? My take on
suppress_envoy_headers
is that it's a way to disable headers that are injected by Envoy by default, although I can see how still gettingx-envoy-*
headers sent with that true (withinclude_request_attempt_count_header: true
) could be confusing. Thoughts?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine either way, perhaps document the behavior when both are true? @htuch any suggestion?