]>
git.proxmox.com Git - ceph.git/blob - ceph/src/test/common/Throttle.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
8 * Author: Loic Dachary <loic@dachary.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library Public License for more details.
31 #include "gtest/gtest.h"
32 #include "common/Thread.h"
33 #include "common/Throttle.h"
34 #include "common/ceph_argparse.h"
36 class ThrottleTest
: public ::testing::Test
{
39 class Thread_get
: public Thread
{
45 Thread_get(Throttle
& _throttle
, int64_t _count
) :
46 throttle(_throttle
), count(_count
) {}
48 void *entry() override
{
50 waited
= throttle
.get(count
);
57 TEST_F(ThrottleTest
, Throttle
) {
58 int64_t throttle_max
= 10;
59 Throttle
throttle(g_ceph_context
, "throttle", throttle_max
);
60 ASSERT_EQ(throttle
.get_max(), throttle_max
);
61 ASSERT_EQ(throttle
.get_current(), 0);
64 TEST_F(ThrottleTest
, take
) {
65 int64_t throttle_max
= 10;
66 Throttle
throttle(g_ceph_context
, "throttle", throttle_max
);
67 ASSERT_EQ(throttle
.take(throttle_max
), throttle_max
);
68 ASSERT_EQ(throttle
.take(throttle_max
), throttle_max
* 2);
71 TEST_F(ThrottleTest
, get
) {
72 int64_t throttle_max
= 10;
73 Throttle
throttle(g_ceph_context
, "throttle");
75 // test increasing max from 0 to throttle_max
77 ASSERT_FALSE(throttle
.get(throttle_max
, throttle_max
));
78 ASSERT_EQ(throttle
.get_max(), throttle_max
);
79 ASSERT_EQ(throttle
.put(throttle_max
), 0);
82 ASSERT_FALSE(throttle
.get(5));
83 ASSERT_EQ(throttle
.put(5), 0);
85 ASSERT_FALSE(throttle
.get(throttle_max
));
86 ASSERT_FALSE(throttle
.get_or_fail(1));
87 ASSERT_FALSE(throttle
.get(1, throttle_max
+ 1));
88 ASSERT_EQ(throttle
.put(throttle_max
+ 1), 0);
89 ASSERT_FALSE(throttle
.get(0, throttle_max
));
90 ASSERT_FALSE(throttle
.get(throttle_max
));
91 ASSERT_FALSE(throttle
.get_or_fail(1));
92 ASSERT_EQ(throttle
.put(throttle_max
), 0);
99 cout
<< "Trying (1) with delay " << delay
<< "us\n";
101 ASSERT_FALSE(throttle
.get(throttle_max
));
102 ASSERT_FALSE(throttle
.get_or_fail(throttle_max
));
104 Thread_get
t(throttle
, 7);
105 t
.create("t_throttle_1");
107 ASSERT_EQ(throttle
.put(throttle_max
), 0);
110 if (!(waited
= t
.waited
))
116 cout
<< "Trying (2) with delay " << delay
<< "us\n";
118 ASSERT_FALSE(throttle
.get(throttle_max
/ 2));
119 ASSERT_FALSE(throttle
.get_or_fail(throttle_max
));
121 Thread_get
t(throttle
, throttle_max
);
122 t
.create("t_throttle_2");
125 Thread_get
u(throttle
, 1);
126 u
.create("u_throttle_2");
129 throttle
.put(throttle_max
/ 2);
134 if (!(waited
= t
.waited
&& u
.waited
))
140 TEST_F(ThrottleTest
, get_or_fail
) {
142 Throttle
throttle(g_ceph_context
, "throttle");
144 ASSERT_TRUE(throttle
.get_or_fail(5));
145 ASSERT_TRUE(throttle
.get_or_fail(5));
149 int64_t throttle_max
= 10;
150 Throttle
throttle(g_ceph_context
, "throttle", throttle_max
);
152 ASSERT_TRUE(throttle
.get_or_fail(throttle_max
));
153 ASSERT_EQ(throttle
.put(throttle_max
), 0);
155 ASSERT_TRUE(throttle
.get_or_fail(throttle_max
* 2));
156 ASSERT_FALSE(throttle
.get_or_fail(1));
157 ASSERT_FALSE(throttle
.get_or_fail(throttle_max
* 2));
158 ASSERT_EQ(throttle
.put(throttle_max
* 2), 0);
160 ASSERT_TRUE(throttle
.get_or_fail(throttle_max
));
161 ASSERT_FALSE(throttle
.get_or_fail(1));
162 ASSERT_EQ(throttle
.put(throttle_max
), 0);
166 TEST_F(ThrottleTest
, wait
) {
167 int64_t throttle_max
= 10;
168 Throttle
throttle(g_ceph_context
, "throttle");
170 // test increasing max from 0 to throttle_max
172 ASSERT_FALSE(throttle
.wait(throttle_max
));
173 ASSERT_EQ(throttle
.get_max(), throttle_max
);
176 useconds_t delay
= 1;
181 cout
<< "Trying (3) with delay " << delay
<< "us\n";
183 ASSERT_FALSE(throttle
.get(throttle_max
/ 2));
184 ASSERT_FALSE(throttle
.get_or_fail(throttle_max
));
186 Thread_get
t(throttle
, throttle_max
);
187 t
.create("t_throttle_3");
191 // Throttle::_reset_max(int64_t m) used to contain a test
192 // that blocked the following statement, only if
193 // the argument was greater than throttle_max.
194 // Although a value lower than throttle_max would cover
195 // the same code in _reset_max, the throttle_max * 100
196 // value is left here to demonstrate that the problem
199 throttle
.wait(throttle_max
* 100);
202 ASSERT_EQ(throttle
.get_current(), throttle_max
/ 2);
204 if (!(waited
= t
.waited
)) {
206 // undo the changes we made
207 throttle
.put(throttle_max
/ 2);
208 throttle
.wait(throttle_max
);
213 std::pair
<double, std::chrono::duration
<double> > test_backoff(
214 double low_threshhold
,
215 double high_threshhold
,
216 double expected_throughput
,
217 double high_multiple
,
220 double put_delay_per_count
,
225 std::condition_variable c
;
227 std::list
<uint64_t> in_queue
;
228 bool stop_getters
= false;
229 bool stop_putters
= false;
231 auto wait_time
= std::chrono::duration
<double>(0);
234 uint64_t total_observed_total
= 0;
235 uint64_t total_observations
= 0;
237 BackoffThrottle
throttle(g_ceph_context
, "backoff_throttle_test", 5);
238 bool valid
= throttle
.set_params(
248 auto getter
= [&]() {
249 std::random_device rd
;
250 std::mt19937
gen(rd());
251 std::uniform_int_distribution
<> dis(0, 10);
253 std::unique_lock
<std::mutex
> g(l
);
254 while (!stop_getters
) {
257 uint64_t to_get
= dis(gen
);
258 auto waited
= throttle
.get(to_get
);
264 in_queue
.push_back(to_get
);
269 auto putter
= [&]() {
270 std::unique_lock
<std::mutex
> g(l
);
271 while (!stop_putters
|| !in_queue
.empty()) {
272 if (in_queue
.empty()) {
277 uint64_t c
= in_queue
.front();
279 total_observed_total
+= total
;
280 total_observations
++;
281 in_queue
.pop_front();
282 ceph_assert(total
<= max
);
285 std::this_thread::sleep_for(
286 c
* std::chrono::duration
<double>(put_delay_per_count
*putters
));
294 vector
<std::thread
> gts(getters
);
295 for (auto &&i
: gts
) i
= std::thread(getter
);
297 vector
<std::thread
> pts(putters
);
298 for (auto &&i
: pts
) i
= std::thread(putter
);
300 std::this_thread::sleep_for(std::chrono::duration
<double>(5));
302 std::unique_lock
<std::mutex
> g(l
);
306 for (auto &&i
: gts
) i
.join();
310 std::unique_lock
<std::mutex
> g(l
);
314 for (auto &&i
: pts
) i
.join();
318 ((double)total_observed_total
)/((double)total_observations
),
322 TEST(BackoffThrottle
, undersaturated
)
324 auto results
= test_backoff(
334 ASSERT_LT(results
.first
, 45);
335 ASSERT_GT(results
.first
, 35);
336 ASSERT_LT(results
.second
.count(), 0.0002);
337 ASSERT_GT(results
.second
.count(), 0.00005);
340 TEST(BackoffThrottle
, balanced
)
342 auto results
= test_backoff(
352 ASSERT_LT(results
.first
, 60);
353 ASSERT_GT(results
.first
, 40);
354 ASSERT_LT(results
.second
.count(), 0.002);
355 ASSERT_GT(results
.second
.count(), 0.0005);
358 TEST(BackoffThrottle
, oversaturated
)
360 auto results
= test_backoff(
370 ASSERT_LT(results
.first
, 101);
371 ASSERT_GT(results
.first
, 85);
372 ASSERT_LT(results
.second
.count(), 0.002);
373 ASSERT_GT(results
.second
.count(), 0.0005);
378 * compile-command: "cd ../.. ;
379 * make unittest_throttle ;
380 * ./unittest_throttle # --gtest_filter=ThrottleTest.take \
381 * --log-to-stderr=true --debug-filestore=20