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