blob: c6614b708b8d98aba71181f8756b7638fc93bbc1 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/network/resource_scheduler_params_manager.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/optional.h"
#include "base/strings/string_number_conversions.h"
#include "net/nqe/network_quality_estimator.h"
#include "net/nqe/network_quality_estimator_params.h"
#include "services/network/public/cpp/features.h"
namespace network {
namespace {
// The maximum number of delayable requests to allow to be in-flight at any
// point in time (across all hosts).
constexpr size_t kDefaultMaxNumDelayableRequestsPerClient = 10;
// Value by which HTTP RTT estimate is multiplied to get the maximum queuing
// duration.
constexpr int kHttpRttMultiplierForQueuingDuration = 30;
// Reads experiment parameters and returns them.
ResourceSchedulerParamsManager::ParamsForNetworkQualityContainer
GetParamsForNetworkQualityContainer() {
// Look for configuration parameters with sequential numeric suffixes, and
// stop looking after the first failure to find an experimetal parameter.
// A sample configuration is given below:
// "EffectiveConnectionType1": "Slow-2G",
// "MaxDelayableRequests1": "6",
// "NonDelayableWeight1": "2.0",
// "EffectiveConnectionType2": "3G",
// "MaxDelayableRequests2": "12",
// "NonDelayableWeight2": "3.0",
// This config implies that when Effective Connection Type (ECT) is Slow-2G,
// then the maximum number of non-delayable requests should be
// limited to 6, and the non-delayable request weight should be set to 2.
// When ECT is 3G, it should be limited to 12. For all other values of ECT,
// the default values are used.
static const char kMaxDelayableRequestsBase[] = "MaxDelayableRequests";
static const char kEffectiveConnectionTypeBase[] = "EffectiveConnectionType";
static const char kNonDelayableWeightBase[] = "NonDelayableWeight";
static constexpr base::TimeDelta kUpperBoundQueuingDuration =
base::TimeDelta::FromSeconds(120);
static constexpr base::TimeDelta kLowerBoundQueuingDuration =
base::TimeDelta::FromSeconds(15);
ResourceSchedulerParamsManager::ParamsForNetworkQualityContainer result;
// 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,
ResourceSchedulerParamsManager::ParamsForNetworkQuality(
8, 3.0, true /* delay_requests_on_multiplexed_connections */,
base::nullopt)));
result.emplace(std::make_pair(
net::EFFECTIVE_CONNECTION_TYPE_2G,
ResourceSchedulerParamsManager::ParamsForNetworkQuality(
8, 3.0, true /* delay_requests_on_multiplexed_connections */,
base::nullopt)));
result.emplace(
std::make_pair(net::EFFECTIVE_CONNECTION_TYPE_3G,
ResourceSchedulerParamsManager::ParamsForNetworkQuality(
kDefaultMaxNumDelayableRequestsPerClient, 0.0,
true /* delay_requests_on_multiplexed_connections */,
base::nullopt)));
for (int config_param_index = 1; config_param_index <= 20;
++config_param_index) {
size_t max_delayable_requests;
if (!base::StringToSizeT(base::GetFieldTrialParamValueByFeature(
features::kThrottleDelayable,
kMaxDelayableRequestsBase +
base::NumberToString(config_param_index)),
&max_delayable_requests)) {
break;
}
base::Optional<net::EffectiveConnectionType> effective_connection_type =
net::GetEffectiveConnectionTypeForName(
base::GetFieldTrialParamValueByFeature(
features::kThrottleDelayable,
kEffectiveConnectionTypeBase +
base::NumberToString(config_param_index)));
DCHECK(effective_connection_type.has_value());
double non_delayable_weight = base::GetFieldTrialParamByFeatureAsDouble(
features::kThrottleDelayable,
kNonDelayableWeightBase + base::NumberToString(config_param_index),
0.0);
// Check if the entry is already present. This will happen if the default
// params are being overridden by the field trial.
ResourceSchedulerParamsManager::ParamsForNetworkQualityContainer::iterator
iter = result.find(effective_connection_type.value());
if (iter != result.end()) {
iter->second.max_delayable_requests = max_delayable_requests;
iter->second.non_delayable_weight = non_delayable_weight;
} else {
result.emplace(std::make_pair(
effective_connection_type.value(),
ResourceSchedulerParamsManager::ParamsForNetworkQuality(
max_delayable_requests, non_delayable_weight, false,
base::nullopt)));
}
}
for (int ect = net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
ect <= net::EFFECTIVE_CONNECTION_TYPE_4G; ++ect) {
net::EffectiveConnectionType effective_connection_type =
static_cast<net::EffectiveConnectionType>(ect);
base::TimeDelta http_rtt =
net::NetworkQualityEstimatorParams::GetDefaultTypicalHttpRtt(
effective_connection_type);
base::TimeDelta max_queuing_time =
http_rtt * kHttpRttMultiplierForQueuingDuration;
// If GetDefaultTypicalHttpRtt returns a null value, set
// |max_queuing_time| to kUpperBoundQueuingDuration, This may happen
// when |ect| is UNKNOWN or OFFLINE. Both these cases are very rare, but
// it's important to handle them to ensure that |max_queuing_time|
// is set to some non-zero value in all cases. This ensures that the
// requests that are queued for too long are always unthrottled.
if (http_rtt.is_zero())
max_queuing_time = kUpperBoundQueuingDuration;
if (max_queuing_time < kLowerBoundQueuingDuration)
max_queuing_time = kLowerBoundQueuingDuration;
if (max_queuing_time > kUpperBoundQueuingDuration)
max_queuing_time = kUpperBoundQueuingDuration;
ResourceSchedulerParamsManager::ParamsForNetworkQualityContainer::iterator
iter = result.find(effective_connection_type);
if (iter != result.end()) {
iter->second.max_queuing_time = max_queuing_time;
} else {
result.emplace(std::make_pair(
effective_connection_type,
ResourceSchedulerParamsManager::ParamsForNetworkQuality(
kDefaultMaxNumDelayableRequestsPerClient, 0.0, false,
max_queuing_time)));
}
}
return result;
}
} // namespace
ResourceSchedulerParamsManager::ParamsForNetworkQuality::
ParamsForNetworkQuality()
: ResourceSchedulerParamsManager::ParamsForNetworkQuality(
kDefaultMaxNumDelayableRequestsPerClient,
0.0,
false,
base::nullopt) {}
ResourceSchedulerParamsManager::ParamsForNetworkQuality::
ParamsForNetworkQuality(size_t max_delayable_requests,
double non_delayable_weight,
bool delay_requests_on_multiplexed_connections,
base::Optional<base::TimeDelta> max_queuing_time)
: max_delayable_requests(max_delayable_requests),
non_delayable_weight(non_delayable_weight),
delay_requests_on_multiplexed_connections(
delay_requests_on_multiplexed_connections),
max_queuing_time(max_queuing_time) {}
ResourceSchedulerParamsManager::ParamsForNetworkQuality::
ParamsForNetworkQuality(
const ResourceSchedulerParamsManager::ParamsForNetworkQuality& other) =
default;
ResourceSchedulerParamsManager::ResourceSchedulerParamsManager()
: ResourceSchedulerParamsManager(GetParamsForNetworkQualityContainer()) {}
ResourceSchedulerParamsManager::ResourceSchedulerParamsManager(
const ParamsForNetworkQualityContainer&
params_for_network_quality_container)
: params_for_network_quality_container_(
params_for_network_quality_container) {}
ResourceSchedulerParamsManager::ResourceSchedulerParamsManager(
const ResourceSchedulerParamsManager& other)
: params_for_network_quality_container_(
other.params_for_network_quality_container_) {}
ResourceSchedulerParamsManager::~ResourceSchedulerParamsManager() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
ResourceSchedulerParamsManager::ParamsForNetworkQuality
ResourceSchedulerParamsManager::GetParamsForEffectiveConnectionType(
net::EffectiveConnectionType effective_connection_type) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ParamsForNetworkQualityContainer::const_iterator iter =
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,
false, base::nullopt);
}
} // namespace network