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"
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"
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
))
38 std::string group_name
= base::FieldTrialList::FindFullName(field_trial_name
);
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
52 namespace domain_reliability
{
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
));
72 DomainReliabilityScheduler::DomainReliabilityScheduler(
74 size_t num_collectors
,
76 const ScheduleUploadCallback
& 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());
198 void DomainReliabilityScheduler::MakeDeterministicForTesting() {
199 backoff_policy_
.jitter_factor
= 0.0;
202 void DomainReliabilityScheduler::MaybeScheduleUpload() {
203 if (!upload_pending_
|| upload_scheduled_
|| upload_running_
)
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(
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()) {
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
) {
263 DCHECK_NE(kInvalidCollectorIndex
, min_index
);
264 *upload_time_out
= min_time
;
265 *collector_index_out
= min_index
;
268 } // namespace domain_reliability