Chromium Code Reviews
|
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2010 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/url_request/request_throttler_entry.h" | |
| 6 | |
| 7 #include <cmath> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "base/rand_util.h" | |
| 11 #include "base/string_number_conversions.h" | |
| 12 #include "net/url_request/request_throttler_header_interface.h" | |
| 13 | |
| 14 const int RequestThrottlerEntry::kDefaultSlidingWindowPeriodMs = 2000; | |
| 15 const int RequestThrottlerEntry::kDefaultMaxSendThreshold = 20; | |
| 16 const int RequestThrottlerEntry::kDefaultInitialBackoffMs = 700; | |
| 17 const int RequestThrottlerEntry::kDefaultAdditionalConstantMs = 100; | |
| 18 const double RequestThrottlerEntry::kDefaultMultiplyFactor = 1.4; | |
| 19 const double RequestThrottlerEntry::kDefaultJitterFactor = 0.4; | |
| 20 const int RequestThrottlerEntry::kDefaultMaximumBackoffMs = 60 * 60 * 1000; | |
| 21 const int RequestThrottlerEntry::kDefaultEntryLifetimeMs = 120000; | |
| 22 const char RequestThrottlerEntry::kRetryHeaderName[] = "X-Retry-After"; | |
| 23 | |
| 24 RequestThrottlerEntry::RequestThrottlerEntry() | |
| 25 : sliding_window_period_( | |
| 26 base::TimeDelta::FromMilliseconds(kDefaultSlidingWindowPeriodMs)), | |
| 27 max_send_threshold_(kDefaultMaxSendThreshold), | |
| 28 initial_backoff_ms_(kDefaultInitialBackoffMs), | |
| 29 additional_constant_ms_(kDefaultAdditionalConstantMs), | |
| 30 multiply_factor_(kDefaultMultiplyFactor), | |
| 31 jitter_factor_(kDefaultJitterFactor), | |
| 32 maximum_backoff_ms_(kDefaultMaximumBackoffMs), | |
| 33 entry_lifetime_ms_(kDefaultEntryLifetimeMs) { | |
| 34 Initialize(); | |
| 35 } | |
| 36 | |
| 37 RequestThrottlerEntry::RequestThrottlerEntry( | |
| 38 int sliding_window_period_ms, | |
| 39 int max_send_threshold, | |
| 40 int initial_backoff_ms, | |
| 41 int additional_constant_ms, | |
| 42 double multiply_factor, | |
| 43 double jitter_factor, | |
| 44 int maximum_backoff_ms) | |
| 45 : sliding_window_period_( | |
| 46 base::TimeDelta::FromMilliseconds(sliding_window_period_ms)), | |
| 47 max_send_threshold_(max_send_threshold), | |
| 48 initial_backoff_ms_(initial_backoff_ms), | |
| 49 additional_constant_ms_(additional_constant_ms), | |
| 50 multiply_factor_(multiply_factor), | |
| 51 jitter_factor_(jitter_factor), | |
| 52 maximum_backoff_ms_(maximum_backoff_ms), | |
| 53 entry_lifetime_ms_(-1) { | |
| 54 DCHECK(sliding_window_period_ms > 0 && | |
| 55 max_send_threshold_ > 0 && | |
| 56 initial_backoff_ms_ >= 0 && | |
| 57 additional_constant_ms_ >= 0 && | |
| 58 multiply_factor_ > 0 && | |
| 59 jitter_factor_ >= 0 && jitter_factor_ < 1 && | |
| 60 maximum_backoff_ms_ >= 0); | |
| 61 | |
| 62 Initialize(); | |
| 63 } | |
| 64 | |
| 65 RequestThrottlerEntry::~RequestThrottlerEntry() { | |
| 66 } | |
| 67 | |
| 68 void RequestThrottlerEntry::Initialize() { | |
| 69 // Since this method is called by the constructors, GetTimeNow() (a virtual | |
| 70 // method) is not used. | |
| 71 exponential_backoff_release_time_ = base::TimeTicks::Now(); | |
| 72 failure_count_ = 0; | |
| 73 latest_response_was_failure_ = false; | |
| 74 | |
| 75 sliding_window_release_time_ = base::TimeTicks::Now(); | |
| 76 } | |
| 77 | |
| 78 bool RequestThrottlerEntry::IsDuringExponentialBackoff() const { | |
| 79 return exponential_backoff_release_time_ > GetTimeNow(); | |
| 80 } | |
| 81 | |
| 82 int64 RequestThrottlerEntry::ReserveSendingTimeForNextRequest( | |
| 83 const base::TimeTicks& earliest_time) { | |
| 84 base::TimeTicks now = GetTimeNow(); | |
| 85 // If a lot of requests were successfully made recently, | |
| 86 // sliding_window_release_time_ may be greater than | |
| 87 // exponential_backoff_release_time_. | |
| 88 base::TimeTicks recommended_sending_time = | |
| 89 std::max(std::max(now, earliest_time), | |
| 90 std::max(exponential_backoff_release_time_, | |
| 91 sliding_window_release_time_)); | |
| 92 | |
| 93 DCHECK(send_log_.empty() || | |
| 94 recommended_sending_time >= send_log_.back()); | |
| 95 // Log the new send event. | |
| 96 send_log_.push(recommended_sending_time); | |
| 97 | |
| 98 sliding_window_release_time_ = recommended_sending_time; | |
| 99 | |
| 100 // Drop the out-of-date events in the event list. | |
| 101 // We don't need to worry that the queue may become empty during this | |
| 102 // operation, since the last element is sliding_window_release_time_. | |
| 103 while ((send_log_.front() + sliding_window_period_ <= | |
| 104 sliding_window_release_time_) || | |
| 105 send_log_.size() > static_cast<unsigned>(max_send_threshold_)) { | |
| 106 send_log_.pop(); | |
| 107 } | |
| 108 | |
| 109 // Check if there are too many send events in recent time. | |
| 110 if (send_log_.size() == static_cast<unsigned>(max_send_threshold_)) | |
| 111 sliding_window_release_time_ = send_log_.front() + sliding_window_period_; | |
| 112 | |
| 113 return (recommended_sending_time - now).InMillisecondsRoundedUp(); | |
| 114 } | |
| 115 | |
| 116 base::TimeTicks | |
| 117 RequestThrottlerEntry::GetExponentialBackoffReleaseTime() const { | |
| 118 return exponential_backoff_release_time_; | |
| 119 } | |
| 120 | |
| 121 void RequestThrottlerEntry::UpdateWithResponse( | |
| 122 const RequestThrottlerHeaderInterface* response) { | |
| 123 if (response->GetResponseCode() >= 500) { | |
| 124 failure_count_++; | |
| 125 latest_response_was_failure_ = true; | |
| 126 exponential_backoff_release_time_ = | |
| 127 CalculateExponentialBackoffReleaseTime(); | |
| 128 } else { | |
| 129 // We slowly decay the number of times delayed instead of resetting it to 0 | |
| 130 // in order to stay stable if we received lots of requests with | |
| 131 // malformed bodies at the same time. | |
| 132 if (failure_count_ > 0) | |
| 133 failure_count_--; | |
| 134 | |
| 135 latest_response_was_failure_ = false; | |
| 136 | |
| 137 // The reason why we are not just cutting the release time to GetTimeNow() | |
| 138 // is on the one hand, it would unset delay put by our custom retry-after | |
| 139 // header and on the other we would like to push every request up to our | |
| 140 // "horizon" when dealing with multiple in-flight requests. Ex: If we send | |
| 141 // three requests and we receive 2 failures and 1 success. The success that | |
| 142 // follows those failures will not reset the release time, further requests | |
| 143 // will then need to wait the delay caused by the 2 failures. | |
| 144 exponential_backoff_release_time_ = std::max( | |
| 145 GetTimeNow(), exponential_backoff_release_time_); | |
| 146 | |
| 147 std::string retry_header = response->GetNormalizedValue(kRetryHeaderName); | |
| 148 if (!retry_header.empty()) | |
| 149 HandleCustomRetryAfter(retry_header); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 bool RequestThrottlerEntry::IsEntryOutdated() const { | |
| 154 if (entry_lifetime_ms_ == -1) | |
| 155 return false; | |
| 156 | |
| 157 base::TimeTicks now = GetTimeNow(); | |
| 158 | |
| 159 // If there are send events in the sliding window period, we still need this | |
| 160 // entry. | |
| 161 if (send_log_.size() > 0 && | |
| 162 send_log_.back() + sliding_window_period_ > now) { | |
| 163 return false; | |
| 164 } | |
| 165 | |
| 166 int64 unused_since_ms = | |
| 167 (now - exponential_backoff_release_time_).InMilliseconds(); | |
| 168 | |
| 169 // Release time is further than now, we are managing it. | |
| 170 if (unused_since_ms < 0) | |
| 171 return false; | |
| 172 | |
| 173 // latest_response_was_failure_ is true indicates that the latest one or | |
| 174 // more requests encountered server errors or had malformed response bodies. | |
| 175 // In that case, we don't want to collect the entry unless it hasn't been used | |
| 176 // for longer than the maximum allowed back-off. | |
| 177 if (latest_response_was_failure_) | |
| 178 return unused_since_ms > std::max(maximum_backoff_ms_, entry_lifetime_ms_); | |
| 179 | |
| 180 // Otherwise, consider the entry is outdated if it hasn't been used for the | |
| 181 // specified lifetime period. | |
| 182 return unused_since_ms > entry_lifetime_ms_; | |
| 183 } | |
| 184 | |
| 185 void RequestThrottlerEntry::ReceivedContentWasMalformed() { | |
| 186 failure_count_ += 2; | |
|
Jói
2010/11/21 19:31:40
Why it's +2 deserves documentation.
yzshen
2010/11/22 17:35:01
Done.
I have commented the reason why it is +2 he
Jói
2010/11/22 21:16:27
Looks good. I don't see a problem with this approa
| |
| 187 latest_response_was_failure_ = true; | |
| 188 exponential_backoff_release_time_ = CalculateExponentialBackoffReleaseTime(); | |
| 189 } | |
| 190 | |
| 191 base::TimeTicks | |
| 192 RequestThrottlerEntry::CalculateExponentialBackoffReleaseTime() { | |
| 193 double delay = initial_backoff_ms_; | |
| 194 delay *= pow(multiply_factor_, failure_count_); | |
| 195 delay += additional_constant_ms_; | |
| 196 delay -= base::RandDouble() * jitter_factor_ * delay; | |
| 197 | |
| 198 // Ensure that we do not exceed maximum delay. | |
| 199 int64 delay_int = static_cast<int64>(delay + 0.5); | |
| 200 delay_int = std::min(delay_int, static_cast<int64>(maximum_backoff_ms_)); | |
| 201 | |
| 202 return std::max(GetTimeNow() + base::TimeDelta::FromMilliseconds(delay_int), | |
| 203 exponential_backoff_release_time_); | |
| 204 } | |
| 205 | |
| 206 base::TimeTicks RequestThrottlerEntry::GetTimeNow() const { | |
| 207 return base::TimeTicks::Now(); | |
| 208 } | |
| 209 | |
| 210 void RequestThrottlerEntry::HandleCustomRetryAfter( | |
| 211 const std::string& header_value) { | |
| 212 // Input parameter is the number of seconds to wait in a floating point value. | |
| 213 double time_in_sec = 0; | |
| 214 bool conversion_is_ok = base::StringToDouble(header_value, &time_in_sec); | |
| 215 | |
| 216 // Conversion of custom retry-after header value failed. | |
| 217 if (!conversion_is_ok) | |
| 218 return; | |
| 219 | |
| 220 // We must use an int value later so we transform this in milliseconds. | |
| 221 int64 value_ms = static_cast<int64>(0.5 + time_in_sec * 1000); | |
| 222 | |
| 223 if (maximum_backoff_ms_ < value_ms || value_ms < 0) | |
| 224 return; | |
| 225 | |
| 226 exponential_backoff_release_time_ = std::max( | |
| 227 (GetTimeNow() + base::TimeDelta::FromMilliseconds(value_ms)), | |
| 228 exponential_backoff_release_time_); | |
| 229 } | |
| OLD | NEW |