• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "components/domain_reliability/scheduler.h"
6 
7 #include <algorithm>
8 
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 
15 namespace {
16 
17 const unsigned kInvalidCollectorIndex = static_cast<unsigned>(-1);
18 
19 const unsigned kDefaultMinimumUploadDelaySec = 60;
20 const unsigned kDefaultMaximumUploadDelaySec = 300;
21 const unsigned kDefaultUploadRetryIntervalSec = 60;
22 
23 const char* kMinimumUploadDelayFieldTrialName = "DomRel-MinimumUploadDelay";
24 const char* kMaximumUploadDelayFieldTrialName = "DomRel-MaximumUploadDelay";
25 const char* kUploadRetryIntervalFieldTrialName = "DomRel-UploadRetryInterval";
26 
GetUnsignedFieldTrialValueOrDefault(std::string field_trial_name,unsigned default_value)27 unsigned GetUnsignedFieldTrialValueOrDefault(std::string field_trial_name,
28                                              unsigned default_value) {
29   if (!base::FieldTrialList::TrialExists(field_trial_name))
30     return default_value;
31 
32   std::string group_name = base::FieldTrialList::FindFullName(field_trial_name);
33   unsigned value;
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
37                << "\".";
38     return default_value;
39   }
40 
41   return value;
42 }
43 
44 }  // namespace
45 
46 namespace domain_reliability {
47 
48 // static
49 DomainReliabilityScheduler::Params
GetFromFieldTrialsOrDefaults()50 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults() {
51   DomainReliabilityScheduler::Params params;
52 
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));
62 
63   return params;
64 }
65 
DomainReliabilityScheduler(MockableTime * time,size_t num_collectors,const Params & params,const ScheduleUploadCallback & callback)66 DomainReliabilityScheduler::DomainReliabilityScheduler(
67     MockableTime* time,
68     size_t num_collectors,
69     const Params& params,
70     const ScheduleUploadCallback& callback)
71     : time_(time),
72       collectors_(num_collectors),
73       params_(params),
74       callback_(callback),
75       upload_pending_(false),
76       upload_scheduled_(false),
77       upload_running_(false),
78       collector_index_(kInvalidCollectorIndex),
79       last_upload_finished_(false) {
80 }
81 
~DomainReliabilityScheduler()82 DomainReliabilityScheduler::~DomainReliabilityScheduler() {}
83 
OnBeaconAdded()84 void DomainReliabilityScheduler::OnBeaconAdded() {
85   if (!upload_pending_)
86     first_beacon_time_ = time_->NowTicks();
87   upload_pending_ = true;
88   MaybeScheduleUpload();
89 }
90 
OnUploadStart()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;
97 
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);
102 
103   VLOG(1) << "Starting upload to collector " << collector_index_ << ".";
104 
105   last_upload_start_time_ = now;
106   last_upload_collector_index_ = collector_index_;
107 
108   return collector_index_;
109 }
110 
OnUploadComplete(bool success)111 void DomainReliabilityScheduler::OnUploadComplete(bool success) {
112   DCHECK(upload_running_);
113   DCHECK_NE(kInvalidCollectorIndex, collector_index_);
114   upload_running_ = false;
115 
116   VLOG(1) << "Upload to collector " << collector_index_
117           << (success ? " succeeded." : " failed.");
118 
119   CollectorState* collector = &collectors_[collector_index_];
120   collector_index_ = kInvalidCollectorIndex;
121 
122   if (success) {
123     collector->failures = 0;
124   } else {
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_;
129 
130     ++collector->failures;
131   }
132 
133   base::TimeTicks now = time_->NowTicks();
134   base::TimeDelta retry_interval = GetUploadRetryInterval(collector->failures);
135   collector->next_upload = now + retry_interval;
136 
137   last_upload_end_time_ = now;
138   last_upload_success_ = success;
139   last_upload_finished_ = true;
140 
141   VLOG(1) << "Next upload to collector at least "
142           << retry_interval.InSeconds() << " seconds from now.";
143 
144   MaybeScheduleUpload();
145 }
146 
GetWebUIData() const147 base::Value* DomainReliabilityScheduler::GetWebUIData() const {
148   base::TimeTicks now = time_->NowTicks();
149 
150   base::DictionaryValue* data = new base::DictionaryValue();
151 
152   data->SetBoolean("upload_pending", upload_pending_);
153   data->SetBoolean("upload_scheduled", upload_scheduled_);
154   data->SetBoolean("upload_running", upload_running_);
155 
156   data->SetInteger("scheduled_min", (scheduled_min_time_ - now).InSeconds());
157   data->SetInteger("scheduled_max", (scheduled_max_time_ - now).InSeconds());
158 
159   data->SetInteger("collector_index", static_cast<int>(collector_index_));
160 
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);
169   }
170 
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);
178   }
179   data->Set("collectors", collectors);
180 
181   return data;
182 }
183 
CollectorState()184 DomainReliabilityScheduler::CollectorState::CollectorState() : failures(0) {}
185 
MaybeScheduleUpload()186 void DomainReliabilityScheduler::MaybeScheduleUpload() {
187   if (!upload_pending_ || upload_scheduled_ || upload_running_)
188     return;
189 
190   upload_scheduled_ = true;
191   old_first_beacon_time_ = first_beacon_time_;
192 
193   base::TimeTicks now = time_->NowTicks();
194 
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);
199 
200   base::TimeTicks min_by_backoff;
201   size_t collector_index;
202   GetNextUploadTimeAndCollector(now, &min_by_backoff, &collector_index);
203 
204   scheduled_min_time_ = std::max(min_by_deadline, min_by_backoff);
205   scheduled_max_time_ = std::max(max_by_deadline, min_by_backoff);
206 
207   base::TimeDelta min_delay = scheduled_min_time_ - now;
208   base::TimeDelta max_delay = scheduled_max_time_ - now;
209 
210   VLOG(1) << "Scheduling upload for between " << min_delay.InSeconds()
211           << " and " << max_delay.InSeconds() << " seconds from now.";
212 
213   callback_.Run(min_delay, max_delay);
214 }
215 
216 // TODO(ttuttle): Add min and max interval to config, use that instead.
217 
218 // TODO(ttuttle): Cap min and max intervals received from config.
219 
GetNextUploadTimeAndCollector(base::TimeTicks now,base::TimeTicks * upload_time_out,size_t * collector_index_out)220 void DomainReliabilityScheduler::GetNextUploadTimeAndCollector(
221     base::TimeTicks now,
222     base::TimeTicks* upload_time_out,
223     size_t* collector_index_out) {
224   DCHECK(upload_time_out);
225   DCHECK(collector_index_out);
226 
227   base::TimeTicks min_time;
228   size_t min_index = kInvalidCollectorIndex;
229 
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) {
234       min_time = now;
235       min_index = i;
236       break;
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;
241       min_index = i;
242     }
243   }
244 
245   DCHECK_NE(kInvalidCollectorIndex, min_index);
246   *upload_time_out = min_time;
247   *collector_index_out = min_index;
248 }
249 
GetUploadRetryInterval(unsigned failures)250 base::TimeDelta DomainReliabilityScheduler::GetUploadRetryInterval(
251     unsigned failures) {
252   if (failures == 0)
253     return base::TimeDelta::FromSeconds(0);
254   else {
255     // Don't back off more than 64x the original delay.
256     if (failures > 7)
257       failures = 7;
258     return params_.upload_retry_interval * (1 << (failures - 1));
259   }
260 }
261 
262 }  // namespace domain_reliability
263