Home | History | Annotate | Download | only in base
      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