• 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 "components/domain_reliability/config.h"
12 #include "components/domain_reliability/util.h"
13 
14 namespace {
15 
16 const unsigned kInvalidCollectorIndex = -1;
17 
18 const unsigned kDefaultMinimumUploadDelaySec = 60;
19 const unsigned kDefaultMaximumUploadDelaySec = 300;
20 const unsigned kDefaultUploadRetryIntervalSec = 60;
21 
22 const char* kMinimumUploadDelayFieldTrialName = "DomRel-MinimumUploadDelay";
23 const char* kMaximumUploadDelayFieldTrialName = "DomRel-MaximumUploadDelay";
24 const char* kUploadRetryIntervalFieldTrialName = "DomRel-UploadRetryInterval";
25 
GetUnsignedFieldTrialValueOrDefault(std::string field_trial_name,unsigned default_value)26 unsigned GetUnsignedFieldTrialValueOrDefault(std::string field_trial_name,
27                                              unsigned default_value) {
28   if (!base::FieldTrialList::TrialExists(field_trial_name))
29     return default_value;
30 
31   std::string group_name = base::FieldTrialList::FindFullName(field_trial_name);
32   unsigned value;
33   if (!base::StringToUint(group_name, &value)) {
34     LOG(ERROR) << "Expected unsigned integer for field trial "
35                << field_trial_name << " group name, but got \"" << group_name
36                << "\".";
37     return default_value;
38   }
39 
40   return value;
41 }
42 
43 }  // namespace
44 
45 namespace domain_reliability {
46 
47 // static
48 DomainReliabilityScheduler::Params
GetFromFieldTrialsOrDefaults()49 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults() {
50   DomainReliabilityScheduler::Params params;
51 
52   params.minimum_upload_delay =
53       base::TimeDelta::FromSeconds(GetUnsignedFieldTrialValueOrDefault(
54           kMinimumUploadDelayFieldTrialName, kDefaultMinimumUploadDelaySec));
55   params.maximum_upload_delay =
56       base::TimeDelta::FromSeconds(GetUnsignedFieldTrialValueOrDefault(
57           kMaximumUploadDelayFieldTrialName, kDefaultMaximumUploadDelaySec));
58   params.upload_retry_interval =
59       base::TimeDelta::FromSeconds(GetUnsignedFieldTrialValueOrDefault(
60           kUploadRetryIntervalFieldTrialName, kDefaultUploadRetryIntervalSec));
61 
62   return params;
63 }
64 
DomainReliabilityScheduler(MockableTime * time,size_t num_collectors,const Params & params,const ScheduleUploadCallback & callback)65 DomainReliabilityScheduler::DomainReliabilityScheduler(
66     MockableTime* time,
67     size_t num_collectors,
68     const Params& params,
69     const ScheduleUploadCallback& callback)
70     : time_(time),
71       collectors_(num_collectors),
72       params_(params),
73       callback_(callback),
74       upload_pending_(false),
75       upload_scheduled_(false),
76       upload_running_(false),
77       collector_index_(kInvalidCollectorIndex) {
78 }
79 
~DomainReliabilityScheduler()80 DomainReliabilityScheduler::~DomainReliabilityScheduler() {}
81 
OnBeaconAdded()82 void DomainReliabilityScheduler::OnBeaconAdded() {
83   if (!upload_pending_)
84     first_beacon_time_ = time_->NowTicks();
85   upload_pending_ = true;
86   MaybeScheduleUpload();
87 }
88 
OnUploadStart()89 size_t DomainReliabilityScheduler::OnUploadStart() {
90   DCHECK(upload_scheduled_);
91   DCHECK_EQ(kInvalidCollectorIndex, collector_index_);
92   upload_pending_ = false;
93   upload_scheduled_ = false;
94   upload_running_ = true;
95 
96   base::TimeTicks now = time_->NowTicks();
97   base::TimeTicks min_upload_time;
98   GetNextUploadTimeAndCollector(now, &min_upload_time, &collector_index_);
99   DCHECK(min_upload_time <= now);
100 
101   VLOG(1) << "Starting upload to collector " << collector_index_ << ".";
102 
103   return collector_index_;
104 }
105 
OnUploadComplete(bool success)106 void DomainReliabilityScheduler::OnUploadComplete(bool success) {
107   DCHECK(upload_running_);
108   DCHECK_NE(kInvalidCollectorIndex, collector_index_);
109   upload_running_ = false;
110 
111   VLOG(1) << "Upload to collector " << collector_index_
112           << (success ? " succeeded." : " failed.");
113 
114   CollectorState* collector = &collectors_[collector_index_];
115   collector_index_ = kInvalidCollectorIndex;
116 
117   if (success) {
118     collector->failures = 0;
119   } else {
120     // Restore upload_pending_ and first_beacon_time_ to pre-upload state,
121     // since upload failed.
122     upload_pending_ = true;
123     first_beacon_time_ = old_first_beacon_time_;
124 
125     ++collector->failures;
126   }
127 
128   base::TimeDelta retry_interval = GetUploadRetryInterval(collector->failures);
129   collector->next_upload = time_->NowTicks() + retry_interval;
130 
131   VLOG(1) << "Next upload to collector at least "
132           << retry_interval.InSeconds() << " seconds from now.";
133 
134   MaybeScheduleUpload();
135 }
136 
CollectorState()137 DomainReliabilityScheduler::CollectorState::CollectorState() : failures(0) {}
138 
MaybeScheduleUpload()139 void DomainReliabilityScheduler::MaybeScheduleUpload() {
140   if (!upload_pending_ || upload_scheduled_ || upload_running_)
141     return;
142 
143   upload_scheduled_ = true;
144   old_first_beacon_time_ = first_beacon_time_;
145 
146   base::TimeTicks now = time_->NowTicks();
147 
148   base::TimeTicks min_by_deadline, max_by_deadline;
149   min_by_deadline = first_beacon_time_ + params_.minimum_upload_delay;
150   max_by_deadline = first_beacon_time_ + params_.maximum_upload_delay;
151   DCHECK(min_by_deadline <= max_by_deadline);
152 
153   base::TimeTicks min_by_backoff;
154   size_t collector_index;
155   GetNextUploadTimeAndCollector(now, &min_by_backoff, &collector_index);
156 
157   base::TimeDelta min_delay = std::max(min_by_deadline, min_by_backoff) - now;
158   base::TimeDelta max_delay = std::max(max_by_deadline, min_by_backoff) - now;
159 
160   VLOG(1) << "Scheduling upload for between " << min_delay.InSeconds()
161           << " and " << max_delay.InSeconds() << " seconds from now.";
162 
163   callback_.Run(min_delay, max_delay);
164 }
165 
166 // TODO(ttuttle): Add min and max interval to config, use that instead.
167 
168 // TODO(ttuttle): Cap min and max intervals received from config.
169 
GetNextUploadTimeAndCollector(base::TimeTicks now,base::TimeTicks * upload_time_out,size_t * collector_index_out)170 void DomainReliabilityScheduler::GetNextUploadTimeAndCollector(
171     base::TimeTicks now,
172     base::TimeTicks* upload_time_out,
173     size_t* collector_index_out) {
174   DCHECK(upload_time_out);
175   DCHECK(collector_index_out);
176 
177   base::TimeTicks min_time;
178   size_t min_index = kInvalidCollectorIndex;
179 
180   for (size_t i = 0; i < collectors_.size(); ++i) {
181     CollectorState* collector = &collectors_[i];
182     // If a collector is usable, use the first one in the list.
183     if (collector->failures == 0 || collector->next_upload <= now) {
184       min_time = now;
185       min_index = i;
186       break;
187     // If not, keep track of which will be usable soonest:
188     } else if (min_index == kInvalidCollectorIndex ||
189         collector->next_upload < min_time) {
190       min_time = collector->next_upload;
191       min_index = i;
192     }
193   }
194 
195   DCHECK_NE(kInvalidCollectorIndex, min_index);
196   *upload_time_out = min_time;
197   *collector_index_out = min_index;
198 }
199 
GetUploadRetryInterval(unsigned failures)200 base::TimeDelta DomainReliabilityScheduler::GetUploadRetryInterval(
201     unsigned failures) {
202   if (failures == 0)
203     return base::TimeDelta::FromSeconds(0);
204   else {
205     // Don't back off more than 64x the original delay.
206     if (failures > 7)
207       failures = 7;
208     return params_.upload_retry_interval * (1 << (failures - 1));
209   }
210 }
211 
212 }  // namespace domain_reliability
213