• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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 #include <limits>
10 
11 #include "base/logging.h"
12 #include "base/rand_util.h"
13 
14 namespace net {
15 
BackoffEntry(const BackoffEntry::Policy * const policy)16 BackoffEntry::BackoffEntry(const BackoffEntry::Policy* const policy)
17     : policy_(policy) {
18   DCHECK(policy_);
19   Reset();
20 }
21 
~BackoffEntry()22 BackoffEntry::~BackoffEntry() {
23   // TODO(joi): Remove this once our clients (e.g. URLRequestThrottlerManager)
24   // always destroy from the I/O thread.
25   DetachFromThread();
26 }
27 
InformOfRequest(bool succeeded)28 void BackoffEntry::InformOfRequest(bool succeeded) {
29   if (!succeeded) {
30     ++failure_count_;
31     exponential_backoff_release_time_ = CalculateReleaseTime();
32   } else {
33     // We slowly decay the number of times delayed instead of
34     // resetting it to 0 in order to stay stable if we receive
35     // successes interleaved between lots of failures.  Note that in
36     // the normal case, the calculated release time (in the next
37     // statement) will be in the past once the method returns.
38     if (failure_count_ > 0)
39       --failure_count_;
40 
41     // The reason why we are not just cutting the release time to
42     // ImplGetTimeNow() is on the one hand, it would unset a release
43     // time set by SetCustomReleaseTime and on the other we would like
44     // to push every request up to our "horizon" when dealing with
45     // multiple in-flight requests. Ex: If we send three requests and
46     // we receive 2 failures and 1 success. The success that follows
47     // those failures will not reset the release time, further
48     // requests will then need to wait the delay caused by the 2
49     // failures.
50     base::TimeDelta delay;
51     if (policy_->always_use_initial_delay)
52       delay = base::TimeDelta::FromMilliseconds(policy_->initial_delay_ms);
53     exponential_backoff_release_time_ = std::max(
54         ImplGetTimeNow() + delay, exponential_backoff_release_time_);
55   }
56 }
57 
ShouldRejectRequest() const58 bool BackoffEntry::ShouldRejectRequest() const {
59   return exponential_backoff_release_time_ > ImplGetTimeNow();
60 }
61 
GetTimeUntilRelease() const62 base::TimeDelta BackoffEntry::GetTimeUntilRelease() const {
63   base::TimeTicks now = ImplGetTimeNow();
64   if (exponential_backoff_release_time_ <= now)
65     return base::TimeDelta();
66   return exponential_backoff_release_time_ - now;
67 }
68 
GetReleaseTime() const69 base::TimeTicks BackoffEntry::GetReleaseTime() const {
70   return exponential_backoff_release_time_;
71 }
72 
SetCustomReleaseTime(const base::TimeTicks & release_time)73 void BackoffEntry::SetCustomReleaseTime(const base::TimeTicks& release_time) {
74   exponential_backoff_release_time_ = release_time;
75 }
76 
CanDiscard() const77 bool BackoffEntry::CanDiscard() const {
78   if (policy_->entry_lifetime_ms == -1)
79     return false;
80 
81   base::TimeTicks now = ImplGetTimeNow();
82 
83   int64 unused_since_ms =
84       (now - exponential_backoff_release_time_).InMilliseconds();
85 
86   // Release time is further than now, we are managing it.
87   if (unused_since_ms < 0)
88     return false;
89 
90   if (failure_count_ > 0) {
91     // Need to keep track of failures until maximum back-off period
92     // has passed (since further failures can add to back-off).
93     return unused_since_ms >= std::max(policy_->maximum_backoff_ms,
94                                        policy_->entry_lifetime_ms);
95   }
96 
97   // Otherwise, consider the entry is outdated if it hasn't been used for the
98   // specified lifetime period.
99   return unused_since_ms >= policy_->entry_lifetime_ms;
100 }
101 
Reset()102 void BackoffEntry::Reset() {
103   failure_count_ = 0;
104 
105   // We leave exponential_backoff_release_time_ unset, meaning 0. We could
106   // initialize to ImplGetTimeNow() but because it's a virtual method it's
107   // not safe to call in the constructor (and the constructor calls Reset()).
108   // The effects are the same, i.e. ShouldRejectRequest() will return false
109   // right after Reset().
110   exponential_backoff_release_time_ = base::TimeTicks();
111 }
112 
ImplGetTimeNow() const113 base::TimeTicks BackoffEntry::ImplGetTimeNow() const {
114   return base::TimeTicks::Now();
115 }
116 
CalculateReleaseTime() const117 base::TimeTicks BackoffEntry::CalculateReleaseTime() const {
118   int effective_failure_count =
119       std::max(0, failure_count_ - policy_->num_errors_to_ignore);
120 
121   // If always_use_initial_delay is true, it's equivalent to
122   // the effective_failure_count always being one greater than when it's false.
123   if (policy_->always_use_initial_delay)
124     ++effective_failure_count;
125 
126   if (effective_failure_count == 0) {
127     // Never reduce previously set release horizon, e.g. due to Retry-After
128     // header.
129     return std::max(ImplGetTimeNow(), exponential_backoff_release_time_);
130   }
131 
132   // The delay is calculated with this formula:
133   // delay = initial_backoff * multiply_factor^(
134   //     effective_failure_count - 1) * Uniform(1 - jitter_factor, 1]
135   double delay = policy_->initial_delay_ms;
136   delay *= pow(policy_->multiply_factor, effective_failure_count - 1);
137   delay -= base::RandDouble() * policy_->jitter_factor * delay;
138 
139   const int64 kMaxInt64 = std::numeric_limits<int64>::max();
140   int64 delay_int = (delay > kMaxInt64) ?
141       kMaxInt64 : static_cast<int64>(delay + 0.5);
142 
143   // Ensure that we do not exceed maximum delay.
144   if (policy_->maximum_backoff_ms >= 0)
145     delay_int = std::min(delay_int, policy_->maximum_backoff_ms);
146 
147   // Never reduce previously set release horizon, e.g. due to Retry-After
148   // header.
149   return std::max(
150       ImplGetTimeNow() + base::TimeDelta::FromMilliseconds(delay_int),
151       exponential_backoff_release_time_);
152 }
153 
154 }  // namespace net
155