• 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/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 
LogMetricsInitSequence(InitSequence sequence)55 void LogMetricsInitSequence(InitSequence sequence) {
56   UMA_HISTOGRAM_ENUMERATION("UMA.InitSequence", sequence,
57                             INIT_SEQUENCE_ENUM_SIZE);
58 }
59 
60 }  // anonymous namespace
61 
MetricsReportingScheduler(const base::Closure & upload_callback)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 
~MetricsReportingScheduler()72 MetricsReportingScheduler::~MetricsReportingScheduler() {}
73 
Start()74 void MetricsReportingScheduler::Start() {
75   running_ = true;
76   ScheduleNextUpload();
77 }
78 
Stop()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.
InitTaskComplete()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 
UploadFinished(bool server_is_healthy,bool more_logs_remaining)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 
UploadCancelled()116 void MetricsReportingScheduler::UploadCancelled() {
117   DCHECK(callback_pending_);
118   callback_pending_ = false;
119   if (running_)
120     ScheduleNextUpload();
121 }
122 
SetUploadIntervalForTesting(base::TimeDelta interval)123 void MetricsReportingScheduler::SetUploadIntervalForTesting(
124     base::TimeDelta interval) {
125   upload_interval_ = interval;
126 }
127 
TriggerUpload()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 
ScheduleNextUpload()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 
BackOffUploadInterval()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