]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/rgw/test_rgw_throttle.cc
import ceph pacific 16.2.5
[ceph.git] / ceph / src / test / rgw / test_rgw_throttle.cc
CommitLineData
11fdf7f2
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2018 Red Hat, Inc.
7 *
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.
12 *
13 */
14
15#include "rgw/rgw_aio_throttle.h"
11fdf7f2 16
9f95a23c
TL
17#include <optional>
18#include <thread>
19#include "include/scope_guard.h"
11fdf7f2 20
9f95a23c 21#include <spawn/spawn.hpp>
11fdf7f2
TL
22#include <gtest/gtest.h>
23
24struct RadosEnv : public ::testing::Environment {
25 public:
26 static constexpr auto poolname = "ceph_test_rgw_throttle";
27
28 static std::optional<RGWSI_RADOS> rados;
29
30 void SetUp() override {
31 rados.emplace(g_ceph_context);
b3b6e05e
TL
32 const NoDoutPrefix no_dpp(g_ceph_context, 1);
33 ASSERT_EQ(0, rados->start(null_yield, &no_dpp));
11fdf7f2
TL
34 int r = rados->pool({poolname}).create();
35 if (r == -EEXIST)
36 r = 0;
37 ASSERT_EQ(0, r);
38 }
9f95a23c
TL
39 void TearDown() override {
40 rados->shutdown();
11fdf7f2
TL
41 rados.reset();
42 }
43};
44std::optional<RGWSI_RADOS> RadosEnv::rados;
45
46auto *const rados_env = ::testing::AddGlobalTestEnvironment(new RadosEnv);
47
48// test fixture for global setup/teardown
49class RadosFixture : public ::testing::Test {
50 protected:
51 RGWSI_RADOS::Obj make_obj(const std::string& oid) {
52 auto obj = RadosEnv::rados->obj({{RadosEnv::poolname}, oid});
b3b6e05e
TL
53 const NoDoutPrefix no_dpp(g_ceph_context, 1);
54 ceph_assert_always(0 == obj.open(&no_dpp));
11fdf7f2
TL
55 return obj;
56 }
57};
58
59using Aio_Throttle = RadosFixture;
60
61namespace rgw {
62
9f95a23c
TL
63struct scoped_completion {
64 Aio* aio = nullptr;
65 AioResult* result = nullptr;
66 ~scoped_completion() { if (aio) { complete(-ECANCELED); } }
67 void complete(int r) {
68 result->result = r;
69 aio->put(*result);
70 aio = nullptr;
71 }
72};
73
74auto wait_on(scoped_completion& c) {
75 return [&c] (Aio* aio, AioResult& r) { c.aio = aio; c.result = &r; };
76}
77
78auto 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) {
86 aio->put(r);
87 }
88 });
89 };
90}
91
11fdf7f2
TL
92TEST_F(Aio_Throttle, NoThrottleUpToMax)
93{
9f95a23c 94 BlockingAioThrottle throttle(4);
11fdf7f2
TL
95 auto obj = make_obj(__PRETTY_FUNCTION__);
96 {
9f95a23c
TL
97 scoped_completion op1;
98 auto c1 = throttle.get(obj, wait_on(op1), 1, 0);
11fdf7f2 99 EXPECT_TRUE(c1.empty());
9f95a23c
TL
100 scoped_completion op2;
101 auto c2 = throttle.get(obj, wait_on(op2), 1, 0);
11fdf7f2 102 EXPECT_TRUE(c2.empty());
9f95a23c
TL
103 scoped_completion op3;
104 auto c3 = throttle.get(obj, wait_on(op3), 1, 0);
11fdf7f2 105 EXPECT_TRUE(c3.empty());
9f95a23c
TL
106 scoped_completion op4;
107 auto c4 = throttle.get(obj, wait_on(op4), 1, 0);
11fdf7f2
TL
108 EXPECT_TRUE(c4.empty());
109 // no completions because no ops had to wait
110 auto c5 = throttle.poll();
9f95a23c 111 EXPECT_TRUE(c5.empty());
11fdf7f2
TL
112 }
113 auto completions = throttle.drain();
114 ASSERT_EQ(4u, completions.size());
115 for (auto& c : completions) {
9f95a23c 116 EXPECT_EQ(-ECANCELED, c.result);
11fdf7f2
TL
117 }
118}
119
120TEST_F(Aio_Throttle, CostOverWindow)
121{
9f95a23c 122 BlockingAioThrottle throttle(4);
11fdf7f2
TL
123 auto obj = make_obj(__PRETTY_FUNCTION__);
124
9f95a23c
TL
125 scoped_completion op;
126 auto c = throttle.get(obj, wait_on(op), 8, 0);
11fdf7f2
TL
127 ASSERT_EQ(1u, c.size());
128 EXPECT_EQ(-EDEADLK, c.front().result);
129}
130
131TEST_F(Aio_Throttle, ThrottleOverMax)
132{
133 constexpr uint64_t window = 4;
9f95a23c 134 BlockingAioThrottle throttle(window);
11fdf7f2
TL
135
136 auto obj = make_obj(__PRETTY_FUNCTION__);
137
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;
142
9f95a23c
TL
143 // timer thread
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] {
150 work.reset();
151 worker.join();
152 });
153
11fdf7f2 154 for (uint64_t i = 0; i < total; i++) {
9f95a23c
TL
155 using namespace std::chrono_literals;
156 auto c = throttle.get(obj, wait_for(context, 10ms), 1, 0);
11fdf7f2
TL
157 outstanding++;
158 outstanding -= c.size();
159 if (max_outstanding < outstanding) {
160 max_outstanding = outstanding;
161 }
162 }
163 auto c = throttle.drain();
164 outstanding -= c.size();
165 EXPECT_EQ(0u, outstanding);
166 EXPECT_EQ(window, max_outstanding);
167}
168
9f95a23c
TL
169TEST_F(Aio_Throttle, YieldCostOverWindow)
170{
171 auto obj = make_obj(__PRETTY_FUNCTION__);
172
173 boost::asio::io_context context;
174 spawn::spawn(context,
175 [&] (spawn::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);
181 });
182}
183
184TEST_F(Aio_Throttle, YieldingThrottleOverMax)
185{
186 constexpr uint64_t window = 4;
187
188 auto obj = make_obj(__PRETTY_FUNCTION__);
189
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;
194
195 boost::asio::io_context context;
196 spawn::spawn(context,
197 [&] (spawn::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);
202 outstanding++;
203 outstanding -= c.size();
204 if (max_outstanding < outstanding) {
205 max_outstanding = outstanding;
206 }
207 }
208 auto c = throttle.drain();
209 outstanding -= c.size();
210 });
211 context.poll(); // run until we block
212 EXPECT_EQ(window, outstanding);
213
214 context.run();
215 EXPECT_EQ(0u, outstanding);
216 EXPECT_EQ(window, max_outstanding);
217}
9f95a23c 218
11fdf7f2 219} // namespace rgw