OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/base/backoff_entry.h" | 5 #include "net/base/backoff_entry.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 | 9 |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/rand_util.h" | 11 #include "base/rand_util.h" |
12 | 12 |
13 namespace net { | 13 namespace net { |
14 | 14 |
15 BackoffEntry::BackoffEntry(const BackoffEntry::Policy* const policy) | 15 BackoffEntry::BackoffEntry(const BackoffEntry::Policy* const policy) |
16 : failure_count_(0), | 16 : policy_(policy) { |
17 policy_(policy) { | |
18 DCHECK(policy_); | 17 DCHECK(policy_); |
19 | 18 Reset(); |
20 // Can't use GetTimeNow() as it's virtual. | |
21 exponential_backoff_release_time_ = base::TimeTicks::Now(); | |
22 } | 19 } |
23 | 20 |
24 BackoffEntry::~BackoffEntry() { | 21 BackoffEntry::~BackoffEntry() { |
25 // TODO(joi): Remove this once our clients (e.g. URLRequestThrottlerManager) | 22 // TODO(joi): Remove this once our clients (e.g. URLRequestThrottlerManager) |
26 // always destroy from the I/O thread. | 23 // always destroy from the I/O thread. |
27 DetachFromThread(); | 24 DetachFromThread(); |
28 } | 25 } |
29 | 26 |
30 void BackoffEntry::InformOfRequest(bool succeeded) { | 27 void BackoffEntry::InformOfRequest(bool succeeded) { |
31 if (!succeeded) { | 28 if (!succeeded) { |
32 ++failure_count_; | 29 ++failure_count_; |
33 exponential_backoff_release_time_ = CalculateReleaseTime(); | 30 exponential_backoff_release_time_ = CalculateReleaseTime(); |
34 } else { | 31 } else { |
35 // We slowly decay the number of times delayed instead of resetting it to 0 | 32 // We slowly decay the number of times delayed instead of resetting it to 0 |
36 // in order to stay stable if we receive successes interleaved between lots | 33 // in order to stay stable if we receive successes interleaved between lots |
37 // of failures. | 34 // of failures. |
38 // | 35 // |
39 // TODO(joi): Revisit this; it might be most correct to go to zero | 36 // TODO(joi): Revisit this; it might be most correct to go to zero |
40 // but have a way to go back to "old error count +1" if there is | 37 // but have a way to go back to "old error count +1" if there is |
41 // another error soon after. | 38 // another error soon after. |
42 if (failure_count_ > 0) | 39 if (failure_count_ > 0) |
43 --failure_count_; | 40 --failure_count_; |
44 | 41 |
45 // The reason why we are not just cutting the release time to GetTimeNow() | 42 // The reason why we are not just cutting the release time to |
46 // is on the one hand, it would unset a release time set by | 43 // ImplGetTimeNow() is on the one hand, it would unset a release |
47 // SetCustomReleaseTime and on the other we would like to push every | 44 // time set by SetCustomReleaseTime and on the other we would like |
48 // request up to our "horizon" when dealing with multiple in-flight | 45 // to push every request up to our "horizon" when dealing with |
49 // requests. Ex: If we send three requests and we receive 2 failures and | 46 // multiple in-flight requests. Ex: If we send three requests and |
50 // 1 success. The success that follows those failures will not reset the | 47 // we receive 2 failures and 1 success. The success that follows |
51 // release time, further requests will then need to wait the delay caused | 48 // those failures will not reset the release time, further |
52 // by the 2 failures. | 49 // requests will then need to wait the delay caused by the 2 |
| 50 // failures. |
53 exponential_backoff_release_time_ = std::max( | 51 exponential_backoff_release_time_ = std::max( |
54 GetTimeNow(), exponential_backoff_release_time_); | 52 ImplGetTimeNow(), exponential_backoff_release_time_); |
55 } | 53 } |
56 } | 54 } |
57 | 55 |
58 bool BackoffEntry::ShouldRejectRequest() const { | 56 bool BackoffEntry::ShouldRejectRequest() const { |
59 return exponential_backoff_release_time_ > GetTimeNow(); | 57 return exponential_backoff_release_time_ > ImplGetTimeNow(); |
60 } | 58 } |
61 | 59 |
62 base::TimeTicks BackoffEntry::GetReleaseTime() const { | 60 base::TimeTicks BackoffEntry::GetReleaseTime() const { |
63 return exponential_backoff_release_time_; | 61 return exponential_backoff_release_time_; |
64 } | 62 } |
65 | 63 |
66 void BackoffEntry::SetCustomReleaseTime(const base::TimeTicks& release_time) { | 64 void BackoffEntry::SetCustomReleaseTime(const base::TimeTicks& release_time) { |
67 exponential_backoff_release_time_ = release_time; | 65 exponential_backoff_release_time_ = release_time; |
68 } | 66 } |
69 | 67 |
70 bool BackoffEntry::CanDiscard() const { | 68 bool BackoffEntry::CanDiscard() const { |
71 if (policy_->entry_lifetime_ms == -1) | 69 if (policy_->entry_lifetime_ms == -1) |
72 return false; | 70 return false; |
73 | 71 |
74 base::TimeTicks now = GetTimeNow(); | 72 base::TimeTicks now = ImplGetTimeNow(); |
75 | 73 |
76 int64 unused_since_ms = | 74 int64 unused_since_ms = |
77 (now - exponential_backoff_release_time_).InMilliseconds(); | 75 (now - exponential_backoff_release_time_).InMilliseconds(); |
78 | 76 |
79 // Release time is further than now, we are managing it. | 77 // Release time is further than now, we are managing it. |
80 if (unused_since_ms < 0) | 78 if (unused_since_ms < 0) |
81 return false; | 79 return false; |
82 | 80 |
83 if (failure_count_ > 0) { | 81 if (failure_count_ > 0) { |
84 // Need to keep track of failures until maximum back-off period | 82 // Need to keep track of failures until maximum back-off period |
85 // has passed (since further failures can add to back-off). | 83 // has passed (since further failures can add to back-off). |
86 return unused_since_ms >= std::max(policy_->maximum_backoff_ms, | 84 return unused_since_ms >= std::max(policy_->maximum_backoff_ms, |
87 policy_->entry_lifetime_ms); | 85 policy_->entry_lifetime_ms); |
88 } | 86 } |
89 | 87 |
90 // Otherwise, consider the entry is outdated if it hasn't been used for the | 88 // Otherwise, consider the entry is outdated if it hasn't been used for the |
91 // specified lifetime period. | 89 // specified lifetime period. |
92 return unused_since_ms >= policy_->entry_lifetime_ms; | 90 return unused_since_ms >= policy_->entry_lifetime_ms; |
93 } | 91 } |
94 | 92 |
95 base::TimeTicks BackoffEntry::GetTimeNow() const { | 93 void BackoffEntry::Reset() { |
| 94 failure_count_ = 0; |
| 95 |
| 96 // We leave exponential_backoff_release_time_ unset, meaning 0. We could |
| 97 // initialize to ImplGetTimeNow() but because it's a virtual method it's |
| 98 // not safe to call in the constructor (and the constructor calls Reset()). |
| 99 // The effects are the same, i.e. ShouldRejectRequest() will return false |
| 100 // right after Reset(). |
| 101 exponential_backoff_release_time_ = base::TimeTicks(); |
| 102 } |
| 103 |
| 104 base::TimeTicks BackoffEntry::ImplGetTimeNow() const { |
96 return base::TimeTicks::Now(); | 105 return base::TimeTicks::Now(); |
97 } | 106 } |
98 | 107 |
99 base::TimeTicks BackoffEntry::CalculateReleaseTime() const { | 108 base::TimeTicks BackoffEntry::CalculateReleaseTime() const { |
100 int effective_failure_count = | 109 int effective_failure_count = |
101 std::max(0, failure_count_ - policy_->num_errors_to_ignore); | 110 std::max(0, failure_count_ - policy_->num_errors_to_ignore); |
102 if (effective_failure_count == 0) { | 111 if (effective_failure_count == 0) { |
103 // Never reduce previously set release horizon, e.g. due to Retry-After | 112 // Never reduce previously set release horizon, e.g. due to Retry-After |
104 // header. | 113 // header. |
105 return std::max(GetTimeNow(), exponential_backoff_release_time_); | 114 return std::max(ImplGetTimeNow(), exponential_backoff_release_time_); |
106 } | 115 } |
107 | 116 |
108 // The delay is calculated with this formula: | 117 // The delay is calculated with this formula: |
109 // delay = initial_backoff * multiply_factor^( | 118 // delay = initial_backoff * multiply_factor^( |
110 // effective_failure_count - 1) * Uniform(1 - jitter_factor, 1] | 119 // effective_failure_count - 1) * Uniform(1 - jitter_factor, 1] |
111 double delay = policy_->initial_backoff_ms; | 120 double delay = policy_->initial_backoff_ms; |
112 delay *= pow(policy_->multiply_factor, effective_failure_count - 1); | 121 delay *= pow(policy_->multiply_factor, effective_failure_count - 1); |
113 delay -= base::RandDouble() * policy_->jitter_factor * delay; | 122 delay -= base::RandDouble() * policy_->jitter_factor * delay; |
114 | 123 |
115 // Ensure that we do not exceed maximum delay. | 124 // Ensure that we do not exceed maximum delay. |
116 int64 delay_int = static_cast<int64>(delay + 0.5); | 125 int64 delay_int = static_cast<int64>(delay + 0.5); |
117 delay_int = std::min(delay_int, | 126 delay_int = std::min(delay_int, |
118 static_cast<int64>(policy_->maximum_backoff_ms)); | 127 static_cast<int64>(policy_->maximum_backoff_ms)); |
119 | 128 |
120 // Never reduce previously set release horizon, e.g. due to Retry-After | 129 // Never reduce previously set release horizon, e.g. due to Retry-After |
121 // header. | 130 // header. |
122 return std::max(GetTimeNow() + base::TimeDelta::FromMilliseconds(delay_int), | 131 return std::max( |
123 exponential_backoff_release_time_); | 132 ImplGetTimeNow() + base::TimeDelta::FromMilliseconds(delay_int), |
| 133 exponential_backoff_release_time_); |
124 } | 134 } |
125 | 135 |
126 } // namespace net | 136 } // namespace net |
OLD | NEW |