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