Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
23 changes: 23 additions & 0 deletions envoy/stream_info/stream_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,22 @@ struct UpstreamTiming {
absl::optional<MonotonicTime> last_upstream_rx_byte_received_;
};

class DownstreamTiming {
public:
void setValue(absl::string_view key, MonotonicTime value) { timings_[key] = value; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we assert that we don't set a value twice? Not sure.

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.

yeah I didn't know if folks using this would want to do updates, so left without.


absl::optional<MonotonicTime> getValue(absl::string_view value) const {
auto ret = timings_.find(value);
if (ret == timings_.end()) {
return {};
}
return ret->second;
}

private:
absl::flat_hash_map<std::string, MonotonicTime> timings_;
};

// Measure the number of bytes sent and received for a stream.
struct BytesMeter {
uint64_t wireBytesSent() const { return wire_bytes_sent_; }
Expand Down Expand Up @@ -406,6 +422,8 @@ class StreamInfo {
*/
virtual absl::optional<std::chrono::nanoseconds> firstDownstreamTxByteSent() const PURE;

// TODO(alyssawilk) move the downstream timing calls into DownstreamTiming in
// a follow-up.
/**
* Sets the time when the first byte of the response is sent downstream.
*/
Expand Down Expand Up @@ -434,6 +452,11 @@ class StreamInfo {
*/
virtual void onRequestComplete() PURE;

/**
* @return the downstream timing information.
*/
virtual DownstreamTiming& downstreamTiming() PURE;

/**
* @param bytes_sent denotes the number of bytes to add to total sent bytes.
*/
Expand Down
8 changes: 8 additions & 0 deletions source/common/stream_info/stream_info_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@ struct StreamInfoImpl : public StreamInfo {
final_time_ = time_source_.monotonicTime();
}

DownstreamTiming& downstreamTiming() override {
if (!downstream_timing_.has_value()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why have downstream_timing_ as optional? Seems like we could just have the struct if we are constructing on absence?

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.

I could go either way here, but I don't think we set these for TCP, and TCP (so redis etc.) have stream info, so I was largely trying to spare them the memory footprint.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ah gotcha!

downstream_timing_ = DownstreamTiming();
}
return downstream_timing_.value();
}

void addBytesReceived(uint64_t bytes_received) override { bytes_received_ += bytes_received; }

uint64_t bytesReceived() const override { return bytes_received_; }
Expand Down Expand Up @@ -360,6 +367,7 @@ struct StreamInfoImpl : public StreamInfo {
std::string requested_server_name_;
const Http::RequestHeaderMap* request_headers_{};
Http::RequestIdStreamInfoProviderSharedPtr request_id_provider_;
absl::optional<DownstreamTiming> downstream_timing_;
UpstreamTiming upstream_timing_;
std::string upstream_transport_failure_reason_;
absl::optional<Upstream::ClusterInfoConstSharedPtr> upstream_cluster_info_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace DynamicForwardProxy {
namespace {

void latchTime(Http::StreamDecoderFilterCallbacks* decoder_callbacks, const std::string& key) {
StreamInfo::DownstreamTiming& downstream_timing =
decoder_callbacks->streamInfo().downstreamTiming();
downstream_timing.setValue(key, decoder_callbacks->dispatcher().timeSource().monotonicTime());
}

} // namespace
struct ResponseStringValues {
const std::string DnsCacheOverflow = "DNS cache overflow";
const std::string PendingRequestOverflow = "Dynamic forward proxy pending request overflow";
Expand Down Expand Up @@ -46,6 +54,8 @@ void ProxyFilter::onDestroy() {
}

Http::FilterHeadersStatus ProxyFilter::decodeHeaders(Http::RequestHeaderMap& headers, bool) {
latchTime(decoder_callbacks_, dnsStart());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm curious about latching the time here vs. latching at other points in this function. For instance, only if the filter gets to loadDnsCacheEntry?

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.

yeah it's the difference between when we start the lookup and when we start doing "DNS work"
I can go either way - moved.


Router::RouteConstSharedPtr route = decoder_callbacks_->route();
const Router::RouteEntry* route_entry;
if (!route || !(route_entry = route->routeEntry())) {
Expand Down Expand Up @@ -132,6 +142,7 @@ Http::FilterHeadersStatus ProxyFilter::decodeHeaders(Http::RequestHeaderMap& hea
addHostAddressToFilterState(host.value()->address());
}

latchTime(decoder_callbacks_, dnsEnd());
return Http::FilterHeadersStatus::Continue;
}
case LoadDnsCacheEntryStatus::Loading:
Expand Down Expand Up @@ -183,6 +194,7 @@ void ProxyFilter::onLoadDnsCacheComplete(
const Common::DynamicForwardProxy::DnsHostInfoSharedPtr& host_info) {
ENVOY_STREAM_LOG(debug, "load DNS cache complete, continuing after adding resolved host: {}",
*decoder_callbacks_, host_info->resolvedHost());
latchTime(decoder_callbacks_, dnsEnd());
ASSERT(circuit_breaker_ != nullptr);
circuit_breaker_.reset();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ class ProxyFilter
public:
ProxyFilter(const ProxyFilterConfigSharedPtr& config) : config_(config) {}

static const std::string& dnsStart() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: I think you can just do static constexpr absl::string_view = ... for these.

CONSTRUCT_ON_FIRST_USE(std::string, "envoy.dynamic_forward_proxy.dns_start_ms");
}
static const std::string& dnsEnd() {
CONSTRUCT_ON_FIRST_USE(std::string, "envoy.dynamic_forward_proxy.dns_end_ms");
}

// Http::PassThroughDecoderFilter
Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers,
bool end_stream) override;
Expand Down
3 changes: 3 additions & 0 deletions test/common/stream_info/test_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ class TestStreamInfo : public StreamInfo::StreamInfo {

void onRequestComplete() override { end_time_ = timeSystem().monotonicTime(); }

Envoy::StreamInfo::DownstreamTiming& downstreamTiming() override { return downstream_timing_; }

void setUpstreamTiming(const Envoy::StreamInfo::UpstreamTiming& upstream_timing) override {
upstream_timing_ = upstream_timing;
}
Expand Down Expand Up @@ -241,6 +243,7 @@ class TestStreamInfo : public StreamInfo::StreamInfo {
SystemTime start_time_;
MonotonicTime start_time_monotonic_;

Envoy::StreamInfo::DownstreamTiming downstream_timing_;
absl::optional<MonotonicTime> last_rx_byte_received_;
absl::optional<MonotonicTime> first_upstream_tx_byte_sent_;
absl::optional<MonotonicTime> last_upstream_tx_byte_sent_;
Expand Down
1 change: 1 addition & 0 deletions test/extensions/filters/http/dynamic_forward_proxy/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ envoy_extension_cc_test(
"//source/extensions/filters/http/dynamic_forward_proxy:config",
"//source/extensions/key_value/file_based:config_lib",
"//test/integration:http_integration_lib",
"//test/integration/filters:stream_info_to_headers_filter_lib",
"@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto",
"@envoy_api//envoy/config/cluster/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ name: dynamic_forward_proxy
max_hosts, max_pending_requests, filename);
config_helper_.prependFilter(filter);

config_helper_.prependFilter(fmt::format(R"EOF(
name: stream-info-to-headers-filter
typed_config:
"@type": type.googleapis.com/google.protobuf.Empty)EOF"));
config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) {
// Switch predefined cluster_0 to CDS filesystem sourcing.
bootstrap.mutable_dynamic_resources()->mutable_cds_config()->set_resource_api_version(
Expand Down Expand Up @@ -174,12 +178,18 @@ TEST_P(ProxyFilterIntegrationTest, RequestWithBody) {
checkSimpleRequestSuccess(1024, 1024, response.get());
EXPECT_EQ(1, test_server_->counter("dns_cache.foo.dns_query_attempt")->value());
EXPECT_EQ(1, test_server_->counter("dns_cache.foo.host_added")->value());
// Make sure dns timings are tracked for cache-misses.
ASSERT_FALSE(response->headers().get(Http::LowerCaseString("dns_start")).empty());
ASSERT_FALSE(response->headers().get(Http::LowerCaseString("dns_end")).empty());

// Now send another request. This should hit the DNS cache.
response = sendRequestAndWaitForResponse(request_headers, 512, default_response_headers_, 512);
checkSimpleRequestSuccess(512, 512, response.get());
EXPECT_EQ(1, test_server_->counter("dns_cache.foo.dns_query_attempt")->value());
EXPECT_EQ(1, test_server_->counter("dns_cache.foo.host_added")->value());
// Make sure dns timings are tracked for cache-hits.
ASSERT_FALSE(response->headers().get(Http::LowerCaseString("dns_start")).empty());
ASSERT_FALSE(response->headers().get(Http::LowerCaseString("dns_end")).empty());
}

// Currently if the first DNS resolution fails, the filter will continue with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,14 @@ TEST_F(ProxyFilterTest, HttpDefaultPort) {
new Upstream::ResourceAutoIncDec(pending_requests_));
InSequence s;

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_())
.WillOnce(Return(circuit_breakers_));
EXPECT_CALL(*transport_socket_factory_, implementsSecureTransport()).WillOnce(Return(false));

Extensions::Common::DynamicForwardProxy::MockLoadDnsCacheEntryHandle* handle =
new Extensions::Common::DynamicForwardProxy::MockLoadDnsCacheEntryHandle();
EXPECT_CALL(*dns_cache_manager_->dns_cache_, loadDnsCacheEntry_(Eq("foo"), 80, _))
Expand All @@ -121,6 +124,8 @@ TEST_F(ProxyFilterTest, HttpsDefaultPort) {
new Upstream::ResourceAutoIncDec(pending_requests_));
InSequence s;

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_())
Expand All @@ -144,6 +149,8 @@ TEST_F(ProxyFilterTest, CacheOverflow) {
new Upstream::ResourceAutoIncDec(pending_requests_));
InSequence s;

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_())
Expand All @@ -168,6 +175,8 @@ TEST_F(ProxyFilterTest, CircuitBreakerOverflow) {
new Upstream::ResourceAutoIncDec(pending_requests_));
InSequence s;

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_())
Expand All @@ -184,6 +193,8 @@ TEST_F(ProxyFilterTest, CircuitBreakerOverflow) {
// Create a second filter for a 2nd request.
auto filter2 = std::make_unique<ProxyFilter>(filter_config_);
filter2->setDecoderFilterCallbacks(callbacks_);
EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_());
Expand All @@ -206,6 +217,8 @@ TEST_F(ProxyFilterTest, CircuitBreakerOverflowWithDnsCacheResourceManager) {
new Upstream::ResourceAutoIncDec(pending_requests_));
InSequence s;

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_())
Expand All @@ -222,6 +235,8 @@ TEST_F(ProxyFilterTest, CircuitBreakerOverflowWithDnsCacheResourceManager) {
// Create a second filter for a 2nd request.
auto filter2 = std::make_unique<ProxyFilter>(filter_config_);
filter2->setDecoderFilterCallbacks(callbacks_);
EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_());
Expand All @@ -245,6 +260,8 @@ TEST_F(ProxyFilterTest, CircuitBreakerOverflowWithDnsCacheResourceManager) {
TEST_F(ProxyFilterTest, NoRoute) {
InSequence s;

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route()).WillOnce(Return(nullptr));
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false));
}
Expand All @@ -253,6 +270,8 @@ TEST_F(ProxyFilterTest, NoRoute) {
TEST_F(ProxyFilterTest, NoCluster) {
InSequence s;

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_)).WillOnce(Return(nullptr));
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false));
Expand All @@ -264,6 +283,8 @@ TEST_F(ProxyFilterTest, NoClusterType) {

InSequence s;

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false));
Expand All @@ -277,6 +298,8 @@ TEST_F(ProxyFilterTest, NonDynamicForwardProxy) {

InSequence s;

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false));
Expand All @@ -291,6 +314,8 @@ TEST_F(ProxyFilterTest, HostRewrite) {
proto_config.set_host_rewrite_literal("bar");
ProxyPerRouteConfig config(proto_config);

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_())
Expand Down Expand Up @@ -320,6 +345,8 @@ TEST_F(ProxyFilterTest, HostRewriteViaHeader) {
proto_config.set_host_rewrite_header("x-set-header");
ProxyPerRouteConfig config(proto_config);

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_())
Expand Down Expand Up @@ -372,6 +399,8 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, AddResolvedHostFilterStateMetadata
auto host_info = std::make_shared<Extensions::Common::DynamicForwardProxy::MockDnsHostInfo>();
host_info->address_ = Network::Utility::parseInternetAddress("1.2.3.4", 80);

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_())
Expand All @@ -393,6 +422,8 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, AddResolvedHostFilterStateMetadata
EXPECT_CALL(*host_info, address());

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());

