Update To 11.40.268.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 // Fixed elements of backoff policy
28 const double kMultiplyFactor = 2.0;
29 const double kJitterFactor = 0.1;
30 const int64 kMaximumBackoffMs = 60 * 1000 * 1000;
31
32 unsigned GetUnsignedFieldTrialValueOrDefault(std::string field_trial_name,
33                                              unsigned default_value) {
34   if (!base::FieldTrialList::TrialExists(field_trial_name))
35     return default_value;
36
37   std::string group_name = base::FieldTrialList::FindFullName(field_trial_name);
38   unsigned value;
39   if (!base::StringToUint(group_name, &value)) {
40     LOG(ERROR) << "Expected unsigned integer for field trial "
41                << field_trial_name << " group name, but got \"" << group_name
42                << "\".";
43     return default_value;
44   }
45
46   return value;
47 }
48
49 }  // namespace
50
51 namespace domain_reliability {
52
53 // static
54 DomainReliabilityScheduler::Params
55 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults() {
56   DomainReliabilityScheduler::Params params;
57
58   params.minimum_upload_delay =
59       base::TimeDelta::FromSeconds(GetUnsignedFieldTrialValueOrDefault(
60           kMinimumUploadDelayFieldTrialName, kDefaultMinimumUploadDelaySec));
61   params.maximum_upload_delay =
62       base::TimeDelta::FromSeconds(GetUnsignedFieldTrialValueOrDefault(
63           kMaximumUploadDelayFieldTrialName, kDefaultMaximumUploadDelaySec));
64   params.upload_retry_interval =
65       base::TimeDelta::FromSeconds(GetUnsignedFieldTrialValueOrDefault(
66           kUploadRetryIntervalFieldTrialName, kDefaultUploadRetryIntervalSec));
67
68   return params;
69 }
70
71 DomainReliabilityScheduler::DomainReliabilityScheduler(
72     MockableTime* time,
73     size_t num_collectors,
74     const Params& params,
75     const ScheduleUploadCallback& callback)
76     : time_(time),
77       params_(params),
78       callback_(callback),
79       upload_pending_(false),
80       upload_scheduled_(false),
81       upload_running_(false),
82       collector_index_(kInvalidCollectorIndex),
83       last_upload_finished_(false) {
84   backoff_policy_.num_errors_to_ignore = 0;
85   backoff_policy_.initial_delay_ms =
86       params.upload_retry_interval.InMilliseconds();
87   backoff_policy_.multiply_factor = kMultiplyFactor;
88   backoff_policy_.jitter_factor = kJitterFactor;
89   backoff_policy_.maximum_backoff_ms = kMaximumBackoffMs;
90   backoff_policy_.entry_lifetime_ms = 0;
91   backoff_policy_.always_use_initial_delay = false;
92
93   for (size_t i = 0; i < num_collectors; ++i) {
94     collectors_.push_back(
95       new MockableTimeBackoffEntry(&backoff_policy_, time_));
96   }
97 }
98
99 DomainReliabilityScheduler::~DomainReliabilityScheduler() {}
100
101 void DomainReliabilityScheduler::OnBeaconAdded() {
102   if (!upload_pending_)
103     first_beacon_time_ = time_->NowTicks();
104   upload_pending_ = true;
105   MaybeScheduleUpload();
106 }
107
108 size_t DomainReliabilityScheduler::OnUploadStart() {
109   DCHECK(upload_scheduled_);
110   DCHECK_EQ(kInvalidCollectorIndex, collector_index_);
111   upload_pending_ = false;
112   upload_scheduled_ = false;
113   upload_running_ = true;
114
115   base::TimeTicks now = time_->NowTicks();
116   base::TimeTicks min_upload_time;
117   GetNextUploadTimeAndCollector(now, &min_upload_time, &collector_index_);
118   DCHECK(min_upload_time <= now);
119
120   VLOG(1) << "Starting upload to collector " << collector_index_ << ".";
121
122   last_upload_start_time_ = now;
123   last_upload_collector_index_ = collector_index_;
124
125   return collector_index_;
126 }
127
128 void DomainReliabilityScheduler::OnUploadComplete(bool success) {
129   DCHECK(upload_running_);
130   DCHECK_NE(kInvalidCollectorIndex, collector_index_);
131   upload_running_ = false;
132
133   VLOG(1) << "Upload to collector " << collector_index_
134           << (success ? " succeeded." : " failed.");
135
136   net::BackoffEntry* backoff = collectors_[collector_index_];
137   collector_index_ = kInvalidCollectorIndex;
138   backoff->InformOfRequest(success);
139
140   if (!success) {
141     // Restore upload_pending_ and first_beacon_time_ to pre-upload state,
142     // since upload failed.
143     upload_pending_ = true;
144     first_beacon_time_ = old_first_beacon_time_;
145   }
146
147   last_upload_end_time_ = time_->NowTicks();
148   last_upload_success_ = success;
149   last_upload_finished_ = true;
150
151   MaybeScheduleUpload();
152 }
153
154 base::Value* DomainReliabilityScheduler::GetWebUIData() const {
155   base::TimeTicks now = time_->NowTicks();
156
157   base::DictionaryValue* data = new base::DictionaryValue();
158
159   data->SetBoolean("upload_pending", upload_pending_);
160   data->SetBoolean("upload_scheduled", upload_scheduled_);
161   data->SetBoolean("upload_running", upload_running_);
162
163   data->SetInteger("scheduled_min", (scheduled_min_time_ - now).InSeconds());
164   data->SetInteger("scheduled_max", (scheduled_max_time_ - now).InSeconds());
165
166   data->SetInteger("collector_index", static_cast<int>(collector_index_));
167
168   if (last_upload_finished_) {
169     base::DictionaryValue* last = new base::DictionaryValue();
170     last->SetInteger("start_time", (now - last_upload_start_time_).InSeconds());
171     last->SetInteger("end_time", (now - last_upload_end_time_).InSeconds());
172     last->SetInteger("collector_index",
173         static_cast<int>(last_upload_collector_index_));
174     last->SetBoolean("success", last_upload_success_);
175     data->Set("last_upload", last);
176   }
177
178   base::ListValue* collectors = new base::ListValue();
179   for (size_t i = 0; i < collectors_.size(); ++i) {
180     const net::BackoffEntry* backoff = collectors_[i];
181     base::DictionaryValue* value = new base::DictionaryValue();
182     value->SetInteger("failures", backoff->failure_count());
183     value->SetInteger("next_upload",
184         (backoff->GetReleaseTime() - now).InSeconds());
185     collectors->Append(value);
186   }
187   data->Set("collectors", collectors);
188
189   return data;
190 }
191
192 void DomainReliabilityScheduler::MakeDeterministicForTesting() {
193   backoff_policy_.jitter_factor = 0.0;
194 }
195
196 void DomainReliabilityScheduler::MaybeScheduleUpload() {
197   if (!upload_pending_ || upload_scheduled_ || upload_running_)
198     return;
199
200   upload_scheduled_ = true;
201   old_first_beacon_time_ = first_beacon_time_;
202
203   base::TimeTicks now = time_->NowTicks();
204
205   base::TimeTicks min_by_deadline, max_by_deadline;
206   min_by_deadline = first_beacon_time_ + params_.minimum_upload_delay;
207   max_by_deadline = first_beacon_time_ + params_.maximum_upload_delay;
208   DCHECK(min_by_deadline <= max_by_deadline);
209
210   base::TimeTicks min_by_backoff;
211   size_t collector_index;
212   GetNextUploadTimeAndCollector(now, &min_by_backoff, &collector_index);
213
214   scheduled_min_time_ = std::max(min_by_deadline, min_by_backoff);
215   scheduled_max_time_ = std::max(max_by_deadline, min_by_backoff);
216
217   base::TimeDelta min_delay = scheduled_min_time_ - now;
218   base::TimeDelta max_delay = scheduled_max_time_ - now;
219
220   VLOG(1) << "Scheduling upload for between " << min_delay.InSeconds()
221           << " and " << max_delay.InSeconds() << " seconds from now.";
222
223   callback_.Run(min_delay, max_delay);
224 }
225
226 // TODO(ttuttle): Add min and max interval to config, use that instead.
227
228 // TODO(ttuttle): Cap min and max intervals received from config.
229
230 void DomainReliabilityScheduler::GetNextUploadTimeAndCollector(
231     base::TimeTicks now,
232     base::TimeTicks* upload_time_out,
233     size_t* collector_index_out) {
234   DCHECK(upload_time_out);
235   DCHECK(collector_index_out);
236
237   base::TimeTicks min_time;
238   size_t min_index = kInvalidCollectorIndex;
239
240   for (size_t i = 0; i < collectors_.size(); ++i) {
241     net::BackoffEntry* backoff = collectors_[i];
242     // If a collector is usable, use the first one in the list.
243     if (!backoff->ShouldRejectRequest()) {
244       min_time = now;
245       min_index = i;
246       break;
247     }
248
249     // If not, keep track of which will be usable soonest:
250     base::TimeTicks time = backoff->GetReleaseTime();
251     if (min_index == kInvalidCollectorIndex || time < min_time) {
252       min_time = time;
253       min_index = i;
254     }
255   }
256
257   DCHECK_NE(kInvalidCollectorIndex, min_index);
258   *upload_time_out = min_time;
259   *collector_index_out = min_index;
260 }
261
262 }  // namespace domain_reliability