diff options
author | motek@chromium.org <motek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-20 17:16:45 +0000 |
---|---|---|
committer | motek@chromium.org <motek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-20 17:16:45 +0000 |
commit | 14bb46691011873fc4d7f63951ea521fb521aa6a (patch) | |
tree | dc661631095dd855d76da80a111fcc749740386a /components | |
parent | a90c8cae340737062a8ab03af4667a55ebac103c (diff) | |
download | chromium_src-14bb46691011873fc4d7f63951ea521fb521aa6a.zip chromium_src-14bb46691011873fc4d7f63951ea521fb521aa6a.tar.gz chromium_src-14bb46691011873fc4d7f63951ea521fb521aa6a.tar.bz2 |
Moved metrics_reporting_scheduler* to //components/metrics.
Automove with some manual fixes.
BUG=374214
Review URL: https://codereview.chromium.org/294693002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271676 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components')
-rw-r--r-- | components/components_tests.gyp | 1 | ||||
-rw-r--r-- | components/metrics.gypi | 2 | ||||
-rw-r--r-- | components/metrics/metrics_reporting_scheduler.cc | 160 | ||||
-rw-r--r-- | components/metrics/metrics_reporting_scheduler.h | 78 | ||||
-rw-r--r-- | components/metrics/metrics_reporting_scheduler_unittest.cc | 58 |
5 files changed, 299 insertions, 0 deletions
diff --git a/components/components_tests.gyp b/components/components_tests.gyp index 6f2e098..64cf12b 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -107,6 +107,7 @@ 'metrics/metrics_hashes_unittest.cc', 'metrics/metrics_log_base_unittest.cc', 'metrics/metrics_log_manager_unittest.cc', + 'metrics/metrics_reporting_scheduler_unittest.cc', 'metrics/persisted_logs_unittest.cc', 'navigation_interception/intercept_navigation_resource_throttle_unittest.cc', 'os_crypt/ie7_password_win_unittest.cc', diff --git a/components/metrics.gypi b/components/metrics.gypi index f7fdc23..a572cda 100644 --- a/components/metrics.gypi +++ b/components/metrics.gypi @@ -24,6 +24,8 @@ 'metrics/metrics_log_manager.h', 'metrics/metrics_pref_names.cc', 'metrics/metrics_pref_names.h', + 'metrics/metrics_reporting_scheduler.cc', + 'metrics/metrics_reporting_scheduler.h', 'metrics/metrics_service_client.h', 'metrics/metrics_service_observer.cc', 'metrics/metrics_service_observer.h', diff --git a/components/metrics/metrics_reporting_scheduler.cc b/components/metrics/metrics_reporting_scheduler.cc new file mode 100644 index 0000000..4b67271b2 --- /dev/null +++ b/components/metrics/metrics_reporting_scheduler.cc @@ -0,0 +1,160 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/metrics/metrics_reporting_scheduler.h" + +#include "base/compiler_specific.h" +#include "base/metrics/histogram.h" + +using base::TimeDelta; + +namespace { + +// The delay, in seconds, after startup before sending the first log message. +#if defined(OS_ANDROID) || defined(OS_IOS) +// Sessions are more likely to be short on a mobile device, so handle the +// initial log quickly. +const int kInitialUploadIntervalSeconds = 15; +#else +const int kInitialUploadIntervalSeconds = 60; +#endif + +// The delay, in seconds, between uploading when there are queued logs from +// previous sessions to send. +#if defined(OS_ANDROID) || defined(OS_IOS) +// Sending in a burst is better on a mobile device, since keeping the radio on +// is very expensive. +const int kUnsentLogsIntervalSeconds = 3; +#else +const int kUnsentLogsIntervalSeconds = 15; +#endif + +// Standard interval between log uploads, in seconds. +#if defined(OS_ANDROID) || defined(OS_IOS) +const int kStandardUploadIntervalSeconds = 5 * 60; // Five minutes. +#else +const int kStandardUploadIntervalSeconds = 30 * 60; // Thirty minutes. +#endif + +// When uploading metrics to the server fails, we progressively wait longer and +// longer before sending the next log. This backoff process helps reduce load +// on a server that is having issues. +// The following is the multiplier we use to expand that inter-log duration. +const double kBackoffMultiplier = 1.1; + +// The maximum backoff multiplier. +const int kMaxBackoffMultiplier = 10; + +enum InitSequence { + TIMER_FIRED_FIRST, + INIT_TASK_COMPLETED_FIRST, + INIT_SEQUENCE_ENUM_SIZE, +}; + +void LogMetricsInitSequence(InitSequence sequence) { + UMA_HISTOGRAM_ENUMERATION("UMA.InitSequence", sequence, + INIT_SEQUENCE_ENUM_SIZE); +} + +} // anonymous namespace + +MetricsReportingScheduler::MetricsReportingScheduler( + const base::Closure& upload_callback) + : upload_callback_(upload_callback), + upload_interval_(TimeDelta::FromSeconds(kInitialUploadIntervalSeconds)), + running_(false), + callback_pending_(false), + init_task_complete_(false), + waiting_for_init_task_complete_(false) { +} + +MetricsReportingScheduler::~MetricsReportingScheduler() {} + +void MetricsReportingScheduler::Start() { + running_ = true; + ScheduleNextUpload(); +} + +void MetricsReportingScheduler::Stop() { + running_ = false; + if (upload_timer_.IsRunning()) + upload_timer_.Stop(); +} + +// Callback from MetricsService when the startup init task has completed. +void MetricsReportingScheduler::InitTaskComplete() { + DCHECK(!init_task_complete_); + init_task_complete_ = true; + if (waiting_for_init_task_complete_) { + waiting_for_init_task_complete_ = false; + TriggerUpload(); + } else { + LogMetricsInitSequence(INIT_TASK_COMPLETED_FIRST); + } +} + +void MetricsReportingScheduler::UploadFinished(bool server_is_healthy, + bool more_logs_remaining) { + DCHECK(callback_pending_); + callback_pending_ = false; + // If the server is having issues, back off. Otherwise, reset to default + // (unless there are more logs to send, in which case the next upload should + // happen sooner). + if (!server_is_healthy) { + BackOffUploadInterval(); + } else if (more_logs_remaining) { + upload_interval_ = TimeDelta::FromSeconds(kUnsentLogsIntervalSeconds); + } else { + upload_interval_ = TimeDelta::FromSeconds(kStandardUploadIntervalSeconds); + } + + if (running_) + ScheduleNextUpload(); +} + +void MetricsReportingScheduler::UploadCancelled() { + DCHECK(callback_pending_); + callback_pending_ = false; + if (running_) + ScheduleNextUpload(); +} + +void MetricsReportingScheduler::SetUploadIntervalForTesting( + base::TimeDelta interval) { + upload_interval_ = interval; +} + +void MetricsReportingScheduler::TriggerUpload() { + // If the timer fired before the init task has completed, don't trigger the + // upload yet - wait for the init task to complete and do it then. + if (!init_task_complete_) { + LogMetricsInitSequence(TIMER_FIRED_FIRST); + waiting_for_init_task_complete_ = true; + return; + } + callback_pending_ = true; + upload_callback_.Run(); +} + +void MetricsReportingScheduler::ScheduleNextUpload() { + DCHECK(running_); + if (upload_timer_.IsRunning() || callback_pending_) + return; + + upload_timer_.Start(FROM_HERE, upload_interval_, this, + &MetricsReportingScheduler::TriggerUpload); +} + +void MetricsReportingScheduler::BackOffUploadInterval() { + DCHECK_GT(kBackoffMultiplier, 1.0); + upload_interval_ = TimeDelta::FromMicroseconds( + static_cast<int64>(kBackoffMultiplier * + upload_interval_.InMicroseconds())); + + TimeDelta max_interval = kMaxBackoffMultiplier * + TimeDelta::FromSeconds(kStandardUploadIntervalSeconds); + if (upload_interval_ > max_interval || upload_interval_.InSeconds() < 0) { + upload_interval_ = max_interval; + } +} diff --git a/components/metrics/metrics_reporting_scheduler.h b/components/metrics/metrics_reporting_scheduler.h new file mode 100644 index 0000000..7cdfad8 --- /dev/null +++ b/components/metrics/metrics_reporting_scheduler.h @@ -0,0 +1,78 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_METRICS_METRICS_REPORTING_SCHEDULER_H_ +#define COMPONENTS_METRICS_METRICS_REPORTING_SCHEDULER_H_ + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "base/timer/timer.h" + +// Scheduler task to drive a MetricsService object's uploading. +class MetricsReportingScheduler { + public: + explicit MetricsReportingScheduler(const base::Closure& upload_callback); + ~MetricsReportingScheduler(); + + // Starts scheduling uploads. This in a no-op if the scheduler is already + // running, so it is safe to call more than once. + void Start(); + + // Stops scheduling uploads. + void Stop(); + + // Callback from MetricsService when the startup init task has completed. + void InitTaskComplete(); + + // Callback from MetricsService when a triggered upload finishes. + void UploadFinished(bool server_is_healthy, bool more_logs_remaining); + + // Callback from MetricsService when a triggered upload is cancelled by the + // MetricsService. + void UploadCancelled(); + + // Sets the upload interval to a specific value, exposed for unit tests. + void SetUploadIntervalForTesting(base::TimeDelta interval); + + private: + // Timer callback indicating it's time for the MetricsService to upload + // metrics. + void TriggerUpload(); + + // Schedules a future call to TriggerUpload if one isn't already pending. + void ScheduleNextUpload(); + + // Increases the upload interval each time it's called, to handle the case + // where the server is having issues. + void BackOffUploadInterval(); + + // The MetricsService method to call when uploading should happen. + const base::Closure upload_callback_; + + base::OneShotTimer<MetricsReportingScheduler> upload_timer_; + + // The interval between being told an upload is done and starting the next + // upload. + base::TimeDelta upload_interval_; + + // Indicates that the scheduler is running (i.e., that Start has been called + // more recently than Stop). + bool running_; + + // Indicates that the last triggered upload hasn't resolved yet. + bool callback_pending_; + + // Whether the InitTaskComplete() callback has been called. + bool init_task_complete_; + + // Whether the initial scheduled upload timer has fired before the init task + // has been completed. + bool waiting_for_init_task_complete_; + + DISALLOW_COPY_AND_ASSIGN(MetricsReportingScheduler); +}; + +#endif // COMPONENTS_METRICS_METRICS_REPORTING_SCHEDULER_H_ diff --git a/components/metrics/metrics_reporting_scheduler_unittest.cc b/components/metrics/metrics_reporting_scheduler_unittest.cc new file mode 100644 index 0000000..f7a3d081 --- /dev/null +++ b/components/metrics/metrics_reporting_scheduler_unittest.cc @@ -0,0 +1,58 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/metrics/metrics_reporting_scheduler.h" + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "testing/gtest/include/gtest/gtest.h" + +class MetricsReportingSchedulerTest : public testing::Test { + public: + MetricsReportingSchedulerTest() : callback_call_count_(0) {} + virtual ~MetricsReportingSchedulerTest() {} + + base::Closure GetCallback() { + return base::Bind(&MetricsReportingSchedulerTest::SchedulerCallback, + base::Unretained(this)); + } + + int callback_call_count() const { return callback_call_count_; } + + private: + void SchedulerCallback() { + ++callback_call_count_; + } + + int callback_call_count_; + + base::MessageLoopForUI message_loop_; + + DISALLOW_COPY_AND_ASSIGN(MetricsReportingSchedulerTest); +}; + + +TEST_F(MetricsReportingSchedulerTest, InitTaskCompleteBeforeTimer) { + MetricsReportingScheduler scheduler(GetCallback()); + scheduler.SetUploadIntervalForTesting(base::TimeDelta()); + scheduler.InitTaskComplete(); + scheduler.Start(); + EXPECT_EQ(0, callback_call_count()); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, callback_call_count()); +} + +TEST_F(MetricsReportingSchedulerTest, InitTaskCompleteAfterTimer) { + MetricsReportingScheduler scheduler(GetCallback()); + scheduler.SetUploadIntervalForTesting(base::TimeDelta()); + scheduler.Start(); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, callback_call_count()); + + scheduler.InitTaskComplete(); + EXPECT_EQ(1, callback_call_count()); +} |