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{
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 network
......@@ -21,6 +21,10 @@ COMPONENT_EXPORT(NETWORK_CPP)
extern const base::Feature kRendererSideResourceScheduler;
COMPONENT_EXPORT(NETWORK_CPP)
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 network
......
......@@ -117,7 +117,8 @@ const char* RequestStartTriggerString(RequestStartTrigger trigger) {
} // namespace
// 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;
// The maximum number of delayable requests to allow to be in-flight at any
......@@ -409,6 +410,13 @@ class ResourceScheduler::Client {
// so the shceduler works always with the normal mode.
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() {}
......@@ -679,7 +687,9 @@ class ResourceScheduler::Client {
kDelayablePriorityThreshold) {
if (resource_scheduler_->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
// delayable.
attributes |= kAttributeDelayable;
......@@ -699,7 +709,28 @@ class ResourceScheduler::Client {
}
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;
for (RequestSet::const_iterator it = in_flight_requests_.begin();
it != in_flight_requests_.end(); ++it) {
......@@ -804,20 +835,22 @@ class ResourceScheduler::Client {
bool priority_delayable =
resource_scheduler_->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 (using_spdy_proxy_ && url_request.url().SchemeIs(url::kHttpScheme))
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
// https://crbug.com/164101. Also, theoretically we should not count a
// request-priority capable request against the delayable requests limit.
if (http_server_properties.SupportsRequestPriority(scheme_host_port))
if (supports_priority)
return ShouldStartOrYieldRequest(request);
}
......@@ -835,7 +868,7 @@ class ResourceScheduler::Client {
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,
// so keep checking.
return DO_NOT_START_REQUEST_AND_KEEP_SEARCHING;
......
......@@ -10,6 +10,7 @@
#include "base/optional.h"
#include "base/strings/string_number_conversions.h"
#include "net/nqe/network_quality_estimator.h"
#include "services/network/public/cpp/features.h"
namespace {
......@@ -17,33 +18,30 @@ namespace {
// point in time (across all hosts).
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 network {
ResourceSchedulerParamsManager::ParamsForNetworkQuality::
ParamsForNetworkQuality()
: max_delayable_requests(kDefaultMaxNumDelayableRequestsPerClient),
non_delayable_weight(0.0) {}
: ResourceSchedulerParamsManager::ParamsForNetworkQuality(
kDefaultMaxNumDelayableRequestsPerClient,
0.0,
false) {}
ResourceSchedulerParamsManager::ParamsForNetworkQuality::
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),
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(GetParamsForNetworkQualityContainer()) {}
: ResourceSchedulerParamsManager(
GetParamsForDelayRequestsOnMultiplexedConnections(
GetParamsForNetworkQualityContainer())) {}
ResourceSchedulerParamsManager::ResourceSchedulerParamsManager(
const ParamsForNetworkQualityContainer&
......@@ -68,7 +66,46 @@ ResourceSchedulerParamsManager::GetParamsForEffectiveConnectionType(
params_for_network_quality_container_.find(effective_connection_type);
if (iter != params_for_network_quality_container_.end())
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
......@@ -82,31 +119,32 @@ ResourceSchedulerParamsManager::GetParamsForNetworkQualityContainer() {
// Set the default params for networks with ECT Slow2G and 2G. These params
// can still be overridden using the field trial.
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,
ParamsForNetworkQuality(8, 3.0)));
ParamsForNetworkQuality(8, 3.0, false)));
for (int config_param_index = 1; config_param_index <= 20;
++config_param_index) {
size_t max_delayable_requests;
if (!base::StringToSizeT(
base::GetFieldTrialParamValueByFeature(
kThrottleDelayable, kMaxDelayableRequestsBase +
base::IntToString(config_param_index)),
&max_delayable_requests)) {
if (!base::StringToSizeT(base::GetFieldTrialParamValueByFeature(
features::kThrottleDelayable,
kMaxDelayableRequestsBase +
base::IntToString(config_param_index)),
&max_delayable_requests)) {
return result;
}
base::Optional<net::EffectiveConnectionType> effective_connection_type =
net::GetEffectiveConnectionTypeForName(
base::GetFieldTrialParamValueByFeature(
kThrottleDelayable, kEffectiveConnectionTypeBase +
base::IntToString(config_param_index)));
features::kThrottleDelayable,
kEffectiveConnectionTypeBase +
base::IntToString(config_param_index)));
DCHECK(effective_connection_type.has_value());
double non_delayable_weight = base::GetFieldTrialParamByFeatureAsDouble(
kThrottleDelayable,
features::kThrottleDelayable,
kNonDelayableWeightBase + base::IntToString(config_param_index), 0.0);
// Check if the entry is already present. This will happen if the default
......@@ -120,7 +158,7 @@ ResourceSchedulerParamsManager::GetParamsForNetworkQualityContainer() {
result.emplace(
std::make_pair(effective_connection_type.value(),
ParamsForNetworkQuality(max_delayable_requests,
non_delayable_weight)));
non_delayable_weight, false)));
}
}
// There should not have been more than 20 params indices specified.
......
......@@ -26,7 +26,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ResourceSchedulerParamsManager {
ParamsForNetworkQuality();
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.
size_t max_delayable_requests;
......@@ -34,6 +35,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ResourceSchedulerParamsManager {
// The weight of a non-delayable request when counting the effective number
// of non-delayable requests in-flight.
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();
......@@ -58,6 +63,13 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ResourceSchedulerParamsManager {
net::EffectiveConnectionType effective_connection_type) const;
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
// |params_for_network_quality_container_|. It looks for configuration
// 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