Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / components / domain_reliability / scheduler.cc
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
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
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
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
82 DomainReliabilityScheduler::~DomainReliabilityScheduler() {}
83
84 void DomainReliabilityScheduler::OnBeaconAdded() {
85   if (!upload_pending_)
86     first_beacon_time_ = time_->NowTicks();
87   upload_pending_ = true;
88   MaybeScheduleUpload();
89 }
90
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
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
147 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
184 DomainReliabilityScheduler::CollectorState::CollectorState() : failures(0) {}
185
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
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
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