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