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