diff options
-rw-r--r-- | net/base/backoff_entry.cc | 20 | ||||
-rw-r--r-- | net/base/backoff_entry.h | 34 | ||||
-rw-r--r-- | net/base/backoff_entry_unittest.cc | 94 | ||||
-rw-r--r-- | net/url_request/url_request_throttler_entry.cc | 11 | ||||
-rw-r--r-- | net/url_request/url_request_throttler_entry.h | 2 | ||||
-rw-r--r-- | remoting/host/chromoting_host.cc | 3 |
6 files changed, 144 insertions, 20 deletions
diff --git a/net/base/backoff_entry.cc b/net/base/backoff_entry.cc index f00dd40..b1826b7 100644 --- a/net/base/backoff_entry.cc +++ b/net/base/backoff_entry.cc @@ -47,8 +47,11 @@ void BackoffEntry::InformOfRequest(bool succeeded) { // those failures will not reset the release time, further // requests will then need to wait the delay caused by the 2 // failures. + base::TimeDelta delay; + if (policy_->always_use_initial_delay) + delay = base::TimeDelta::FromMilliseconds(policy_->initial_delay_ms); exponential_backoff_release_time_ = std::max( - ImplGetTimeNow(), exponential_backoff_release_time_); + ImplGetTimeNow() + delay, exponential_backoff_release_time_); } } @@ -56,6 +59,13 @@ bool BackoffEntry::ShouldRejectRequest() const { return exponential_backoff_release_time_ > ImplGetTimeNow(); } +base::TimeDelta BackoffEntry::GetTimeUntilRelease() const { + base::TimeTicks now = ImplGetTimeNow(); + if (exponential_backoff_release_time_ <= now) + return base::TimeDelta(); + return exponential_backoff_release_time_ - now; +} + base::TimeTicks BackoffEntry::GetReleaseTime() const { return exponential_backoff_release_time_; } @@ -107,6 +117,12 @@ base::TimeTicks BackoffEntry::ImplGetTimeNow() const { base::TimeTicks BackoffEntry::CalculateReleaseTime() const { int effective_failure_count = std::max(0, failure_count_ - policy_->num_errors_to_ignore); + + // If always_use_initial_delay is true, it's equivalent to + // the effective_failure_count always being one greater than when it's false. + if (policy_->always_use_initial_delay) + ++effective_failure_count; + if (effective_failure_count == 0) { // Never reduce previously set release horizon, e.g. due to Retry-After // header. @@ -116,7 +132,7 @@ base::TimeTicks BackoffEntry::CalculateReleaseTime() const { // The delay is calculated with this formula: // delay = initial_backoff * multiply_factor^( // effective_failure_count - 1) * Uniform(1 - jitter_factor, 1] - double delay = policy_->initial_backoff_ms; + double delay = policy_->initial_delay_ms; delay *= pow(policy_->multiply_factor, effective_failure_count - 1); delay -= base::RandDouble() * policy_->jitter_factor * delay; diff --git a/net/base/backoff_entry.h b/net/base/backoff_entry.h index ce280a4..4827278 100644 --- a/net/base/backoff_entry.h +++ b/net/base/backoff_entry.h @@ -1,9 +1,9 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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 NET_BASE_BACKOFF_ITEM_H_ -#define NET_BASE_BACKOFF_ITEM_H_ +#ifndef NET_BASE_BACKOFF_ENTRY_H_ +#define NET_BASE_BACKOFF_ENTRY_H_ #pragma once #include "base/threading/non_thread_safe.h" @@ -17,8 +17,7 @@ namespace net { // // This utility class knows nothing about network specifics; it is // intended for reuse in various networking scenarios. -class NET_EXPORT_PRIVATE BackoffEntry - : NON_EXPORTED_BASE(public base::NonThreadSafe) { +class NET_EXPORT BackoffEntry : NON_EXPORTED_BASE(public base::NonThreadSafe) { public: // The set of parameters that define a back-off policy. struct Policy { @@ -26,8 +25,11 @@ class NET_EXPORT_PRIVATE BackoffEntry // exponential back-off rules. int num_errors_to_ignore; - // Initial delay for exponential back-off. - int initial_backoff_ms; + // Initial delay. The interpretation of this value depends on + // always_use_initial_delay. It's either how long we wait between + // requests before backoff starts, or how much we delay the first request + // after backoff starts. + int initial_delay_ms; // Factor by which the waiting time will be multiplied. double multiply_factor; @@ -43,6 +45,15 @@ class NET_EXPORT_PRIVATE BackoffEntry // Time to keep an entry from being discarded even when it // has no significant state, -1 to never discard. int64 entry_lifetime_ms; + + // If true, we always use a delay of initial_delay_ms, even before + // we've seen num_errors_to_ignore errors. Otherwise, initial_delay_ms + // is the first delay once we start exponential backoff. + // + // So if we're ignoring 1 error, we'll see (N, N, Nm, Nm^2, ...) if true, + // and (0, 0, N, Nm, ...) when false, where N is initial_backoff_ms and + // m is multiply_factor, assuming we've already seen one success. + bool always_use_initial_delay; }; // Lifetime of policy must enclose lifetime of BackoffEntry. The @@ -62,13 +73,16 @@ class NET_EXPORT_PRIVATE BackoffEntry // state) will no longer reject requests. base::TimeTicks GetReleaseTime() const; + // Returns the time until a request can be sent. + base::TimeDelta GetTimeUntilRelease() const; + // Causes this object reject requests until the specified absolute time. // This can be used to e.g. implement support for a Retry-After header. void SetCustomReleaseTime(const base::TimeTicks& release_time); // Returns true if this object has no significant state (i.e. you could // just as well start with a fresh BackoffEntry object), and hasn't - // had for Policy::entry_lifetime_ms_. + // had for Policy::entry_lifetime_ms. bool CanDiscard() const; // Resets this entry to a fresh (as if just constructed) state. @@ -89,7 +103,7 @@ class NET_EXPORT_PRIVATE BackoffEntry // allowed to start sending requests again. base::TimeTicks exponential_backoff_release_time_; - // Counts request errors; reset on success. + // Counts request errors; decremented on success. int failure_count_; const Policy* const policy_; @@ -99,4 +113,4 @@ class NET_EXPORT_PRIVATE BackoffEntry } // namespace net -#endif // NET_BASE_BACKOFF_ITEM_H_ +#endif // NET_BASE_BACKOFF_ENTRY_H_ diff --git a/net/base/backoff_entry_unittest.cc b/net/base/backoff_entry_unittest.cc index 320c9e3..9a9f4cf 100644 --- a/net/base/backoff_entry_unittest.cc +++ b/net/base/backoff_entry_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -11,7 +11,7 @@ using base::TimeDelta; using base::TimeTicks; using net::BackoffEntry; -BackoffEntry::Policy base_policy = { 0, 1000, 2.0, 0.0, 20000, 2000 }; +BackoffEntry::Policy base_policy = { 0, 1000, 2.0, 0.0, 20000, 2000, false }; class TestBackoffEntry : public BackoffEntry { public: @@ -42,9 +42,11 @@ class TestBackoffEntry : public BackoffEntry { TEST(BackoffEntryTest, BaseTest) { TestBackoffEntry entry(&base_policy); EXPECT_FALSE(entry.ShouldRejectRequest()); + EXPECT_EQ(TimeDelta(), entry.GetTimeUntilRelease()); entry.InformOfRequest(false); EXPECT_TRUE(entry.ShouldRejectRequest()); + EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry.GetTimeUntilRelease()); } TEST(BackoffEntryTest, CanDiscardNeverExpires) { @@ -83,6 +85,26 @@ TEST(BackoffEntryTest, CanDiscard) { EXPECT_TRUE(entry.CanDiscard()); } +TEST(BackoffEntryTest, CanDiscardAlwaysDelay) { + BackoffEntry::Policy always_delay_policy = base_policy; + always_delay_policy.always_use_initial_delay = true; + always_delay_policy.entry_lifetime_ms = 0; + + TestBackoffEntry entry(&always_delay_policy); + + // Because lifetime is non-zero, we shouldn't be able to discard yet. + entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(2000)); + EXPECT_TRUE(entry.CanDiscard()); + + // Even with no failures, we wait until the delay before we allow discard. + entry.InformOfRequest(true); + EXPECT_FALSE(entry.CanDiscard()); + + // Wait until the delay expires, and we can discard the entry again. + entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(1000)); + EXPECT_TRUE(entry.CanDiscard()); +} + TEST(BackoffEntryTest, CanDiscardNotStored) { BackoffEntry::Policy no_store_policy = base_policy; no_store_policy.entry_lifetime_ms = 0; @@ -95,10 +117,13 @@ TEST(BackoffEntryTest, ShouldIgnoreFirstTwo) { lenient_policy.num_errors_to_ignore = 2; BackoffEntry entry(&lenient_policy); + entry.InformOfRequest(false); EXPECT_FALSE(entry.ShouldRejectRequest()); + entry.InformOfRequest(false); EXPECT_FALSE(entry.ShouldRejectRequest()); + entry.InformOfRequest(false); EXPECT_TRUE(entry.ShouldRejectRequest()); } @@ -114,16 +139,19 @@ TEST(BackoffEntryTest, ReleaseTimeCalculation) { entry.InformOfRequest(false); result = entry.GetReleaseTime(); EXPECT_EQ(entry.ImplGetTimeNow() + TimeDelta::FromMilliseconds(1000), result); + EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry.GetTimeUntilRelease()); // 2 errors. entry.InformOfRequest(false); result = entry.GetReleaseTime(); EXPECT_EQ(entry.ImplGetTimeNow() + TimeDelta::FromMilliseconds(2000), result); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), entry.GetTimeUntilRelease()); // 3 errors. entry.InformOfRequest(false); result = entry.GetReleaseTime(); EXPECT_EQ(entry.ImplGetTimeNow() + TimeDelta::FromMilliseconds(4000), result); + EXPECT_EQ(TimeDelta::FromMilliseconds(4000), entry.GetTimeUntilRelease()); // 6 errors (to check it doesn't pass maximum). entry.InformOfRequest(false); @@ -134,6 +162,42 @@ TEST(BackoffEntryTest, ReleaseTimeCalculation) { entry.ImplGetTimeNow() + TimeDelta::FromMilliseconds(20000), result); } +TEST(BackoffEntryTest, ReleaseTimeCalculationAlwaysDelay) { + BackoffEntry::Policy always_delay_policy = base_policy; + always_delay_policy.always_use_initial_delay = true; + always_delay_policy.num_errors_to_ignore = 2; + + TestBackoffEntry entry(&always_delay_policy); + + // With previous requests, should return "now". + TimeTicks result = entry.GetReleaseTime(); + EXPECT_EQ(TimeDelta(), entry.GetTimeUntilRelease()); + + // 1 error. + entry.InformOfRequest(false); + EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry.GetTimeUntilRelease()); + + // 2 errors. + entry.InformOfRequest(false); + EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry.GetTimeUntilRelease()); + + // 3 errors, exponential backoff starts. + entry.InformOfRequest(false); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), entry.GetTimeUntilRelease()); + + // 4 errors. + entry.InformOfRequest(false); + EXPECT_EQ(TimeDelta::FromMilliseconds(4000), entry.GetTimeUntilRelease()); + + // 8 errors (to check it doesn't pass maximum). + entry.InformOfRequest(false); + entry.InformOfRequest(false); + entry.InformOfRequest(false); + entry.InformOfRequest(false); + result = entry.GetReleaseTime(); + EXPECT_EQ(TimeDelta::FromMilliseconds(20000), entry.GetTimeUntilRelease()); +} + TEST(BackoffEntryTest, ReleaseTimeCalculationWithJitter) { for (int i = 0; i < 10; ++i) { BackoffEntry::Policy jittery_policy = base_policy; @@ -172,6 +236,32 @@ TEST(BackoffEntryTest, FailureThenSuccess) { entry.GetReleaseTime()); } +TEST(BackoffEntryTest, FailureThenSuccessAlwaysDelay) { + BackoffEntry::Policy always_delay_policy = base_policy; + always_delay_policy.always_use_initial_delay = true; + always_delay_policy.num_errors_to_ignore = 1; + + TestBackoffEntry entry(&always_delay_policy); + + // Failure count 1. + entry.InformOfRequest(false); + EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry.GetTimeUntilRelease()); + + // Failure count 2. + entry.InformOfRequest(false); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), entry.GetTimeUntilRelease()); + entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(2000)); + + // Success. We should go back to the original delay. + entry.InformOfRequest(true); + EXPECT_EQ(TimeDelta::FromMilliseconds(1000), entry.GetTimeUntilRelease()); + + // Failure count reaches 2 again. We should increase the delay once more. + entry.InformOfRequest(false); + EXPECT_EQ(TimeDelta::FromMilliseconds(2000), entry.GetTimeUntilRelease()); + entry.set_now(entry.GetReleaseTime() + TimeDelta::FromMilliseconds(2000)); +} + TEST(BackoffEntryTest, RetainCustomHorizon) { TestBackoffEntry custom(&base_policy); TimeTicks custom_horizon = TimeTicks() + TimeDelta::FromDays(3); diff --git a/net/url_request/url_request_throttler_entry.cc b/net/url_request/url_request_throttler_entry.cc index 3154f9c..ece2b8b 100644 --- a/net/url_request/url_request_throttler_entry.cc +++ b/net/url_request/url_request_throttler_entry.cc @@ -39,7 +39,7 @@ const int URLRequestThrottlerEntry::kDefaultMaxSendThreshold = 20; // avoid false positives. It should help avoid back-off from kicking in e.g. // on flaky connections. const int URLRequestThrottlerEntry::kDefaultNumErrorsToIgnore = 2; -const int URLRequestThrottlerEntry::kDefaultInitialBackoffMs = 700; +const int URLRequestThrottlerEntry::kDefaultInitialDelayMs = 700; const double URLRequestThrottlerEntry::kDefaultMultiplyFactor = 1.4; const double URLRequestThrottlerEntry::kDefaultJitterFactor = 0.4; const int URLRequestThrottlerEntry::kDefaultMaximumBackoffMs = 15 * 60 * 1000; @@ -116,12 +116,13 @@ URLRequestThrottlerEntry::URLRequestThrottlerEntry( DCHECK(manager_); Initialize(); - backoff_policy_.initial_backoff_ms = initial_backoff_ms; + backoff_policy_.initial_delay_ms = initial_backoff_ms; backoff_policy_.multiply_factor = multiply_factor; backoff_policy_.jitter_factor = jitter_factor; backoff_policy_.maximum_backoff_ms = maximum_backoff_ms; backoff_policy_.entry_lifetime_ms = -1; backoff_policy_.num_errors_to_ignore = 0; + backoff_policy_.always_use_initial_delay = false; } bool URLRequestThrottlerEntry::IsEntryOutdated() const { @@ -163,8 +164,7 @@ bool URLRequestThrottlerEntry::ShouldRejectRequest(int load_flags) const { GetBackoffEntry()->ShouldRejectRequest()) { int num_failures = GetBackoffEntry()->failure_count(); int release_after_ms = - (GetBackoffEntry()->GetReleaseTime() - base::TimeTicks::Now()) - .InMilliseconds(); + GetBackoffEntry()->GetTimeUntilRelease().InMilliseconds(); net_log_.AddEvent( NetLog::TYPE_THROTTLING_REJECTED_REQUEST, @@ -271,11 +271,12 @@ URLRequestThrottlerEntry::~URLRequestThrottlerEntry() { void URLRequestThrottlerEntry::Initialize() { sliding_window_release_time_ = base::TimeTicks::Now(); backoff_policy_.num_errors_to_ignore = kDefaultNumErrorsToIgnore; - backoff_policy_.initial_backoff_ms = kDefaultInitialBackoffMs; + backoff_policy_.initial_delay_ms = kDefaultInitialDelayMs; backoff_policy_.multiply_factor = kDefaultMultiplyFactor; backoff_policy_.jitter_factor = kDefaultJitterFactor; backoff_policy_.maximum_backoff_ms = kDefaultMaximumBackoffMs; backoff_policy_.entry_lifetime_ms = kDefaultEntryLifetimeMs; + backoff_policy_.always_use_initial_delay = false; // We pretend we just had a successful response so that we have a // starting point to our tracking. This is called from the diff --git a/net/url_request/url_request_throttler_entry.h b/net/url_request/url_request_throttler_entry.h index 5c73c85..3ebd5c9 100644 --- a/net/url_request/url_request_throttler_entry.h +++ b/net/url_request/url_request_throttler_entry.h @@ -43,7 +43,7 @@ class NET_EXPORT URLRequestThrottlerEntry static const int kDefaultNumErrorsToIgnore; // Initial delay for exponential back-off. - static const int kDefaultInitialBackoffMs; + static const int kDefaultInitialDelayMs; // Factor by which the waiting time will be multiplied. static const double kDefaultMultiplyFactor; diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc index 0519648..2e300d2 100644 --- a/remoting/host/chromoting_host.cc +++ b/remoting/host/chromoting_host.cc @@ -54,6 +54,9 @@ const net::BackoffEntry::Policy kDefaultBackoffPolicy = { // Time to keep an entry from being discarded even when it // has no significant state, -1 to never discard. -1, + + // Don't use initial delay unless the last request was an error. + false, }; } // namespace |