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.
4
5 #include "net/base/backoff_entry.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <limits>
10
11 #include "base/logging.h"
12 #include "base/rand_util.h"
13
14 namespace net {
15
BackoffEntry(const BackoffEntry::Policy * const policy)16 BackoffEntry::BackoffEntry(const BackoffEntry::Policy* const policy)
17 : policy_(policy) {
18 DCHECK(policy_);
19 Reset();
20 }
21
~BackoffEntry()22 BackoffEntry::~BackoffEntry() {
23 // TODO(joi): Remove this once our clients (e.g. URLRequestThrottlerManager)
24 // always destroy from the I/O thread.
25 DetachFromThread();
26 }
27
InformOfRequest(bool succeeded)28 void BackoffEntry::InformOfRequest(bool succeeded) {
29 if (!succeeded) {
30 ++failure_count_;
31 exponential_backoff_release_time_ = CalculateReleaseTime();
32 } else {
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)
39 --failure_count_;
40
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
49 // failures.
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_);
55 }
56 }
57
ShouldRejectRequest() const58 bool BackoffEntry::ShouldRejectRequest() const {
59 return exponential_backoff_release_time_ > ImplGetTimeNow();
60 }
61
GetTimeUntilRelease() const62 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;
67 }
68
GetReleaseTime() const69 base::TimeTicks BackoffEntry::GetReleaseTime() const {
70 return exponential_backoff_release_time_;
71 }
72
SetCustomReleaseTime(const base::TimeTicks & release_time)73 void BackoffEntry::SetCustomReleaseTime(const base::TimeTicks& release_time) {
74 exponential_backoff_release_time_ = release_time;
75 }
76
CanDiscard() const77 bool BackoffEntry::CanDiscard() const {
78 if (policy_->entry_lifetime_ms == -1)
79 return false;
80
81 base::TimeTicks now = ImplGetTimeNow();
82
83 int64 unused_since_ms =
84 (now - exponential_backoff_release_time_).InMilliseconds();
85
86 // Release time is further than now, we are managing it.
87 if (unused_since_ms < 0)
88 return false;
89
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);
95 }
96
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;
100 }
101
Reset()102 void BackoffEntry::Reset() {
103 failure_count_ = 0;
104
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();
111 }
112
ImplGetTimeNow() const113 base::TimeTicks BackoffEntry::ImplGetTimeNow() const {
114 return base::TimeTicks::Now();
115 }
116
CalculateReleaseTime() const117 base::TimeTicks BackoffEntry::CalculateReleaseTime() const {
118 int effective_failure_count =
119 std::max(0, failure_count_ - policy_->num_errors_to_ignore);
120
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;
125
126 if (effective_failure_count == 0) {
127 // Never reduce previously set release horizon, e.g. due to Retry-After
128 // header.
129 return std::max(ImplGetTimeNow(), exponential_backoff_release_time_);
130 }
131
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;
138
139 const int64 kMaxInt64 = std::numeric_limits<int64>::max();
140 int64 delay_int = (delay > kMaxInt64) ?
141 kMaxInt64 : static_cast<int64>(delay + 0.5);
142
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);
146
147 // Never reduce previously set release horizon, e.g. due to Retry-After
148 // header.
149 return std::max(
150 ImplGetTimeNow() + base::TimeDelta::FromMilliseconds(delay_int),
151 exponential_backoff_release_time_);
152 }
153
154 } // namespace net
155