-
Notifications
You must be signed in to change notification settings - Fork 5.4k
/
Codel.cpp
137 lines (115 loc) · 4.59 KB
/
Codel.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/executors/Codel.h>
#include <algorithm>
#include <stdexcept>
#include <folly/portability/GFlags.h>
FOLLY_GFLAGS_DEFINE_int32(
codel_interval, 100, "Codel default interval time in ms");
FOLLY_GFLAGS_DEFINE_int32(
codel_target_delay, 5, "Target codel queueing delay in ms");
using namespace std::chrono;
namespace folly {
Codel::Codel()
: Codel(Codel::Options()
.setInterval(milliseconds(FLAGS_codel_interval))
.setTargetDelay(milliseconds(FLAGS_codel_target_delay))) {}
Codel::Codel(const Options& options)
: codelMinDelayNs_(0),
codelIntervalTimeNs_(
duration_cast<nanoseconds>(steady_clock::now().time_since_epoch())
.count()),
targetDelay_(options.targetDelay()),
interval_(options.interval()),
codelResetDelay_(true),
overloaded_(false) {}
bool Codel::overloaded(nanoseconds delay) {
bool ret = false;
auto now = steady_clock::now();
// Avoid another thread updating the value at the same time we are using it
// to calculate the overloaded state
auto minDelay = nanoseconds(codelMinDelayNs_);
// Get a snapshot of the parameters to determine overload condition
auto opts = getOptions();
auto sloughTimeout = getSloughTimeout(opts.targetDelay());
if (now > steady_clock::time_point(nanoseconds(codelIntervalTimeNs_)) &&
// testing before exchanging is more cacheline-friendly
(!codelResetDelay_.load(std::memory_order_acquire) &&
!codelResetDelay_.exchange(true))) {
codelIntervalTimeNs_ =
duration_cast<nanoseconds>((now + opts.interval()).time_since_epoch())
.count();
if (minDelay > opts.targetDelay()) {
overloaded_ = true;
} else {
overloaded_ = false;
}
}
// Care must be taken that only a single thread resets codelMinDelay_,
// and that it happens after the interval reset above
if (codelResetDelay_.load(std::memory_order_acquire) &&
codelResetDelay_.exchange(false)) {
codelMinDelayNs_ = delay.count();
// More than one request must come in during an interval before codel
// starts dropping requests
return false;
} else if (delay < nanoseconds(codelMinDelayNs_)) {
codelMinDelayNs_ = delay.count();
}
// Here is where we apply different logic than codel proper. Instead of
// adapting the interval until the next drop, we slough off requests with
// queueing delay > 2*target_delay while in the overloaded regime. This
// empirically works better for our services than the codel approach of
// increasingly often dropping packets.
if (overloaded_ && delay > sloughTimeout) {
ret = true;
}
return ret;
}
int Codel::getLoad() {
// it might be better to use the average delay instead of minDelay, but we'd
// have to track it. aspiring bootcamper?
auto opts = getOptions();
return std::min<int>(
100, 100 * getMinDelay() / getSloughTimeout(opts.targetDelay()));
}
void Codel::setOptions(Options const& options) {
// Carry out some basic sanity checks.
auto delay = options.targetDelay();
auto interval = options.interval();
if (interval <= delay || delay <= milliseconds::zero() ||
interval <= milliseconds::zero()) {
throw std::invalid_argument("Invalid arguments provided");
}
interval_.store(interval, std::memory_order_relaxed);
targetDelay_.store(delay, std::memory_order_relaxed);
}
const Codel::Options Codel::getOptions() const {
auto interval = interval_.load(std::memory_order_relaxed);
auto delay = targetDelay_.load(std::memory_order_relaxed);
// Enforcing the invariant that targetDelay <= interval. A violation could
// potentially occur if either parameter was updated by another concurrent
// thread via the setOptions() method.
delay = std::min(delay, interval);
return Codel::Options().setTargetDelay(delay).setInterval(interval);
}
nanoseconds Codel::getMinDelay() {
return nanoseconds(codelMinDelayNs_);
}
milliseconds Codel::getSloughTimeout(milliseconds delay) const {
return delay * 2;
}
} // namespace folly