1 // Copyright (c) 2012 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.
5 #include "net/base/backoff_entry.h"
11 #include "base/logging.h"
12 #include "base/rand_util.h"
16 BackoffEntry::BackoffEntry(const BackoffEntry::Policy
* const policy
)
22 BackoffEntry::~BackoffEntry() {
23 // TODO(joi): Remove this once our clients (e.g. URLRequestThrottlerManager)
24 // always destroy from the I/O thread.
28 void BackoffEntry::InformOfRequest(bool succeeded
) {
31 exponential_backoff_release_time_
= CalculateReleaseTime();
33 // We slowly decay the number of times delayed instead of
34 // resetting it to 0 in order to stay stable if we receive
35 // successes interleaved between lots of failures. Note that in
36 // the normal case, the calculated release time (in the next
37 // statement) will be in the past once the method returns.
38 if (failure_count_
> 0)
41 // The reason why we are not just cutting the release time to
42 // ImplGetTimeNow() is on the one hand, it would unset a release
43 // time set by SetCustomReleaseTime and on the other we would like
44 // to push every request up to our "horizon" when dealing with
45 // multiple in-flight requests. Ex: If we send three requests and
46 // we receive 2 failures and 1 success. The success that follows
47 // those failures will not reset the release time, further
48 // requests will then need to wait the delay caused by the 2
50 base::TimeDelta delay
;
51 if (policy_
->always_use_initial_delay
)
52 delay
= base::TimeDelta::FromMilliseconds(policy_
->initial_delay_ms
);
53 exponential_backoff_release_time_
= std::max(
54 ImplGetTimeNow() + delay
, exponential_backoff_release_time_
);
58 bool BackoffEntry::ShouldRejectRequest() const {
59 return exponential_backoff_release_time_
> ImplGetTimeNow();
62 base::TimeDelta
BackoffEntry::GetTimeUntilRelease() const {
63 base::TimeTicks now
= ImplGetTimeNow();
64 if (exponential_backoff_release_time_
<= now
)
65 return base::TimeDelta();
66 return exponential_backoff_release_time_
- now
;
69 base::TimeTicks
BackoffEntry::GetReleaseTime() const {
70 return exponential_backoff_release_time_
;
73 void BackoffEntry::SetCustomReleaseTime(const base::TimeTicks
& release_time
) {
74 exponential_backoff_release_time_
= release_time
;
77 bool BackoffEntry::CanDiscard() const {
78 if (policy_
->entry_lifetime_ms
== -1)
81 base::TimeTicks now
= ImplGetTimeNow();
83 int64 unused_since_ms
=
84 (now
- exponential_backoff_release_time_
).InMilliseconds();
86 // Release time is further than now, we are managing it.
87 if (unused_since_ms
< 0)
90 if (failure_count_
> 0) {
91 // Need to keep track of failures until maximum back-off period
92 // has passed (since further failures can add to back-off).
93 return unused_since_ms
>= std::max(policy_
->maximum_backoff_ms
,
94 policy_
->entry_lifetime_ms
);
97 // Otherwise, consider the entry is outdated if it hasn't been used for the
98 // specified lifetime period.
99 return unused_since_ms
>= policy_
->entry_lifetime_ms
;
102 void BackoffEntry::Reset() {
105 // We leave exponential_backoff_release_time_ unset, meaning 0. We could
106 // initialize to ImplGetTimeNow() but because it's a virtual method it's
107 // not safe to call in the constructor (and the constructor calls Reset()).
108 // The effects are the same, i.e. ShouldRejectRequest() will return false
109 // right after Reset().
110 exponential_backoff_release_time_
= base::TimeTicks();
113 base::TimeTicks
BackoffEntry::ImplGetTimeNow() const {
114 return base::TimeTicks::Now();
117 base::TimeTicks
BackoffEntry::CalculateReleaseTime() const {
118 int effective_failure_count
=
119 std::max(0, failure_count_
- policy_
->num_errors_to_ignore
);
121 // If always_use_initial_delay is true, it's equivalent to
122 // the effective_failure_count always being one greater than when it's false.
123 if (policy_
->always_use_initial_delay
)
124 ++effective_failure_count
;
126 if (effective_failure_count
== 0) {
127 // Never reduce previously set release horizon, e.g. due to Retry-After
129 return std::max(ImplGetTimeNow(), exponential_backoff_release_time_
);
132 // The delay is calculated with this formula:
133 // delay = initial_backoff * multiply_factor^(
134 // effective_failure_count - 1) * Uniform(1 - jitter_factor, 1]
135 double delay
= policy_
->initial_delay_ms
;
136 delay
*= pow(policy_
->multiply_factor
, effective_failure_count
- 1);
137 delay
-= base::RandDouble() * policy_
->jitter_factor
* delay
;
139 const int64 kMaxInt64
= std::numeric_limits
<int64
>::max();
140 int64 delay_int
= (delay
> kMaxInt64
) ?
141 kMaxInt64
: static_cast<int64
>(delay
+ 0.5);
143 // Ensure that we do not exceed maximum delay.
144 if (policy_
->maximum_backoff_ms
>= 0)
145 delay_int
= std::min(delay_int
, policy_
->maximum_backoff_ms
);
147 // Never reduce previously set release horizon, e.g. due to Retry-After
150 ImplGetTimeNow() + base::TimeDelta::FromMilliseconds(delay_int
),
151 exponential_backoff_release_time_
);