summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/base/backoff_entry.cc20
-rw-r--r--net/base/backoff_entry.h34
-rw-r--r--net/base/backoff_entry_unittest.cc94
-rw-r--r--net/url_request/url_request_throttler_entry.cc11
-rw-r--r--net/url_request/url_request_throttler_entry.h2
-rw-r--r--remoting/host/chromoting_host.cc3
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