Home | History | Annotate | Download | only in metrics
      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/metrics/metrics_reporting_scheduler.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/metrics/histogram.h"
      9 
     10 using base::TimeDelta;
     11 
     12 namespace metrics {
     13 
     14 namespace {
     15 
     16 // The delay, in seconds, after startup before sending the first log message.
     17 #if defined(OS_ANDROID) || defined(OS_IOS)
     18 // Sessions are more likely to be short on a mobile device, so handle the
     19 // initial log quickly.
     20 const int kInitialUploadIntervalSeconds = 15;
     21 #else
     22 const int kInitialUploadIntervalSeconds = 60;
     23 #endif
     24 
     25 // The delay, in seconds, between uploading when there are queued logs from
     26 // previous sessions to send.
     27 #if defined(OS_ANDROID) || defined(OS_IOS)
     28 // Sending in a burst is better on a mobile device, since keeping the radio on
     29 // is very expensive.
     30 const int kUnsentLogsIntervalSeconds = 3;
     31 #else
     32 const int kUnsentLogsIntervalSeconds = 15;
     33 #endif
     34 
     35 // Standard interval between log uploads, in seconds.
     36 #if defined(OS_ANDROID) || defined(OS_IOS)
     37 const int kStandardUploadIntervalSeconds = 5 * 60;  // Five minutes.
     38 #else
     39 const int kStandardUploadIntervalSeconds = 30 * 60;  // Thirty minutes.
     40 #endif
     41 
     42 // When uploading metrics to the server fails, we progressively wait longer and
     43 // longer before sending the next log. This backoff process helps reduce load
     44 // on a server that is having issues.
     45 // The following is the multiplier we use to expand that inter-log duration.
     46 const double kBackoffMultiplier = 1.1;
     47 
     48 // The maximum backoff multiplier.
     49 const int kMaxBackoffMultiplier = 10;
     50 
     51 enum InitSequence {
     52   TIMER_FIRED_FIRST,
     53   INIT_TASK_COMPLETED_FIRST,
     54   INIT_SEQUENCE_ENUM_SIZE,
     55 };
     56 
     57 void LogMetricsInitSequence(InitSequence sequence) {
     58   UMA_HISTOGRAM_ENUMERATION("UMA.InitSequence", sequence,
     59                             INIT_SEQUENCE_ENUM_SIZE);
     60 }
     61 
     62 }  // anonymous namespace
     63 
     64 MetricsReportingScheduler::MetricsReportingScheduler(
     65     const base::Closure& upload_callback)
     66     : upload_callback_(upload_callback),
     67       upload_interval_(TimeDelta::FromSeconds(kInitialUploadIntervalSeconds)),
     68       running_(false),
     69       callback_pending_(false),
     70       init_task_complete_(false),
     71       waiting_for_init_task_complete_(false) {
     72 }
     73 
     74 MetricsReportingScheduler::~MetricsReportingScheduler() {}
     75 
     76 void MetricsReportingScheduler::Start() {
     77   running_ = true;
     78   ScheduleNextUpload();
     79 }
     80 
     81 void MetricsReportingScheduler::Stop() {
     82   running_ = false;
     83   if (upload_timer_.IsRunning())
     84     upload_timer_.Stop();
     85 }
     86 
     87 // Callback from MetricsService when the startup init task has completed.
     88 void MetricsReportingScheduler::InitTaskComplete() {
     89   DCHECK(!init_task_complete_);
     90   init_task_complete_ = true;
     91   if (waiting_for_init_task_complete_) {
     92     waiting_for_init_task_complete_ = false;
     93     TriggerUpload();
     94   } else {
     95     LogMetricsInitSequence(INIT_TASK_COMPLETED_FIRST);
     96   }
     97 }
     98 
     99 void MetricsReportingScheduler::UploadFinished(bool server_is_healthy,
    100                                                bool more_logs_remaining) {
    101   DCHECK(callback_pending_);
    102   callback_pending_ = false;
    103   // If the server is having issues, back off. Otherwise, reset to default
    104   // (unless there are more logs to send, in which case the next upload should
    105   // happen sooner).
    106   if (!server_is_healthy) {
    107     BackOffUploadInterval();
    108   } else if (more_logs_remaining) {
    109     upload_interval_ = TimeDelta::FromSeconds(kUnsentLogsIntervalSeconds);
    110   } else {
    111     upload_interval_ = TimeDelta::FromSeconds(kStandardUploadIntervalSeconds);
    112   }
    113 
    114   if (running_)
    115     ScheduleNextUpload();
    116 }
    117 
    118 void MetricsReportingScheduler::UploadCancelled() {
    119   DCHECK(callback_pending_);
    120   callback_pending_ = false;
    121   if (running_)
    122     ScheduleNextUpload();
    123 }
    124 
    125 void MetricsReportingScheduler::SetUploadIntervalForTesting(
    126     base::TimeDelta interval) {
    127   upload_interval_ = interval;
    128 }
    129 
    130 void MetricsReportingScheduler::TriggerUpload() {
    131   // If the timer fired before the init task has completed, don't trigger the
    132   // upload yet - wait for the init task to complete and do it then.
    133   if (!init_task_complete_) {
    134     LogMetricsInitSequence(TIMER_FIRED_FIRST);
    135     waiting_for_init_task_complete_ = true;
    136     return;
    137   }
    138   callback_pending_ = true;
    139   upload_callback_.Run();
    140 }
    141 
    142 void MetricsReportingScheduler::ScheduleNextUpload() {
    143   DCHECK(running_);
    144   if (upload_timer_.IsRunning() || callback_pending_)
    145     return;
    146 
    147   upload_timer_.Start(FROM_HERE, upload_interval_, this,
    148                       &MetricsReportingScheduler::TriggerUpload);
    149 }
    150 
    151 void MetricsReportingScheduler::BackOffUploadInterval() {
    152   DCHECK_GT(kBackoffMultiplier, 1.0);
    153   upload_interval_ = TimeDelta::FromMicroseconds(
    154       static_cast<int64>(kBackoffMultiplier *
    155                          upload_interval_.InMicroseconds()));
    156 
    157   TimeDelta max_interval = kMaxBackoffMultiplier *
    158       TimeDelta::FromSeconds(kStandardUploadIntervalSeconds);
    159   if (upload_interval_ > max_interval || upload_interval_.InSeconds() < 0) {
    160     upload_interval_ = max_interval;
    161   }
    162 }
    163 
    164 }  // namespace metrics
    165