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"
17 const unsigned kInvalidCollectorIndex
= static_cast<unsigned>(-1);
19 const unsigned kDefaultMinimumUploadDelaySec
= 60;
20 const unsigned kDefaultMaximumUploadDelaySec
= 300;
21 const unsigned kDefaultUploadRetryIntervalSec
= 60;
23 const char* kMinimumUploadDelayFieldTrialName
= "DomRel-MinimumUploadDelay";
24 const char* kMaximumUploadDelayFieldTrialName
= "DomRel-MaximumUploadDelay";
25 const char* kUploadRetryIntervalFieldTrialName
= "DomRel-UploadRetryInterval";
27 unsigned GetUnsignedFieldTrialValueOrDefault(std::string field_trial_name
,
28 unsigned default_value
) {
29 if (!base::FieldTrialList::TrialExists(field_trial_name
))
32 std::string group_name
= base::FieldTrialList::FindFullName(field_trial_name
);
34 if (!base::StringToUint(group_name
, &value
)) {
35 LOG(ERROR
) << "Expected unsigned integer for field trial "
36 << field_trial_name
<< " group name, but got \"" << group_name
46 namespace domain_reliability
{
49 DomainReliabilityScheduler::Params
50 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults() {
51 DomainReliabilityScheduler::Params params
;
53 params
.minimum_upload_delay
=
54 base::TimeDelta::FromSeconds(GetUnsignedFieldTrialValueOrDefault(
55 kMinimumUploadDelayFieldTrialName
, kDefaultMinimumUploadDelaySec
));
56 params
.maximum_upload_delay
=
57 base::TimeDelta::FromSeconds(GetUnsignedFieldTrialValueOrDefault(
58 kMaximumUploadDelayFieldTrialName
, kDefaultMaximumUploadDelaySec
));
59 params
.upload_retry_interval
=
60 base::TimeDelta::FromSeconds(GetUnsignedFieldTrialValueOrDefault(
61 kUploadRetryIntervalFieldTrialName
, kDefaultUploadRetryIntervalSec
));
66 DomainReliabilityScheduler::DomainReliabilityScheduler(
68 size_t num_collectors
,
70 const ScheduleUploadCallback
& callback
)
72 collectors_(num_collectors
),
75 upload_pending_(false),
76 upload_scheduled_(false),
77 upload_running_(false),
78 collector_index_(kInvalidCollectorIndex
),
79 last_upload_finished_(false) {
82 DomainReliabilityScheduler::~DomainReliabilityScheduler() {}
84 void DomainReliabilityScheduler::OnBeaconAdded() {
86 first_beacon_time_
= time_
->NowTicks();
87 upload_pending_
= true;
88 MaybeScheduleUpload();
91 size_t DomainReliabilityScheduler::OnUploadStart() {
92 DCHECK(upload_scheduled_
);
93 DCHECK_EQ(kInvalidCollectorIndex
, collector_index_
);
94 upload_pending_
= false;
95 upload_scheduled_
= false;
96 upload_running_
= true;
98 base::TimeTicks now
= time_
->NowTicks();
99 base::TimeTicks min_upload_time
;
100 GetNextUploadTimeAndCollector(now
, &min_upload_time
, &collector_index_
);
101 DCHECK(min_upload_time
<= now
);
103 VLOG(1) << "Starting upload to collector " << collector_index_
<< ".";
105 last_upload_start_time_
= now
;
106 last_upload_collector_index_
= collector_index_
;
108 return collector_index_
;
111 void DomainReliabilityScheduler::OnUploadComplete(bool success
) {
112 DCHECK(upload_running_
);
113 DCHECK_NE(kInvalidCollectorIndex
, collector_index_
);
114 upload_running_
= false;
116 VLOG(1) << "Upload to collector " << collector_index_
117 << (success
? " succeeded." : " failed.");
119 CollectorState
* collector
= &collectors_
[collector_index_
];
120 collector_index_
= kInvalidCollectorIndex
;
123 collector
->failures
= 0;
125 // Restore upload_pending_ and first_beacon_time_ to pre-upload state,
126 // since upload failed.
127 upload_pending_
= true;
128 first_beacon_time_
= old_first_beacon_time_
;
130 ++collector
->failures
;
133 base::TimeTicks now
= time_
->NowTicks();
134 base::TimeDelta retry_interval
= GetUploadRetryInterval(collector
->failures
);
135 collector
->next_upload
= now
+ retry_interval
;
137 last_upload_end_time_
= now
;
138 last_upload_success_
= success
;
139 last_upload_finished_
= true;
141 VLOG(1) << "Next upload to collector at least "
142 << retry_interval
.InSeconds() << " seconds from now.";
144 MaybeScheduleUpload();
147 base::Value
* DomainReliabilityScheduler::GetWebUIData() const {
148 base::TimeTicks now
= time_
->NowTicks();
150 base::DictionaryValue
* data
= new base::DictionaryValue();
152 data
->SetBoolean("upload_pending", upload_pending_
);
153 data
->SetBoolean("upload_scheduled", upload_scheduled_
);
154 data
->SetBoolean("upload_running", upload_running_
);
156 data
->SetInteger("scheduled_min", (scheduled_min_time_
- now
).InSeconds());
157 data
->SetInteger("scheduled_max", (scheduled_max_time_
- now
).InSeconds());
159 data
->SetInteger("collector_index", static_cast<int>(collector_index_
));
161 if (last_upload_finished_
) {
162 base::DictionaryValue
* last
= new base::DictionaryValue();
163 last
->SetInteger("start_time", (now
- last_upload_start_time_
).InSeconds());
164 last
->SetInteger("end_time", (now
- last_upload_end_time_
).InSeconds());
165 last
->SetInteger("collector_index",
166 static_cast<int>(last_upload_collector_index_
));
167 last
->SetBoolean("success", last_upload_success_
);
168 data
->Set("last_upload", last
);
171 base::ListValue
* collectors
= new base::ListValue();
172 for (size_t i
= 0; i
< collectors_
.size(); ++i
) {
173 const CollectorState
* state
= &collectors_
[i
];
174 base::DictionaryValue
* value
= new base::DictionaryValue();
175 value
->SetInteger("failures", state
->failures
);
176 value
->SetInteger("next_upload", (state
->next_upload
- now
).InSeconds());
177 collectors
->Append(value
);
179 data
->Set("collectors", collectors
);
184 DomainReliabilityScheduler::CollectorState::CollectorState() : failures(0) {}
186 void DomainReliabilityScheduler::MaybeScheduleUpload() {
187 if (!upload_pending_
|| upload_scheduled_
|| upload_running_
)
190 upload_scheduled_
= true;
191 old_first_beacon_time_
= first_beacon_time_
;
193 base::TimeTicks now
= time_
->NowTicks();
195 base::TimeTicks min_by_deadline
, max_by_deadline
;
196 min_by_deadline
= first_beacon_time_
+ params_
.minimum_upload_delay
;
197 max_by_deadline
= first_beacon_time_
+ params_
.maximum_upload_delay
;
198 DCHECK(min_by_deadline
<= max_by_deadline
);
200 base::TimeTicks min_by_backoff
;
201 size_t collector_index
;
202 GetNextUploadTimeAndCollector(now
, &min_by_backoff
, &collector_index
);
204 scheduled_min_time_
= std::max(min_by_deadline
, min_by_backoff
);
205 scheduled_max_time_
= std::max(max_by_deadline
, min_by_backoff
);
207 base::TimeDelta min_delay
= scheduled_min_time_
- now
;
208 base::TimeDelta max_delay
= scheduled_max_time_
- now
;
210 VLOG(1) << "Scheduling upload for between " << min_delay
.InSeconds()
211 << " and " << max_delay
.InSeconds() << " seconds from now.";
213 callback_
.Run(min_delay
, max_delay
);
216 // TODO(ttuttle): Add min and max interval to config, use that instead.
218 // TODO(ttuttle): Cap min and max intervals received from config.
220 void DomainReliabilityScheduler::GetNextUploadTimeAndCollector(
222 base::TimeTicks
* upload_time_out
,
223 size_t* collector_index_out
) {
224 DCHECK(upload_time_out
);
225 DCHECK(collector_index_out
);
227 base::TimeTicks min_time
;
228 size_t min_index
= kInvalidCollectorIndex
;
230 for (size_t i
= 0; i
< collectors_
.size(); ++i
) {
231 CollectorState
* collector
= &collectors_
[i
];
232 // If a collector is usable, use the first one in the list.
233 if (collector
->failures
== 0 || collector
->next_upload
<= now
) {
237 // If not, keep track of which will be usable soonest:
238 } else if (min_index
== kInvalidCollectorIndex
||
239 collector
->next_upload
< min_time
) {
240 min_time
= collector
->next_upload
;
245 DCHECK_NE(kInvalidCollectorIndex
, min_index
);
246 *upload_time_out
= min_time
;
247 *collector_index_out
= min_index
;
250 base::TimeDelta
DomainReliabilityScheduler::GetUploadRetryInterval(
253 return base::TimeDelta::FromSeconds(0);
255 // Don't back off more than 64x the original delay.
258 return params_
.upload_retry_interval
* (1 << (failures
- 1));
262 } // namespace domain_reliability