Commit 45c07e54 authored by Tarun Bansal's avatar Tarun Bansal Committed by Commit Bot

Throttle requests to H2 servers on slow connections only

This CL adds mechanism to throttle requests to H2 servers
similar to how requests to HTTP 1.1 are throttled. Note
that this CL does not add 6/host limit to H2 servers even when
throttling is in place.

Cq-Include-Trybots: master.tryserver.chromium.linux:linux_mojo
Change-Id: Ia9f86335b97031a459a1374695727f4efc8bcb3a
Bug: 836552
Reviewed-on: https://chromium-review.googlesource.com/912732Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Reviewed-by: default avatarRyan Sturm <ryansturm@chromium.org>
Commit-Queue: Tarun Bansal <tbansal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#560382}
parent b242799c
...@@ -23,5 +23,23 @@ const base::Feature kRendererSideResourceScheduler{ ...@@ -23,5 +23,23 @@ const base::Feature kRendererSideResourceScheduler{
const base::Feature kReporting{"Reporting", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kReporting{"Reporting", base::FEATURE_DISABLED_BY_DEFAULT};
// Based on the field trial parameters, this feature will override the value of
// the maximum number of delayable requests allowed in flight. The number of
// delayable requests allowed in flight will be based on the network's
// effective connection type ranges and the
// corresponding number of delayable requests in flight specified in the
// experiment configuration. Based on field trial parameters, this experiment
// may also throttle delayable requests based on the number of non-delayable
// requests in-flight times a weighting factor.
const base::Feature kThrottleDelayable{"ThrottleDelayable",
base::FEATURE_ENABLED_BY_DEFAULT};
// When kPriorityRequestsDelayableOnSlowConnections is enabled, HTTP
// requests fetched from a SPDY/QUIC/H2 proxies can be delayed by the
// ResourceScheduler just as HTTP/1.1 resources are. However, requests from such
// servers are not subject to kMaxNumDelayableRequestsPerHostPerClient limit.
const base::Feature kDelayRequestsOnMultiplexedConnections{
"DelayRequestsOnMultiplexedConnections", base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace features } // namespace features
} // namespace network } // namespace network
...@@ -21,6 +21,10 @@ COMPONENT_EXPORT(NETWORK_CPP) ...@@ -21,6 +21,10 @@ COMPONENT_EXPORT(NETWORK_CPP)
extern const base::Feature kRendererSideResourceScheduler; extern const base::Feature kRendererSideResourceScheduler;
COMPONENT_EXPORT(NETWORK_CPP) COMPONENT_EXPORT(NETWORK_CPP)
extern const base::Feature kReporting; extern const base::Feature kReporting;
COMPONENT_EXPORT(NETWORK_CPP)
extern const base::Feature kThrottleDelayable;
COMPONENT_EXPORT(NETWORK_CPP)
extern const base::Feature kDelayRequestsOnMultiplexedConnections;
} // namespace features } // namespace features
} // namespace network } // namespace network
......
...@@ -117,7 +117,8 @@ const char* RequestStartTriggerString(RequestStartTrigger trigger) { ...@@ -117,7 +117,8 @@ const char* RequestStartTriggerString(RequestStartTrigger trigger) {
} // namespace } // namespace
// The maximum number of requests to allow be in-flight at any point in time per // The maximum number of requests to allow be in-flight at any point in time per
// host. // host. This limit does not apply to hosts that support request prioritization
// when |delay_requests_on_multiplexed_connections| is true.
static const size_t kMaxNumDelayableRequestsPerHostPerClient = 6; static const size_t kMaxNumDelayableRequestsPerHostPerClient = 6;
// The maximum number of delayable requests to allow to be in-flight at any // The maximum number of delayable requests to allow to be in-flight at any
...@@ -409,6 +410,13 @@ class ResourceScheduler::Client { ...@@ -409,6 +410,13 @@ class ResourceScheduler::Client {
// so the shceduler works always with the normal mode. // so the shceduler works always with the normal mode.
deprecated_has_html_body_ = true; deprecated_has_html_body_ = true;
} }
// Must not run the conflicting experiments together.
DCHECK(!params_for_network_quality_
.delay_requests_on_multiplexed_connections ||
!resource_scheduler->priority_requests_delayable());
DCHECK(!params_for_network_quality_
.delay_requests_on_multiplexed_connections ||
!resource_scheduler->head_priority_requests_delayable());
} }
~Client() {} ~Client() {}
...@@ -679,7 +687,9 @@ class ResourceScheduler::Client { ...@@ -679,7 +687,9 @@ class ResourceScheduler::Client {
kDelayablePriorityThreshold) { kDelayablePriorityThreshold) {
if (resource_scheduler_->priority_requests_delayable() || if (resource_scheduler_->priority_requests_delayable() ||
(resource_scheduler_->head_priority_requests_delayable() && (resource_scheduler_->head_priority_requests_delayable() &&
!deprecated_has_html_body_)) { !deprecated_has_html_body_) ||
params_for_network_quality_
.delay_requests_on_multiplexed_connections) {
// Resources below the delayable priority threshold that are considered // Resources below the delayable priority threshold that are considered
// delayable. // delayable.
attributes |= kAttributeDelayable; attributes |= kAttributeDelayable;
...@@ -699,7 +709,28 @@ class ResourceScheduler::Client { ...@@ -699,7 +709,28 @@ class ResourceScheduler::Client {
} }
bool ReachedMaxRequestsPerHostPerClient( bool ReachedMaxRequestsPerHostPerClient(
const net::HostPortPair& active_request_host) const { const net::HostPortPair& active_request_host,
bool supports_priority) const {
// This method should not be called for requests to origins that support
// prioritization (aka multiplexing) unless one of the experiments to
// throttle priority requests is enabled.
DCHECK(
!supports_priority ||
params_for_network_quality_.delay_requests_on_multiplexed_connections ||
resource_scheduler_->priority_requests_delayable() ||
resource_scheduler_->head_priority_requests_delayable());
// kMaxNumDelayableRequestsPerHostPerClient limit does not apply to servers
// that support request priorities when
// |delay_requests_on_multiplexed_connections| is true. If
// |delay_requests_on_multiplexed_connections| is false, then
// kMaxNumDelayableRequestsPerHostPerClient limit still applies to other
// experiments that delay priority requests.
if (supports_priority &&
params_for_network_quality_.delay_requests_on_multiplexed_connections) {
return false;
}
size_t same_host_count = 0; size_t same_host_count = 0;
for (RequestSet::const_iterator it = in_flight_requests_.begin(); for (RequestSet::const_iterator it = in_flight_requests_.begin();
it != in_flight_requests_.end(); ++it) { it != in_flight_requests_.end(); ++it) {
...@@ -804,20 +835,22 @@ class ResourceScheduler::Client { ...@@ -804,20 +835,22 @@ class ResourceScheduler::Client {
bool priority_delayable = bool priority_delayable =
resource_scheduler_->priority_requests_delayable() || resource_scheduler_->priority_requests_delayable() ||
(resource_scheduler_->head_priority_requests_delayable() && (resource_scheduler_->head_priority_requests_delayable() &&
!deprecated_has_html_body_); !deprecated_has_html_body_) ||
params_for_network_quality_.delay_requests_on_multiplexed_connections;
url::SchemeHostPort scheme_host_port(url_request.url());
bool supports_priority = url_request.context()
->http_server_properties()
->SupportsRequestPriority(scheme_host_port);
if (!priority_delayable) { if (!priority_delayable) {
if (using_spdy_proxy_ && url_request.url().SchemeIs(url::kHttpScheme)) if (using_spdy_proxy_ && url_request.url().SchemeIs(url::kHttpScheme))
return ShouldStartOrYieldRequest(request); return ShouldStartOrYieldRequest(request);
url::SchemeHostPort scheme_host_port(url_request.url());
net::HttpServerProperties& http_server_properties =
*url_request.context()->http_server_properties();
// TODO(willchan): We should really improve this algorithm as described in // TODO(willchan): We should really improve this algorithm as described in
// https://crbug.com/164101. Also, theoretically we should not count a // https://crbug.com/164101. Also, theoretically we should not count a
// request-priority capable request against the delayable requests limit. // request-priority capable request against the delayable requests limit.
if (http_server_properties.SupportsRequestPriority(scheme_host_port)) if (supports_priority)
return ShouldStartOrYieldRequest(request); return ShouldStartOrYieldRequest(request);
} }
...@@ -835,7 +868,7 @@ class ResourceScheduler::Client { ...@@ -835,7 +868,7 @@ class ResourceScheduler::Client {
return DO_NOT_START_REQUEST_AND_STOP_SEARCHING; return DO_NOT_START_REQUEST_AND_STOP_SEARCHING;
} }
if (ReachedMaxRequestsPerHostPerClient(host_port_pair)) { if (ReachedMaxRequestsPerHostPerClient(host_port_pair, supports_priority)) {
// There may be other requests for other hosts that may be allowed, // There may be other requests for other hosts that may be allowed,
// so keep checking. // so keep checking.
return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING; return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/optional.h" #include "base/optional.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "net/nqe/network_quality_estimator.h" #include "net/nqe/network_quality_estimator.h"
#include "services/network/public/cpp/features.h"
namespace { namespace {
...@@ -17,33 +18,30 @@ namespace { ...@@ -17,33 +18,30 @@ namespace {
// point in time (across all hosts). // point in time (across all hosts).
static const size_t kDefaultMaxNumDelayableRequestsPerClient = 10; static const size_t kDefaultMaxNumDelayableRequestsPerClient = 10;
// Based on the field trial parameters, this feature will override the value of
// the maximum number of delayable requests allowed in flight. The number of
// delayable requests allowed in flight will be based on the network's
// effective connection type ranges and the
// corresponding number of delayable requests in flight specified in the
// experiment configuration. Based on field trial parameters, this experiment
// may also throttle delayable requests based on the number of non-delayable
// requests in-flight times a weighting factor.
const base::Feature kThrottleDelayable{"ThrottleDelayable",
base::FEATURE_ENABLED_BY_DEFAULT};
} // namespace } // namespace
namespace network { namespace network {
ResourceSchedulerParamsManager::ParamsForNetworkQuality:: ResourceSchedulerParamsManager::ParamsForNetworkQuality::
ParamsForNetworkQuality() ParamsForNetworkQuality()
: max_delayable_requests(kDefaultMaxNumDelayableRequestsPerClient), : ResourceSchedulerParamsManager::ParamsForNetworkQuality(
non_delayable_weight(0.0) {} kDefaultMaxNumDelayableRequestsPerClient,
0.0,
false) {}
ResourceSchedulerParamsManager::ParamsForNetworkQuality:: ResourceSchedulerParamsManager::ParamsForNetworkQuality::
ParamsForNetworkQuality(size_t max_delayable_requests, ParamsForNetworkQuality(size_t max_delayable_requests,
double non_delayable_weight) double non_delayable_weight,
bool delay_requests_on_multiplexed_connections)
: max_delayable_requests(max_delayable_requests), : max_delayable_requests(max_delayable_requests),
non_delayable_weight(non_delayable_weight) {} non_delayable_weight(non_delayable_weight),
delay_requests_on_multiplexed_connections(
delay_requests_on_multiplexed_connections) {}
ResourceSchedulerParamsManager::ResourceSchedulerParamsManager() ResourceSchedulerParamsManager::ResourceSchedulerParamsManager()
: ResourceSchedulerParamsManager(GetParamsForNetworkQualityContainer()) {} : ResourceSchedulerParamsManager(
GetParamsForDelayRequestsOnMultiplexedConnections(
GetParamsForNetworkQualityContainer())) {}
ResourceSchedulerParamsManager::ResourceSchedulerParamsManager( ResourceSchedulerParamsManager::ResourceSchedulerParamsManager(
const ParamsForNetworkQualityContainer& const ParamsForNetworkQualityContainer&
...@@ -68,7 +66,46 @@ ResourceSchedulerParamsManager::GetParamsForEffectiveConnectionType( ...@@ -68,7 +66,46 @@ ResourceSchedulerParamsManager::GetParamsForEffectiveConnectionType(
params_for_network_quality_container_.find(effective_connection_type); params_for_network_quality_container_.find(effective_connection_type);
if (iter != params_for_network_quality_container_.end()) if (iter != params_for_network_quality_container_.end())
return iter->second; return iter->second;
return ParamsForNetworkQuality(kDefaultMaxNumDelayableRequestsPerClient, 0.0); return ParamsForNetworkQuality(kDefaultMaxNumDelayableRequestsPerClient, 0.0,
false);
}
// static
ResourceSchedulerParamsManager::ParamsForNetworkQualityContainer
ResourceSchedulerParamsManager::
GetParamsForDelayRequestsOnMultiplexedConnections(
ResourceSchedulerParamsManager::ParamsForNetworkQualityContainer
result) {
if (!base::FeatureList::IsEnabled(
features::kDelayRequestsOnMultiplexedConnections)) {
return result;
}
base::Optional<net::EffectiveConnectionType> max_effective_connection_type =
net::GetEffectiveConnectionTypeForName(
base::GetFieldTrialParamValueByFeature(
features::kDelayRequestsOnMultiplexedConnections,
"MaxEffectiveConnectionType"));
if (!max_effective_connection_type)
return result;
for (int ect = net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G;
ect <= max_effective_connection_type.value(); ++ect) {
net::EffectiveConnectionType effective_connection_type =
static_cast<net::EffectiveConnectionType>(ect);
ParamsForNetworkQualityContainer::iterator iter =
result.find(effective_connection_type);
if (iter != result.end()) {
iter->second.delay_requests_on_multiplexed_connections = true;
} else {
result.emplace(std::make_pair(
effective_connection_type,
ParamsForNetworkQuality(kDefaultMaxNumDelayableRequestsPerClient, 0.0,
true)));
}
}
return result;
} }
// static // static
...@@ -82,31 +119,32 @@ ResourceSchedulerParamsManager::GetParamsForNetworkQualityContainer() { ...@@ -82,31 +119,32 @@ ResourceSchedulerParamsManager::GetParamsForNetworkQualityContainer() {
// Set the default params for networks with ECT Slow2G and 2G. These params // Set the default params for networks with ECT Slow2G and 2G. These params
// can still be overridden using the field trial. // can still be overridden using the field trial.
result.emplace(std::make_pair(net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G, result.emplace(std::make_pair(net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G,
ParamsForNetworkQuality(8, 3.0))); ParamsForNetworkQuality(8, 3.0, false)));
result.emplace(std::make_pair(net::EFFECTIVE_CONNECTION_TYPE_2G, result.emplace(std::make_pair(net::EFFECTIVE_CONNECTION_TYPE_2G,
ParamsForNetworkQuality(8, 3.0))); ParamsForNetworkQuality(8, 3.0, false)));
for (int config_param_index = 1; config_param_index <= 20; for (int config_param_index = 1; config_param_index <= 20;
++config_param_index) { ++config_param_index) {
size_t max_delayable_requests; size_t max_delayable_requests;
if (!base::StringToSizeT( if (!base::StringToSizeT(base::GetFieldTrialParamValueByFeature(
base::GetFieldTrialParamValueByFeature( features::kThrottleDelayable,
kThrottleDelayable, kMaxDelayableRequestsBase + kMaxDelayableRequestsBase +
base::IntToString(config_param_index)), base::IntToString(config_param_index)),
&max_delayable_requests)) { &max_delayable_requests)) {
return result; return result;
} }
base::Optional<net::EffectiveConnectionType> effective_connection_type = base::Optional<net::EffectiveConnectionType> effective_connection_type =
net::GetEffectiveConnectionTypeForName( net::GetEffectiveConnectionTypeForName(
base::GetFieldTrialParamValueByFeature( base::GetFieldTrialParamValueByFeature(
kThrottleDelayable, kEffectiveConnectionTypeBase + features::kThrottleDelayable,
base::IntToString(config_param_index))); kEffectiveConnectionTypeBase +
base::IntToString(config_param_index)));
DCHECK(effective_connection_type.has_value()); DCHECK(effective_connection_type.has_value());
double non_delayable_weight = base::GetFieldTrialParamByFeatureAsDouble( double non_delayable_weight = base::GetFieldTrialParamByFeatureAsDouble(
kThrottleDelayable, features::kThrottleDelayable,
kNonDelayableWeightBase + base::IntToString(config_param_index), 0.0); kNonDelayableWeightBase + base::IntToString(config_param_index), 0.0);
// Check if the entry is already present. This will happen if the default // Check if the entry is already present. This will happen if the default
...@@ -120,7 +158,7 @@ ResourceSchedulerParamsManager::GetParamsForNetworkQualityContainer() { ...@@ -120,7 +158,7 @@ ResourceSchedulerParamsManager::GetParamsForNetworkQualityContainer() {
result.emplace( result.emplace(
std::make_pair(effective_connection_type.value(), std::make_pair(effective_connection_type.value(),
ParamsForNetworkQuality(max_delayable_requests, ParamsForNetworkQuality(max_delayable_requests,
non_delayable_weight))); non_delayable_weight, false)));
} }
} }
// There should not have been more than 20 params indices specified. // There should not have been more than 20 params indices specified.
......
...@@ -26,7 +26,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ResourceSchedulerParamsManager { ...@@ -26,7 +26,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ResourceSchedulerParamsManager {
ParamsForNetworkQuality(); ParamsForNetworkQuality();
ParamsForNetworkQuality(size_t max_delayable_requests, ParamsForNetworkQuality(size_t max_delayable_requests,
double non_delayable_weight); double non_delayable_weight,
bool delay_requests_on_multiplexed_connections);
// The maximum number of delayable requests allowed. // The maximum number of delayable requests allowed.
size_t max_delayable_requests; size_t max_delayable_requests;
...@@ -34,6 +35,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ResourceSchedulerParamsManager { ...@@ -34,6 +35,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ResourceSchedulerParamsManager {
// The weight of a non-delayable request when counting the effective number // The weight of a non-delayable request when counting the effective number
// of non-delayable requests in-flight. // of non-delayable requests in-flight.
double non_delayable_weight; double non_delayable_weight;
// True if requests to servers that support prioritization (e.g.,
// H2/SPDY/QUIC) should be delayed similar to other HTTP 1.1 requests.
bool delay_requests_on_multiplexed_connections;
}; };
ResourceSchedulerParamsManager(); ResourceSchedulerParamsManager();
...@@ -58,6 +63,13 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ResourceSchedulerParamsManager { ...@@ -58,6 +63,13 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ResourceSchedulerParamsManager {
net::EffectiveConnectionType effective_connection_type) const; net::EffectiveConnectionType effective_connection_type) const;
private: private:
// Reads the experiments params for DelayRequestsOnMultiplexedConnections
// finch experiment, modifies |result| based on the experiment params, and
// returns the modified |result|.
static ParamsForNetworkQualityContainer
GetParamsForDelayRequestsOnMultiplexedConnections(
ParamsForNetworkQualityContainer result);
// Reads experiment parameters and populates // Reads experiment parameters and populates
// |params_for_network_quality_container_|. It looks for configuration // |params_for_network_quality_container_|. It looks for configuration
// parameters with sequential numeric suffixes, and stops looking after the // parameters with sequential numeric suffixes, and stops looking after the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment