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.
5 #include "components/metrics/metrics_reporting_scheduler.h"
7 #include "base/compiler_specific.h"
8 #include "base/metrics/field_trial.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "components/variations/variations_associated_data.h"
13 using base::TimeDelta
;
19 // The delay, in seconds, after startup before sending the first log message.
20 #if defined(OS_ANDROID) || defined(OS_IOS)
21 // Sessions are more likely to be short on a mobile device, so handle the
22 // initial log quickly.
23 const int kInitialUploadIntervalSeconds
= 15;
25 const int kInitialUploadIntervalSeconds
= 60;
28 // The delay, in seconds, between uploading when there are queued logs from
29 // previous sessions to send.
30 #if defined(OS_ANDROID) || defined(OS_IOS)
31 // Sending in a burst is better on a mobile device, since keeping the radio on
33 const int kUnsentLogsIntervalSeconds
= 3;
35 const int kUnsentLogsIntervalSeconds
= 15;
38 // Standard interval between log uploads, in seconds.
39 #if defined(OS_ANDROID) || defined(OS_IOS)
40 const int kStandardUploadIntervalSeconds
= 5 * 60; // Five minutes.
42 const int kStandardUploadIntervalSeconds
= 30 * 60; // Thirty minutes.
45 // When uploading metrics to the server fails, we progressively wait longer and
46 // longer before sending the next log. This backoff process helps reduce load
47 // on a server that is having issues.
48 // The following is the multiplier we use to expand that inter-log duration.
49 const double kBackoffMultiplier
= 1.1;
51 // The maximum backoff multiplier.
52 const int kMaxBackoffMultiplier
= 10;
56 INIT_TASK_COMPLETED_FIRST
,
57 INIT_SEQUENCE_ENUM_SIZE
,
60 void LogMetricsInitSequence(InitSequence sequence
) {
61 UMA_HISTOGRAM_ENUMERATION("UMA.InitSequence", sequence
,
62 INIT_SEQUENCE_ENUM_SIZE
);
65 void LogActualUploadInterval(TimeDelta interval
) {
66 UMA_HISTOGRAM_CUSTOM_COUNTS("UMA.ActualLogUploadInterval",
69 base::TimeDelta::FromHours(12).InMinutes(),
73 // Returns upload interval specified for the current experiment running.
74 // TODO(gayane): Only for experimenting with upload interval for Android
75 // (bug: 17391128). Should be removed once the experiments are done.
76 base::TimeDelta
GetUploadIntervalFromExperiment() {
77 std::string interval_str
= variations::GetVariationParamValue(
78 "UMALogUploadInterval", "interval");
80 if (interval_str
.empty() || !base::StringToInt(interval_str
, &interval
))
81 return TimeDelta::FromSeconds(kStandardUploadIntervalSeconds
);
83 return TimeDelta::FromMinutes(interval
);
86 } // anonymous namespace
88 MetricsReportingScheduler::MetricsReportingScheduler(
89 const base::Closure
& upload_callback
)
90 : upload_callback_(upload_callback
),
91 upload_interval_(TimeDelta::FromSeconds(kInitialUploadIntervalSeconds
)),
93 callback_pending_(false),
94 init_task_complete_(false),
95 waiting_for_init_task_complete_(false) {
98 MetricsReportingScheduler::~MetricsReportingScheduler() {}
100 void MetricsReportingScheduler::Start() {
101 GetUploadIntervalFromExperiment();
103 ScheduleNextUpload();
106 void MetricsReportingScheduler::Stop() {
108 if (upload_timer_
.IsRunning())
109 upload_timer_
.Stop();
112 // Callback from MetricsService when the startup init task has completed.
113 void MetricsReportingScheduler::InitTaskComplete() {
114 DCHECK(!init_task_complete_
);
115 init_task_complete_
= true;
116 if (waiting_for_init_task_complete_
) {
117 waiting_for_init_task_complete_
= false;
120 LogMetricsInitSequence(INIT_TASK_COMPLETED_FIRST
);
124 void MetricsReportingScheduler::UploadFinished(bool server_is_healthy
,
125 bool more_logs_remaining
) {
126 DCHECK(callback_pending_
);
127 callback_pending_
= false;
128 // If the server is having issues, back off. Otherwise, reset to default
129 // (unless there are more logs to send, in which case the next upload should
131 if (!server_is_healthy
) {
132 BackOffUploadInterval();
133 } else if (more_logs_remaining
) {
134 upload_interval_
= TimeDelta::FromSeconds(kUnsentLogsIntervalSeconds
);
136 upload_interval_
= GetStandardUploadInterval();
137 last_upload_finish_time_
= base::TimeTicks::Now();
141 ScheduleNextUpload();
144 void MetricsReportingScheduler::UploadCancelled() {
145 DCHECK(callback_pending_
);
146 callback_pending_
= false;
148 ScheduleNextUpload();
151 void MetricsReportingScheduler::SetUploadIntervalForTesting(
152 base::TimeDelta interval
) {
153 upload_interval_
= interval
;
156 void MetricsReportingScheduler::TriggerUpload() {
157 // If the timer fired before the init task has completed, don't trigger the
158 // upload yet - wait for the init task to complete and do it then.
159 if (!init_task_complete_
) {
160 LogMetricsInitSequence(TIMER_FIRED_FIRST
);
161 waiting_for_init_task_complete_
= true;
165 if (!last_upload_finish_time_
.is_null()) {
166 LogActualUploadInterval(base::TimeTicks::Now() - last_upload_finish_time_
);
167 last_upload_finish_time_
= base::TimeTicks();
170 callback_pending_
= true;
171 upload_callback_
.Run();
174 void MetricsReportingScheduler::ScheduleNextUpload() {
176 if (upload_timer_
.IsRunning() || callback_pending_
)
179 upload_timer_
.Start(FROM_HERE
, upload_interval_
, this,
180 &MetricsReportingScheduler::TriggerUpload
);
183 void MetricsReportingScheduler::BackOffUploadInterval() {
184 DCHECK_GT(kBackoffMultiplier
, 1.0);
185 upload_interval_
= TimeDelta::FromMicroseconds(
186 static_cast<int64
>(kBackoffMultiplier
*
187 upload_interval_
.InMicroseconds()));
189 TimeDelta max_interval
= kMaxBackoffMultiplier
* GetStandardUploadInterval();
190 if (upload_interval_
> max_interval
|| upload_interval_
.InSeconds() < 0) {
191 upload_interval_
= max_interval
;
195 base::TimeDelta
MetricsReportingScheduler::GetStandardUploadInterval() {
196 #if defined(OS_ANDROID)
197 return GetUploadIntervalFromExperiment();
199 return TimeDelta::FromSeconds(kStandardUploadIntervalSeconds
);
203 } // namespace metrics