• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BackoffEntry(const BackoffEntry::Policy * const policy)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 
~BackoffEntry()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 
InformOfRequest(bool succeeded)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 
ShouldRejectRequest() const58 bool BackoffEntry::ShouldRejectRequest() const {
59   return exponential_backoff_release_time_ > GetTimeNow();
60 }
61 
GetReleaseTime() const62 base::TimeTicks BackoffEntry::GetReleaseTime() const {
63   return exponential_backoff_release_time_;
64 }
65 
SetCustomReleaseTime(const base::TimeTicks & release_time)66 void BackoffEntry::SetCustomReleaseTime(const base::TimeTicks& release_time) {
67   exponential_backoff_release_time_ = release_time;
68 }
69 
CanDiscard() const70 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 
GetTimeNow() const95 base::TimeTicks BackoffEntry::GetTimeNow() const {
96   return base::TimeTicks::Now();
97 }
98 
CalculateReleaseTime() const99 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