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 10using base::TimeDelta; 11 12namespace metrics { 13 14namespace { 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. 20const int kInitialUploadIntervalSeconds = 15; 21#else 22const 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. 30const int kUnsentLogsIntervalSeconds = 3; 31#else 32const int kUnsentLogsIntervalSeconds = 15; 33#endif 34 35// Standard interval between log uploads, in seconds. 36#if defined(OS_ANDROID) || defined(OS_IOS) 37const int kStandardUploadIntervalSeconds = 5 * 60; // Five minutes. 38#else 39const 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. 46const double kBackoffMultiplier = 1.1; 47 48// The maximum backoff multiplier. 49const int kMaxBackoffMultiplier = 10; 50 51enum InitSequence { 52 TIMER_FIRED_FIRST, 53 INIT_TASK_COMPLETED_FIRST, 54 INIT_SEQUENCE_ENUM_SIZE, 55}; 56 57void LogMetricsInitSequence(InitSequence sequence) { 58 UMA_HISTOGRAM_ENUMERATION("UMA.InitSequence", sequence, 59 INIT_SEQUENCE_ENUM_SIZE); 60} 61 62} // anonymous namespace 63 64MetricsReportingScheduler::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 74MetricsReportingScheduler::~MetricsReportingScheduler() {} 75 76void MetricsReportingScheduler::Start() { 77 running_ = true; 78 ScheduleNextUpload(); 79} 80 81void 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. 88void 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 99void 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 118void MetricsReportingScheduler::UploadCancelled() { 119 DCHECK(callback_pending_); 120 callback_pending_ = false; 121 if (running_) 122 ScheduleNextUpload(); 123} 124 125void MetricsReportingScheduler::SetUploadIntervalForTesting( 126 base::TimeDelta interval) { 127 upload_interval_ = interval; 128} 129 130void 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 142void 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 151void 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