Home | History | Annotate | Download | only in domain_reliability
      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
    263