]>
git.proxmox.com Git - ceph.git/blob - ceph/src/test/rgw/test_rgw_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) 2018 Red Hat, Inc.
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
15 #include "rgw/rgw_aio_throttle.h"
19 #include "include/scope_guard.h"
21 #include <spawn/spawn.hpp>
22 #include <gtest/gtest.h>
24 struct RadosEnv
: public ::testing::Environment
{
26 static constexpr auto poolname
= "ceph_test_rgw_throttle";
28 static std::optional
<RGWSI_RADOS
> rados
;
30 void SetUp() override
{
31 rados
.emplace(g_ceph_context
);
32 const NoDoutPrefix
no_dpp(g_ceph_context
, 1);
33 ASSERT_EQ(0, rados
->start(null_yield
, &no_dpp
));
34 int r
= rados
->pool({poolname
}).create(&no_dpp
);
39 void TearDown() override
{
44 std::optional
<RGWSI_RADOS
> RadosEnv::rados
;
46 auto *const rados_env
= ::testing::AddGlobalTestEnvironment(new RadosEnv
);
48 // test fixture for global setup/teardown
49 class RadosFixture
: public ::testing::Test
{
51 RGWSI_RADOS::Obj
make_obj(const std::string
& oid
) {
52 auto obj
= RadosEnv::rados
->obj({{RadosEnv::poolname
}, oid
});
53 const NoDoutPrefix
no_dpp(g_ceph_context
, 1);
54 ceph_assert_always(0 == obj
.open(&no_dpp
));
59 using Aio_Throttle
= RadosFixture
;
63 struct scoped_completion
{
65 AioResult
* result
= nullptr;
66 ~scoped_completion() { if (aio
) { complete(-ECANCELED
); } }
67 void complete(int r
) {
74 auto wait_on(scoped_completion
& c
) {
75 return [&c
] (Aio
* aio
, AioResult
& r
) { c
.aio
= aio
; c
.result
= &r
; };
78 auto wait_for(boost::asio::io_context
& context
, ceph::timespan duration
) {
79 return [&context
, duration
] (Aio
* aio
, AioResult
& r
) {
80 using Clock
= ceph::coarse_mono_clock
;
81 using Timer
= boost::asio::basic_waitable_timer
<Clock
>;
82 auto t
= std::make_unique
<Timer
>(context
);
83 t
->expires_after(duration
);
84 t
->async_wait([aio
, &r
, t
=std::move(t
)] (boost::system::error_code ec
) {
85 if (ec
!= boost::asio::error::operation_aborted
) {
92 TEST_F(Aio_Throttle
, NoThrottleUpToMax
)
94 BlockingAioThrottle
throttle(4);
95 auto obj
= make_obj(__PRETTY_FUNCTION__
);
97 scoped_completion op1
;
98 auto c1
= throttle
.get(obj
, wait_on(op1
), 1, 0);
99 EXPECT_TRUE(c1
.empty());
100 scoped_completion op2
;
101 auto c2
= throttle
.get(obj
, wait_on(op2
), 1, 0);
102 EXPECT_TRUE(c2
.empty());
103 scoped_completion op3
;
104 auto c3
= throttle
.get(obj
, wait_on(op3
), 1, 0);
105 EXPECT_TRUE(c3
.empty());
106 scoped_completion op4
;
107 auto c4
= throttle
.get(obj
, wait_on(op4
), 1, 0);
108 EXPECT_TRUE(c4
.empty());
109 // no completions because no ops had to wait
110 auto c5
= throttle
.poll();
111 EXPECT_TRUE(c5
.empty());
113 auto completions
= throttle
.drain();
114 ASSERT_EQ(4u, completions
.size());
115 for (auto& c
: completions
) {
116 EXPECT_EQ(-ECANCELED
, c
.result
);
120 TEST_F(Aio_Throttle
, CostOverWindow
)
122 BlockingAioThrottle
throttle(4);
123 auto obj
= make_obj(__PRETTY_FUNCTION__
);
125 scoped_completion op
;
126 auto c
= throttle
.get(obj
, wait_on(op
), 8, 0);
127 ASSERT_EQ(1u, c
.size());
128 EXPECT_EQ(-EDEADLK
, c
.front().result
);
131 TEST_F(Aio_Throttle
, ThrottleOverMax
)
133 constexpr uint64_t window
= 4;
134 BlockingAioThrottle
throttle(window
);
136 auto obj
= make_obj(__PRETTY_FUNCTION__
);
138 // issue 32 writes, and verify that max_outstanding <= window
139 constexpr uint64_t total
= 32;
140 uint64_t max_outstanding
= 0;
141 uint64_t outstanding
= 0;
144 boost::asio::io_context context
;
145 using Executor
= boost::asio::io_context::executor_type
;
146 using Work
= boost::asio::executor_work_guard
<Executor
>;
147 std::optional
<Work
> work(context
.get_executor());
148 std::thread
worker([&context
] { context
.run(); });
149 auto g
= make_scope_guard([&work
, &worker
] {
154 for (uint64_t i
= 0; i
< total
; i
++) {
155 using namespace std::chrono_literals
;
156 auto c
= throttle
.get(obj
, wait_for(context
, 10ms
), 1, 0);
158 outstanding
-= c
.size();
159 if (max_outstanding
< outstanding
) {
160 max_outstanding
= outstanding
;
163 auto c
= throttle
.drain();
164 outstanding
-= c
.size();
165 EXPECT_EQ(0u, outstanding
);
166 EXPECT_EQ(window
, max_outstanding
);
169 TEST_F(Aio_Throttle
, YieldCostOverWindow
)
171 auto obj
= make_obj(__PRETTY_FUNCTION__
);
173 boost::asio::io_context context
;
174 spawn::spawn(context
,
175 [&] (yield_context yield
) {
176 YieldingAioThrottle
throttle(4, context
, yield
);
177 scoped_completion op
;
178 auto c
= throttle
.get(obj
, wait_on(op
), 8, 0);
179 ASSERT_EQ(1u, c
.size());
180 EXPECT_EQ(-EDEADLK
, c
.front().result
);
184 TEST_F(Aio_Throttle
, YieldingThrottleOverMax
)
186 constexpr uint64_t window
= 4;
188 auto obj
= make_obj(__PRETTY_FUNCTION__
);
190 // issue 32 writes, and verify that max_outstanding <= window
191 constexpr uint64_t total
= 32;
192 uint64_t max_outstanding
= 0;
193 uint64_t outstanding
= 0;
195 boost::asio::io_context context
;
196 spawn::spawn(context
,
197 [&] (yield_context yield
) {
198 YieldingAioThrottle
throttle(window
, context
, yield
);
199 for (uint64_t i
= 0; i
< total
; i
++) {
200 using namespace std::chrono_literals
;
201 auto c
= throttle
.get(obj
, wait_for(context
, 10ms
), 1, 0);
203 outstanding
-= c
.size();
204 if (max_outstanding
< outstanding
) {
205 max_outstanding
= outstanding
;
208 auto c
= throttle
.drain();
209 outstanding
-= c
.size();
211 context
.poll(); // run until we block
212 EXPECT_EQ(window
, outstanding
);
215 EXPECT_EQ(0u, outstanding
);
216 EXPECT_EQ(window
, max_outstanding
);