Roll src/third_party/WebKit f298044:aa8346d (svn 202628:202629)
[chromium-blink-merge.git] / components / domain_reliability / scheduler.cc
blob040c4b1c38a7f1b47ae48a6503928e6720b5204d
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/domain_reliability/scheduler.h"
7 #include <algorithm>
9 #include "base/metrics/field_trial.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/values.h"
12 #include "components/domain_reliability/config.h"
13 #include "components/domain_reliability/util.h"
14 #include "net/base/backoff_entry.h"
16 namespace {
18 const unsigned kInvalidCollectorIndex = static_cast<unsigned>(-1);
20 const unsigned kDefaultMinimumUploadDelaySec = 60;
21 const unsigned kDefaultMaximumUploadDelaySec = 300;
22 const unsigned kDefaultUploadRetryIntervalSec = 60;
24 const char* kMinimumUploadDelayFieldTrialName = "DomRel-MinimumUploadDelay";
25 const char* kMaximumUploadDelayFieldTrialName = "DomRel-MaximumUploadDelay";
26 const char* kUploadRetryIntervalFieldTrialName = "DomRel-UploadRetryInterval";
28 // Fixed elements of backoff policy
29 const double kMultiplyFactor = 2.0;
30 const double kJitterFactor = 0.1;
31 const int64 kMaximumBackoffMs = 60 * 1000 * 1000;
33 unsigned GetUnsignedFieldTrialValueOrDefault(std::string field_trial_name,
34 unsigned default_value) {
35 if (!base::FieldTrialList::TrialExists(field_trial_name))
36 return default_value;
38 std::string group_name = base::FieldTrialList::FindFullName(field_trial_name);
39 unsigned value;
40 if (!base::StringToUint(group_name, &value)) {
41 LOG(ERROR) << "Expected unsigned integer for field trial "
42 << field_trial_name << " group name, but got \"" << group_name
43 << "\".";
44 return default_value;
47 return value;
50 } // namespace
52 namespace domain_reliability {
54 // static
55 DomainReliabilityScheduler::Params
56 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults() {
57 DomainReliabilityScheduler::Params params;
59 params.minimum_upload_delay =
60 base::TimeDelta::FromSeconds(GetUnsignedFieldTrialValueOrDefault(
61 kMinimumUploadDelayFieldTrialName, kDefaultMinimumUploadDelaySec));
62 params.maximum_upload_delay =
63 base::TimeDelta::FromSeconds(GetUnsignedFieldTrialValueOrDefault(
64 kMaximumUploadDelayFieldTrialName, kDefaultMaximumUploadDelaySec));
65 params.upload_retry_interval =
66 base::TimeDelta::FromSeconds(GetUnsignedFieldTrialValueOrDefault(
67 kUploadRetryIntervalFieldTrialName, kDefaultUploadRetryIntervalSec));
69 return params;
72 DomainReliabilityScheduler::DomainReliabilityScheduler(
73 MockableTime* time,
74 size_t num_collectors,
75 const Params& params,
76 const ScheduleUploadCallback& callback)
77 : time_(time),
78 params_(params),
79 callback_(callback),
80 upload_pending_(false),
81 upload_scheduled_(false),
82 upload_running_(false),
83 collector_index_(kInvalidCollectorIndex),
84 last_upload_finished_(false) {
85 backoff_policy_.num_errors_to_ignore = 0;
86 backoff_policy_.initial_delay_ms =
87 params.upload_retry_interval.InMilliseconds();
88 backoff_policy_.multiply_factor = kMultiplyFactor;
89 backoff_policy_.jitter_factor = kJitterFactor;
90 backoff_policy_.maximum_backoff_ms = kMaximumBackoffMs;
91 backoff_policy_.entry_lifetime_ms = 0;
92 backoff_policy_.always_use_initial_delay = false;
94 for (size_t i = 0; i < num_collectors; ++i) {
95 collectors_.push_back(
96 new net::BackoffEntry(&backoff_policy_, time_));
100 DomainReliabilityScheduler::~DomainReliabilityScheduler() {}
102 void DomainReliabilityScheduler::OnBeaconAdded() {
103 if (!upload_pending_)
104 first_beacon_time_ = time_->NowTicks();
105 upload_pending_ = true;
106 MaybeScheduleUpload();
109 size_t DomainReliabilityScheduler::OnUploadStart() {
110 DCHECK(upload_scheduled_);
111 DCHECK_EQ(kInvalidCollectorIndex, collector_index_);
112 upload_pending_ = false;
113 upload_scheduled_ = false;
114 upload_running_ = true;
116 base::TimeTicks now = time_->NowTicks();
117 base::TimeTicks min_upload_time;
118 GetNextUploadTimeAndCollector(now, &min_upload_time, &collector_index_);
119 DCHECK(min_upload_time <= now);
121 VLOG(1) << "Starting upload to collector " << collector_index_ << ".";
123 last_upload_start_time_ = now;
124 last_upload_collector_index_ = collector_index_;
126 return collector_index_;
129 void DomainReliabilityScheduler::OnUploadComplete(
130 const DomainReliabilityUploader::UploadResult& result) {
131 DCHECK(upload_running_);
132 DCHECK_NE(kInvalidCollectorIndex, collector_index_);
133 upload_running_ = false;
135 VLOG(1) << "Upload to collector " << collector_index_
136 << (result.is_success() ? " succeeded." : " failed.");
138 net::BackoffEntry* backoff = collectors_[collector_index_];
139 collector_index_ = kInvalidCollectorIndex;
141 backoff->InformOfRequest(result.is_success());
142 if (result.is_retry_after())
143 backoff->SetCustomReleaseTime(time_->NowTicks() + result.retry_after);
144 last_collector_retry_delay_ = backoff->GetTimeUntilRelease();
146 if (!result.is_success()) {
147 // Restore upload_pending_ and first_beacon_time_ to pre-upload state,
148 // since upload failed.
149 upload_pending_ = true;
150 first_beacon_time_ = old_first_beacon_time_;
153 last_upload_end_time_ = time_->NowTicks();
154 last_upload_success_ = result.is_success();
155 last_upload_finished_ = true;
157 MaybeScheduleUpload();
160 scoped_ptr<base::Value> DomainReliabilityScheduler::GetWebUIData() const {
161 base::TimeTicks now = time_->NowTicks();
163 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue());
165 data->SetBoolean("upload_pending", upload_pending_);
166 data->SetBoolean("upload_scheduled", upload_scheduled_);
167 data->SetBoolean("upload_running", upload_running_);
169 data->SetInteger("scheduled_min", (scheduled_min_time_ - now).InSeconds());
170 data->SetInteger("scheduled_max", (scheduled_max_time_ - now).InSeconds());
172 data->SetInteger("collector_index", static_cast<int>(collector_index_));
174 if (last_upload_finished_) {
175 scoped_ptr<base::DictionaryValue> last(new base::DictionaryValue());
176 last->SetInteger("start_time", (now - last_upload_start_time_).InSeconds());
177 last->SetInteger("end_time", (now - last_upload_end_time_).InSeconds());
178 last->SetInteger("collector_index",
179 static_cast<int>(last_upload_collector_index_));
180 last->SetBoolean("success", last_upload_success_);
181 data->Set("last_upload", last.Pass());
184 scoped_ptr<base::ListValue> collectors_value(new base::ListValue());
185 for (const auto& collector : collectors_) {
186 scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
187 value->SetInteger("failures", collector->failure_count());
188 value->SetInteger("next_upload",
189 (collector->GetReleaseTime() - now).InSeconds());
190 // Using release instead of Pass because Pass can't implicitly upcast.
191 collectors_value->Append(value.release());
193 data->Set("collectors", collectors_value.Pass());
195 return data.Pass();
198 void DomainReliabilityScheduler::MakeDeterministicForTesting() {
199 backoff_policy_.jitter_factor = 0.0;
202 void DomainReliabilityScheduler::MaybeScheduleUpload() {
203 if (!upload_pending_ || upload_scheduled_ || upload_running_)
204 return;
206 upload_scheduled_ = true;
207 old_first_beacon_time_ = first_beacon_time_;
209 base::TimeTicks now = time_->NowTicks();
211 base::TimeTicks min_by_deadline, max_by_deadline;
212 min_by_deadline = first_beacon_time_ + params_.minimum_upload_delay;
213 max_by_deadline = first_beacon_time_ + params_.maximum_upload_delay;
214 DCHECK(min_by_deadline <= max_by_deadline);
216 base::TimeTicks min_by_backoff;
217 size_t collector_index;
218 GetNextUploadTimeAndCollector(now, &min_by_backoff, &collector_index);
220 scheduled_min_time_ = std::max(min_by_deadline, min_by_backoff);
221 scheduled_max_time_ = std::max(max_by_deadline, min_by_backoff);
223 base::TimeDelta min_delay = scheduled_min_time_ - now;
224 base::TimeDelta max_delay = scheduled_max_time_ - now;
226 VLOG(1) << "Scheduling upload for between " << min_delay.InSeconds()
227 << " and " << max_delay.InSeconds() << " seconds from now.";
229 callback_.Run(min_delay, max_delay);
232 // TODO(ttuttle): Add min and max interval to config, use that instead.
234 // TODO(ttuttle): Cap min and max intervals received from config.
236 void DomainReliabilityScheduler::GetNextUploadTimeAndCollector(
237 base::TimeTicks now,
238 base::TimeTicks* upload_time_out,
239 size_t* collector_index_out) {
240 DCHECK(upload_time_out);
241 DCHECK(collector_index_out);
243 base::TimeTicks min_time;
244 size_t min_index = kInvalidCollectorIndex;
246 for (size_t i = 0; i < collectors_.size(); ++i) {
247 net::BackoffEntry* backoff = collectors_[i];
248 // If a collector is usable, use the first one in the list.
249 if (!backoff->ShouldRejectRequest()) {
250 min_time = now;
251 min_index = i;
252 break;
255 // If not, keep track of which will be usable soonest:
256 base::TimeTicks time = backoff->GetReleaseTime();
257 if (min_index == kInvalidCollectorIndex || time < min_time) {
258 min_time = time;
259 min_index = i;
263 DCHECK_NE(kInvalidCollectorIndex, min_index);
264 *upload_time_out = min_time;
265 *collector_index_out = min_index;
268 } // namespace domain_reliability