/Users/deen/code/yugabyte-db/src/yb/util/net/rate_limiter-test.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | |
14 | | #include <chrono> |
15 | | #include <random> |
16 | | |
17 | | #include "yb/util/net/rate_limiter.h" |
18 | | #include "yb/util/random_util.h" |
19 | | #include "yb/util/size_literals.h" |
20 | | #include "yb/util/status.h" |
21 | | #include "yb/util/test_macros.h" |
22 | | |
23 | | #include <gtest/gtest.h> |
24 | | |
25 | | DECLARE_uint64(rate_limiter_min_rate); |
26 | | |
27 | | namespace yb { |
28 | | |
29 | | using namespace std::chrono_literals; |
30 | | |
31 | | constexpr uint64_t kRate = 1_KB; |
32 | | |
33 | | namespace { |
34 | 8 | uint64_t GetDifference(uint64_t n1, uint64_t n2) { |
35 | 5 | return n1 > n2 ? n1 - n2 : n2 - n1; |
36 | 8 | } |
37 | | } |
38 | | |
39 | 1 | TEST(RateLimiter, TestRate) { |
40 | 8 | for (uint64_t rate : {1_KB, 512ul, 1ul, 0ul, 48ul, 4_KB, 1_MB, 1_GB}) { |
41 | 8 | RateLimiter rate_limiter([rate]() { return rate; }); |
42 | 8 | auto min_rate = std::max(rate, static_cast<uint64_t>(FLAGS_rate_limiter_min_rate)); |
43 | 8 | ASSERT_EQ(rate_limiter.GetMaxSizeForNextTransmission(), |
44 | 8 | min_rate * rate_limiter.time_slot_ms() / MonoTime::kMillisecondsPerSecond); |
45 | 8 | } |
46 | 1 | } |
47 | | |
48 | 1 | TEST(RateLimiter, TestUpdateDataSize) { |
49 | 1 | MonoDelta local_sleep_time(3s); |
50 | 1 | RateLimiter rate_limiter([]() { return kRate; }); |
51 | 1 | rate_limiter.Init(); |
52 | 1 | SleepFor(local_sleep_time); |
53 | 1 | auto start = MonoTime::Now(); |
54 | | // This method should sleep about 1 second to make the rate equivalent to 1024 bytes/sec. |
55 | 1 | rate_limiter.UpdateDataSizeAndMaybeSleep(4 * kRate); |
56 | 1 | auto elapsed = MonoTime::Now().GetDeltaSince(start); |
57 | 1 | auto time_diff_ms = GetDifference(elapsed.ToMilliseconds(), MonoTime::kMillisecondsPerSecond); |
58 | 1 | #if defined(OS_MACOSX) |
59 | | // MacOS tests are much slower, and the timing check usually fails. So use the time slept by the |
60 | | // rate limiter instead. |
61 | 1 | if (time_diff_ms > 100) { |
62 | 0 | time_diff_ms = GetDifference(rate_limiter.total_time_slept().ToMilliseconds(), |
63 | 0 | MonoTime::kMillisecondsPerSecond); |
64 | 0 | } |
65 | 1 | #endif |
66 | 1 | ASSERT_LE(time_diff_ms, 100); |
67 | 1 | auto max_allowed_rate_diff = kRate * 5 / 100; |
68 | 1 | auto diff = GetDifference(rate_limiter.GetRate(), kRate); |
69 | 1 | #if defined(OS_MACOSX) |
70 | 1 | if (diff > max_allowed_rate_diff) { |
71 | | // We add the 3s that we slept in this test. |
72 | 0 | diff = GetDifference(rate_limiter.MacOSRate(local_sleep_time), kRate); |
73 | | |
74 | | // For MacOS allow a 10% difference. |
75 | 0 | max_allowed_rate_diff = kRate * 10 / 100; |
76 | 0 | } |
77 | 1 | #endif |
78 | 1 | ASSERT_LE(diff, max_allowed_rate_diff); |
79 | 1 | } |
80 | | |
81 | 1 | TEST(RateLimiter, TestSendRequest) { |
82 | 1 | MonoDelta local_sleep_time(3s); |
83 | 1 | RateLimiter rate_limiter([]() { return kRate; }); |
84 | 1 | auto start = MonoTime::Now(); |
85 | 1 | auto status = rate_limiter.SendOrReceiveData( |
86 | | // Send or receive function. |
87 | 1 | [local_sleep_time]() { |
88 | 1 | SleepFor(local_sleep_time); |
89 | 1 | return Status::OK(); |
90 | 1 | }, |
91 | | // Returns the total amount of data sent or received. |
92 | 1 | []() { return 4 * kRate; }); |
93 | 1 | ASSERT_OK(status); |
94 | 1 | auto elapsed = MonoTime::Now().GetDeltaSince(start); |
95 | | // The elapsed time should be ~4s (4 * kRate bytes transmitted at kRate bytes/sec). |
96 | 1 | auto time_diff_ms = GetDifference(elapsed.ToMilliseconds(), 4 * MonoTime::kMillisecondsPerSecond); |
97 | | |
98 | | // Allow a 5% difference (200 ms in this case). |
99 | 1 | auto max_allowed_time_diff_ms = 4 * MonoTime::kMillisecondsPerSecond * 5 / 100; |
100 | 1 | #if defined(OS_MACOSX) |
101 | | // MacOS tests are much slower, and the timing check usually fails. So use the time slept by the |
102 | | // rate limiter instead. |
103 | 1 | if (time_diff_ms > max_allowed_time_diff_ms) { |
104 | 0 | time_diff_ms = GetDifference( |
105 | 0 | rate_limiter.total_time_slept().ToMilliseconds() + local_sleep_time.ToMilliseconds(), |
106 | 0 | 4 * MonoTime::kMillisecondsPerSecond); |
107 | | |
108 | | // For MacOS allow a 10% time difference. |
109 | 0 | max_allowed_time_diff_ms = 4 * MonoTime::kMillisecondsPerSecond * 10 / 100; |
110 | 0 | } |
111 | 1 | #endif |
112 | 1 | ASSERT_LE(time_diff_ms, max_allowed_time_diff_ms); |
113 | | // Check that diff in rate is not more than 5%. |
114 | 1 | auto max_allowed_rate_diff = kRate * 5 / 100; |
115 | 1 | auto diff = GetDifference(rate_limiter.GetRate(), kRate); |
116 | 1 | #if defined(OS_MACOSX) |
117 | 1 | if (diff > max_allowed_rate_diff) { |
118 | | // We add the 3s that we slept in this test. |
119 | 0 | diff = GetDifference(rate_limiter.MacOSRate(local_sleep_time), kRate); |
120 | | |
121 | | // Allow 10% difference for MacOS. |
122 | 0 | max_allowed_rate_diff = kRate * 10 / 100; |
123 | 0 | } |
124 | 1 | #endif |
125 | 1 | ASSERT_LE(diff, max_allowed_rate_diff); |
126 | 1 | } |
127 | | |
128 | 1 | TEST(RateLimiter, TestSendRequestWithMultipleRates) { |
129 | 1 | vector<uint64_t> rates; |
130 | 1 | uint64_t rates_sum = 0; |
131 | | |
132 | 100 | RateLimiter rate_limiter([&rates, &rates_sum]() { |
133 | | // This is the rate updater function. |
134 | 100 | auto nconnections = RandomUniformInt(1, 20); |
135 | 100 | auto rate = kRate * 1_KB / nconnections; |
136 | 100 | rates.push_back(rate); |
137 | 100 | rates_sum += rate; |
138 | 100 | return rate; |
139 | 100 | }); |
140 | | |
141 | 1 | constexpr int kIterations = 100; |
142 | 1 | constexpr int kIterationsPerSecond = 10; |
143 | 1 | auto start = MonoTime::Now(); |
144 | 101 | for (int i = 0; i < kIterations; i++) { |
145 | 100 | auto status = rate_limiter.SendOrReceiveData( |
146 | | // Send or receive function. |
147 | 100 | []() { |
148 | | // Sleep for 100ms. |
149 | 100 | SleepFor( |
150 | 100 | MonoDelta::FromMilliseconds(MonoTime::kMillisecondsPerSecond / kIterationsPerSecond)); |
151 | 100 | return Status::OK(); |
152 | 100 | }, |
153 | | // Returns the total amount of data sent or received. |
154 | 100 | [&rates]() { |
155 | | // The number of bytes sent or received is always equal to the rate / 10, which means that |
156 | | // it should take 100ms to send or receive the data. But since our function sleeps for |
157 | | // 100ms, the rate_limiter object should not sleep. |
158 | 100 | return rates.back() / kIterationsPerSecond; |
159 | 100 | }); |
160 | 100 | ASSERT_OK(status); |
161 | 100 | } |
162 | | |
163 | 1 | auto end = MonoTime::Now(); |
164 | 1 | auto elapsed = end.GetDeltaSince(start); |
165 | | |
166 | | // The total time should be ~10s |
167 | | // ((kIterations * MonoTime::kMillisecondsPerSecond / 10) milliseconds because each call to the |
168 | | // send/receive function slept for (MonoTime::kMillisecondsPerSecond / 10) ms) since rate limiter |
169 | | // should have never had additional sleeps. The only sleeps that should have happened are the ones |
170 | | // in our send/receive function. |
171 | 1 | auto expected_time_ms = kIterations * MonoTime::kMillisecondsPerSecond / 10; |
172 | 1 | auto time_diff_ms = GetDifference(elapsed.ToMilliseconds(), expected_time_ms); |
173 | 1 | auto max_allowed_time_diff_ms = expected_time_ms * 5 / 100; |
174 | 1 | #if defined(OS_MACOSX) |
175 | | // MacOS tests are much slower, and the timing check usually fails. So use the time slept by the |
176 | | // rate limiter instead. |
177 | | // Since we don't expect the rate limiter to sleep, RateLimiter::total_time_slept() should be |
178 | | // close to zero. |
179 | 1 | if (time_diff_ms > expected_time_ms * 5 / 100) { |
180 | 0 | time_diff_ms = rate_limiter.total_time_slept().ToMilliseconds(); |
181 | | // For MacOS allow a 10% difference. |
182 | 0 | max_allowed_time_diff_ms = expected_time_ms * 10 / 100; |
183 | 0 | } |
184 | 1 | #endif |
185 | 1 | ASSERT_LE(time_diff_ms, max_allowed_time_diff_ms); |
186 | | |
187 | 1 | auto expected_avg_rate = rates_sum / rates.size(); |
188 | | // Check that diff in rate is not more than 5%. |
189 | 1 | auto max_allowed_rate_diff = expected_avg_rate * 5 / 100; |
190 | | |
191 | 1 | LOG(INFO) << "Expected average rate: " << expected_avg_rate; |
192 | 1 | LOG(INFO) << "Rate limiter rate: " << rate_limiter.GetRate(); |
193 | 1 | auto diff = GetDifference(rate_limiter.GetRate(), expected_avg_rate); |
194 | 1 | #if defined(OS_MACOSX) |
195 | 1 | if (diff > max_allowed_rate_diff) { |
196 | | // We add the time our send/receive function spent sleeping. |
197 | 0 | diff = GetDifference(rate_limiter.MacOSRate(MonoDelta::FromMilliseconds(expected_time_ms)), |
198 | 0 | expected_avg_rate); |
199 | | // For MacOS allow a 10% difference. |
200 | 0 | max_allowed_rate_diff = expected_avg_rate * 10 / 100; |
201 | 0 | } |
202 | 1 | #endif |
203 | 1 | ASSERT_LE(diff, max_allowed_rate_diff); |
204 | 1 | } |
205 | | |
206 | 1 | TEST(RateLimiter, TestFastSendRequestWithMultipleRates) { |
207 | 1 | vector<uint64_t> rates; |
208 | 1 | uint64_t rates_sum = 0; |
209 | | |
210 | 100 | RateLimiter rate_limiter([&rates, &rates_sum]() { |
211 | 100 | auto nconnections = RandomUniformInt(1, 20); |
212 | 100 | auto rate = kRate * 1_KB / nconnections; |
213 | 100 | rates.push_back(rate); |
214 | 100 | rates_sum += rate; |
215 | 100 | return rate; |
216 | 100 | }); |
217 | | |
218 | 1 | constexpr int kIterations = 100; |
219 | 1 | constexpr int kIterationsPerSecond = 10; |
220 | 1 | auto start = MonoTime::Now(); |
221 | 101 | for (int i = 0; i < kIterations; i++) { |
222 | 100 | auto status = rate_limiter.SendOrReceiveData( |
223 | | // Send or receive function. This time there is no sleep, so the sleep will be called by |
224 | | // the RateLimiter object. |
225 | 100 | []() { |
226 | 100 | return Status::OK(); |
227 | 100 | }, |
228 | | // The number of bytes sent or received is always equal to the rate / 10, which means that |
229 | | // the rate limiter object should sleep for 100ms. |
230 | 100 | [&rates]() { |
231 | 100 | return rates.back() / kIterationsPerSecond; |
232 | 100 | }); |
233 | 100 | ASSERT_OK(status); |
234 | 100 | } |
235 | 1 | auto end = MonoTime::Now(); |
236 | 1 | auto elapsed = end.GetDeltaSince(start); |
237 | | |
238 | | // The total time should be ~10s |
239 | | // ((kIterations * MonoTime::kMillisecondsPerSecond / 10) milliseconds) since the RateLimiter |
240 | | // object should have slept for 100ms for every call to SendOrReceiveData. |
241 | 1 | auto expected_time_ms = kIterations * MonoTime::kMillisecondsPerSecond / 10; |
242 | 1 | auto time_diff_ms = GetDifference(elapsed.ToMilliseconds(), expected_time_ms); |
243 | 1 | auto max_allowed_time_diff_ms = expected_time_ms * 5 / 100; |
244 | 1 | #if defined(OS_MACOSX) |
245 | | // MacOS tests are much slower, and the timing check usually fails. So use the time slept by the |
246 | | // rate limiter instead. |
247 | 1 | if (time_diff_ms > max_allowed_time_diff_ms) { |
248 | 0 | time_diff_ms = GetDifference(rate_limiter.total_time_slept().ToMilliseconds(), |
249 | 0 | expected_time_ms); |
250 | | // For MacOS allow a 10% difference. |
251 | 0 | max_allowed_time_diff_ms = expected_time_ms * 10 / 100; |
252 | 0 | } |
253 | 1 | #endif |
254 | | // Verify that the difference in elapsed time is within 5% (or 10% for MacOS in some cases) of the |
255 | | // expected time. |
256 | 1 | ASSERT_LE(time_diff_ms, max_allowed_time_diff_ms); |
257 | | |
258 | 1 | auto expected_avg_rate = rates_sum / rates.size(); |
259 | | // Check that diff in rate is not more than 5%. |
260 | 1 | auto max_allowed_rate_diff = expected_avg_rate * 5 / 100; |
261 | | |
262 | 1 | LOG(INFO) << "Expected average rate: " << expected_avg_rate; |
263 | 1 | LOG(INFO) << "Rate limiter rate: " << rate_limiter.GetRate(); |
264 | 1 | auto rate_diff = GetDifference(rate_limiter.GetRate(), expected_avg_rate); |
265 | 1 | #if defined(OS_MACOSX) |
266 | 1 | if (rate_diff > max_allowed_rate_diff) { |
267 | 0 | rate_diff = GetDifference(rate_limiter.MacOSRate(), expected_avg_rate); |
268 | | |
269 | | // For MacOS allow a 10% rate difference. |
270 | 0 | max_allowed_rate_diff = expected_avg_rate * 10 / 100; |
271 | 0 | } |
272 | 1 | #endif |
273 | 1 | LOG(INFO) << "diff: " << rate_diff; |
274 | 1 | ASSERT_LE(rate_diff, max_allowed_rate_diff); |
275 | 1 | } |
276 | | |
277 | 1 | TEST(RateLimiter, TestInactiveRateLimiter) { |
278 | 10 | RateLimiter active_rate_limiter([]() { return kRate; }); |
279 | 1 | RateLimiter inactive_rate_limiter; |
280 | 1 | ASSERT_TRUE(active_rate_limiter.active()); |
281 | 1 | ASSERT_FALSE(inactive_rate_limiter.active()); |
282 | | |
283 | 1 | vector<MonoDelta> elapsed_times; |
284 | 2 | for (auto* rate_limiter : {&active_rate_limiter, &inactive_rate_limiter}) { |
285 | 2 | auto start = MonoTime::Now(); |
286 | 22 | for (int i = 0; i < 10; i++) { |
287 | 20 | auto status = rate_limiter->SendOrReceiveData([]() { return Status::OK(); }, |
288 | 20 | []() { return kRate * 11 / 10; }); |
289 | 20 | } |
290 | 2 | auto end = MonoTime::Now(); |
291 | 2 | elapsed_times.emplace_back(end.GetDeltaSince(start)); |
292 | 2 | LOG(INFO) << "Elapsed time is: " << elapsed_times.back().ToMilliseconds(); |
293 | 2 | } |
294 | 1 | ASSERT_LT(elapsed_times[1].ToMilliseconds(), elapsed_times[0].ToMilliseconds() / 100); |
295 | 1 | } |
296 | | |
297 | | } // namespace yb |