// Host was resolved successfully, so continue filter iteration.
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false));
Expand All @@ -410,7 +441,7 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, UpdateResolvedHostFilterStateMetad
Upstream::ResourceAutoIncDec* circuit_breakers_(
new Upstream::ResourceAutoIncDec(pending_requests_));

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, streamInfo()).Times(testing::AnyNumber());

// Pre-populate the filter state with an address.
auto& filter_state = callbacks_.streamInfo().filterState();
Expand All @@ -427,6 +458,8 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, UpdateResolvedHostFilterStateMetad
auto host_info = std::make_shared<Extensions::Common::DynamicForwardProxy::MockDnsHostInfo>();
host_info->address_ = Network::Utility::parseInternetAddress("1.2.3.4", 80);

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_())
Expand All @@ -448,13 +481,17 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, UpdateResolvedHostFilterStateMetad
EXPECT_CALL(*host_info, address());

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());

// Host was resolved successfully, so continue filter iteration.
EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false));

// We expect FilterState to be populated
// We expect FilterState and resolution times to be populated
EXPECT_TRUE(
filter_state->hasData<StreamInfo::UpstreamAddress>(StreamInfo::UpstreamAddress::key()));
callbacks_.streamInfo().downstreamTiming().getValue(ProxyFilter::dnsStart()).has_value());
EXPECT_TRUE(
callbacks_.streamInfo().downstreamTiming().getValue(ProxyFilter::dnsEnd()).has_value());

const StreamInfo::UpstreamAddress& updated_address_obj =
filter_state->getDataReadOnly<StreamInfo::UpstreamAddress>(
Expand Down Expand Up @@ -482,6 +519,8 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, IgnoreFilterStateMetadataNullAddre
auto host_info = std::make_shared<Extensions::Common::DynamicForwardProxy::MockDnsHostInfo>();
host_info->address_ = nullptr;

EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());
EXPECT_CALL(callbacks_, route());
EXPECT_CALL(cm_, getThreadLocalCluster(_));
EXPECT_CALL(*dns_cache_manager_->dns_cache_, canCreateDnsRequest_())
Expand All @@ -501,6 +540,8 @@ TEST_F(UpstreamResolvedHostFilterStateHelper, IgnoreFilterStateMetadataNullAddre
}));

EXPECT_CALL(*host_info, address());
EXPECT_CALL(callbacks_, streamInfo());
EXPECT_CALL(callbacks_, dispatcher());

EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false));

Expand Down
Loading