]>
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.
24 #include "gtest/gtest.h"
25 #include "common/Mutex.h"
26 #include "common/Thread.h"
27 #include "common/Throttle.h"
28 #include "common/ceph_argparse.h"
29 #include "common/backport14.h"
38 class ThrottleTest
: public ::testing::Test
{
41 class Thread_get
: public Thread
{
47 Thread_get(Throttle
& _throttle
, int64_t _count
) :
54 void *entry() override
{
56 waited
= throttle
.get(count
);
63 TEST_F(ThrottleTest
, Throttle
) {
64 int64_t throttle_max
= 10;
65 Throttle
throttle(g_ceph_context
, "throttle", throttle_max
);
66 ASSERT_EQ(throttle
.get_max(), throttle_max
);
67 ASSERT_EQ(throttle
.get_current(), 0);
70 TEST_F(ThrottleTest
, take
) {
71 int64_t throttle_max
= 10;
72 Throttle
throttle(g_ceph_context
, "throttle", throttle_max
);
73 ASSERT_EQ(throttle
.take(throttle_max
), throttle_max
);
74 ASSERT_EQ(throttle
.take(throttle_max
), throttle_max
* 2);
77 TEST_F(ThrottleTest
, get
) {
78 int64_t throttle_max
= 10;
79 Throttle
throttle(g_ceph_context
, "throttle");
81 // test increasing max from 0 to throttle_max
83 ASSERT_FALSE(throttle
.get(throttle_max
, throttle_max
));
84 ASSERT_EQ(throttle
.get_max(), throttle_max
);
85 ASSERT_EQ(throttle
.put(throttle_max
), 0);
88 ASSERT_FALSE(throttle
.get(5));
89 ASSERT_EQ(throttle
.put(5), 0);
91 ASSERT_FALSE(throttle
.get(throttle_max
));
92 ASSERT_FALSE(throttle
.get_or_fail(1));
93 ASSERT_FALSE(throttle
.get(1, throttle_max
+ 1));
94 ASSERT_EQ(throttle
.put(throttle_max
+ 1), 0);
95 ASSERT_FALSE(throttle
.get(0, throttle_max
));
96 ASSERT_FALSE(throttle
.get(throttle_max
));
97 ASSERT_FALSE(throttle
.get_or_fail(1));
98 ASSERT_EQ(throttle
.put(throttle_max
), 0);
100 useconds_t delay
= 1;
105 cout
<< "Trying (1) with delay " << delay
<< "us\n";
107 ASSERT_FALSE(throttle
.get(throttle_max
));
108 ASSERT_FALSE(throttle
.get_or_fail(throttle_max
));
110 Thread_get
t(throttle
, 7);
111 t
.create("t_throttle_1");
113 ASSERT_EQ(throttle
.put(throttle_max
), 0);
116 if (!(waited
= t
.waited
))
122 cout
<< "Trying (2) with delay " << delay
<< "us\n";
124 ASSERT_FALSE(throttle
.get(throttle_max
/ 2));
125 ASSERT_FALSE(throttle
.get_or_fail(throttle_max
));
127 Thread_get
t(throttle
, throttle_max
);
128 t
.create("t_throttle_2");
131 Thread_get
u(throttle
, 1);
132 u
.create("u_throttle_2");
135 throttle
.put(throttle_max
/ 2);
140 if (!(waited
= t
.waited
&& u
.waited
))
146 TEST_F(ThrottleTest
, get_or_fail
) {
148 Throttle
throttle(g_ceph_context
, "throttle");
150 ASSERT_TRUE(throttle
.get_or_fail(5));
151 ASSERT_TRUE(throttle
.get_or_fail(5));
155 int64_t throttle_max
= 10;
156 Throttle
throttle(g_ceph_context
, "throttle", throttle_max
);
158 ASSERT_TRUE(throttle
.get_or_fail(throttle_max
));
159 ASSERT_EQ(throttle
.put(throttle_max
), 0);
161 ASSERT_TRUE(throttle
.get_or_fail(throttle_max
* 2));
162 ASSERT_FALSE(throttle
.get_or_fail(1));
163 ASSERT_FALSE(throttle
.get_or_fail(throttle_max
* 2));
164 ASSERT_EQ(throttle
.put(throttle_max
* 2), 0);
166 ASSERT_TRUE(throttle
.get_or_fail(throttle_max
));
167 ASSERT_FALSE(throttle
.get_or_fail(1));
168 ASSERT_EQ(throttle
.put(throttle_max
), 0);
172 TEST_F(ThrottleTest
, wait
) {
173 int64_t throttle_max
= 10;
174 Throttle
throttle(g_ceph_context
, "throttle");
176 // test increasing max from 0 to throttle_max
178 ASSERT_FALSE(throttle
.wait(throttle_max
));
179 ASSERT_EQ(throttle
.get_max(), throttle_max
);
182 useconds_t delay
= 1;
187 cout
<< "Trying (3) with delay " << delay
<< "us\n";
189 ASSERT_FALSE(throttle
.get(throttle_max
/ 2));
190 ASSERT_FALSE(throttle
.get_or_fail(throttle_max
));
192 Thread_get
t(throttle
, throttle_max
);
193 t
.create("t_throttle_3");
197 // Throttle::_reset_max(int64_t m) used to contain a test
198 // that blocked the following statement, only if
199 // the argument was greater than throttle_max.
200 // Although a value lower than throttle_max would cover
201 // the same code in _reset_max, the throttle_max * 100
202 // value is left here to demonstrate that the problem
205 throttle
.wait(throttle_max
* 100);
208 ASSERT_EQ(throttle
.get_current(), throttle_max
/ 2);
210 if (!(waited
= t
.waited
)) {
212 // undo the changes we made
213 throttle
.put(throttle_max
/ 2);
214 throttle
.wait(throttle_max
);
220 TEST_F(ThrottleTest
, destructor
) {
222 int64_t throttle_max
= 10;
223 auto throttle
= ceph::make_unique
<Throttle
>(g_ceph_context
, "throttle",
227 ASSERT_FALSE(throttle
->get(5));
228 unique_ptr
<Thread_get
> t
= ceph::make_unique
<Thread_get
>(*throttle
, 7);
229 t
->create("t_throttle");
231 useconds_t delay
= 1;
234 if (throttle
->get_or_fail(1)) {
245 std::pair
<double, std::chrono::duration
<double> > test_backoff(
246 double low_threshhold
,
247 double high_threshhold
,
248 double expected_throughput
,
249 double high_multiple
,
252 double put_delay_per_count
,
257 std::condition_variable c
;
259 std::list
<uint64_t> in_queue
;
260 bool stop_getters
= false;
261 bool stop_putters
= false;
263 auto wait_time
= std::chrono::duration
<double>(0);
266 uint64_t total_observed_total
= 0;
267 uint64_t total_observations
= 0;
269 BackoffThrottle
throttle(g_ceph_context
, "backoff_throttle_test", 5);
270 bool valid
= throttle
.set_params(
280 auto getter
= [&]() {
281 std::random_device rd
;
282 std::mt19937
gen(rd());
283 std::uniform_int_distribution
<> dis(0, 10);
285 std::unique_lock
<std::mutex
> g(l
);
286 while (!stop_getters
) {
289 uint64_t to_get
= dis(gen
);
290 auto waited
= throttle
.get(to_get
);
296 in_queue
.push_back(to_get
);
301 auto putter
= [&]() {
302 std::unique_lock
<std::mutex
> g(l
);
303 while (!stop_putters
|| !in_queue
.empty()) {
304 if (in_queue
.empty()) {
309 uint64_t c
= in_queue
.front();
311 total_observed_total
+= total
;
312 total_observations
++;
313 in_queue
.pop_front();
314 assert(total
<= max
);
317 std::this_thread::sleep_for(
318 c
* std::chrono::duration
<double>(put_delay_per_count
*putters
));
326 vector
<std::thread
> gts(getters
);
327 for (auto &&i
: gts
) i
= std::thread(getter
);
329 vector
<std::thread
> pts(putters
);
330 for (auto &&i
: pts
) i
= std::thread(putter
);
332 std::this_thread::sleep_for(std::chrono::duration
<double>(5));
334 std::unique_lock
<std::mutex
> g(l
);
338 for (auto &&i
: gts
) i
.join();
342 std::unique_lock
<std::mutex
> g(l
);
346 for (auto &&i
: pts
) i
.join();
350 ((double)total_observed_total
)/((double)total_observations
),
354 TEST(BackoffThrottle
, destruct
) {
356 auto throttle
= ceph::make_unique
<BackoffThrottle
>(
357 g_ceph_context
, "destructor test", 10);
358 ASSERT_TRUE(throttle
->set_params(0.4, 0.6, 1000, 2, 10, 6, nullptr));
368 // No equivalent of get_or_fail()
369 std::this_thread::sleep_for(std::chrono::milliseconds(250));
373 TEST(BackoffThrottle
, undersaturated
)
375 auto results
= test_backoff(
385 ASSERT_LT(results
.first
, 45);
386 ASSERT_GT(results
.first
, 35);
387 ASSERT_LT(results
.second
.count(), 0.0002);
388 ASSERT_GT(results
.second
.count(), 0.00005);
391 TEST(BackoffThrottle
, balanced
)
393 auto results
= test_backoff(
403 ASSERT_LT(results
.first
, 60);
404 ASSERT_GT(results
.first
, 40);
405 ASSERT_LT(results
.second
.count(), 0.002);
406 ASSERT_GT(results
.second
.count(), 0.0005);
409 TEST(BackoffThrottle
, oversaturated
)
411 auto results
= test_backoff(
421 ASSERT_LT(results
.first
, 101);
422 ASSERT_GT(results
.first
, 85);
423 ASSERT_LT(results
.second
.count(), 0.002);
424 ASSERT_GT(results
.second
.count(), 0.0005);
427 TEST(OrderedThrottle
, destruct
) {
429 auto throttle
= ceph::make_unique
<OrderedThrottle
>(1, false);
430 throttle
->start_op(nullptr);
438 // No equivalent of get_or_fail()
439 std::this_thread::sleep_for(std::chrono::milliseconds(250));
445 * compile-command: "cd ../.. ;
446 * make unittest_throttle ;
447 * ./unittest_throttle # --gtest_filter=ThrottleTest.destructor \
448 * --log-to-stderr=true --debug-filestore=20