1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
5 #include "gtest/gtest.h"
7 #include "global/global_context.h"
8 #include "global/global_init.h"
9 #include "common/common_init.h"
11 #include "osd/scheduler/mClockScheduler.h"
12 #include "osd/scheduler/OpSchedulerItem.h"
14 using namespace ceph::osd::scheduler
;
16 int main(int argc
, char **argv
) {
17 std::vector
<const char*> args(argv
, argv
+argc
);
18 auto cct
= global_init(nullptr, args
, CEPH_ENTITY_TYPE_OSD
,
19 CODE_ENVIRONMENT_UTILITY
,
20 CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
);
21 common_init_finish(g_ceph_context
);
23 ::testing::InitGoogleTest(&argc
, argv
);
24 return RUN_ALL_TESTS();
28 class mClockSchedulerTest
: public testing::Test
{
41 mClockSchedulerTest() :
47 q(g_ceph_context
, whoami
, num_shards
, shard_id
, is_rotational
, monc
),
53 struct MockDmclockItem
: public PGOpQueueable
{
54 op_scheduler_class scheduler_class
;
56 MockDmclockItem(op_scheduler_class _scheduler_class
) :
57 PGOpQueueable(spg_t()),
58 scheduler_class(_scheduler_class
) {}
61 : MockDmclockItem(op_scheduler_class::background_best_effort
) {}
63 ostream
&print(ostream
&rhs
) const final
{ return rhs
; }
65 std::optional
<OpRequestRef
> maybe_get_op() const final
{
69 op_scheduler_class
get_scheduler_class() const final
{
70 return scheduler_class
;
73 void run(OSD
*osd
, OSDShard
*sdata
, PGRef
& pg
, ThreadPool::TPHandle
&handle
) final
{}
77 template <typename
... Args
>
78 OpSchedulerItem
create_item(
79 epoch_t e
, uint64_t owner
, Args
&&... args
)
81 return OpSchedulerItem(
82 std::make_unique
<mClockSchedulerTest::MockDmclockItem
>(
83 std::forward
<Args
>(args
)...),
88 template <typename
... Args
>
89 OpSchedulerItem
create_high_prio_item(
90 unsigned priority
, epoch_t e
, uint64_t owner
, Args
&&... args
)
92 // Create high priority item for testing high prio queue
93 return OpSchedulerItem(
94 std::make_unique
<mClockSchedulerTest::MockDmclockItem
>(
95 std::forward
<Args
>(args
)...),
100 OpSchedulerItem
get_item(WorkItem item
)
102 return std::move(std::get
<OpSchedulerItem
>(item
));
105 TEST_F(mClockSchedulerTest
, TestEmpty
) {
106 ASSERT_TRUE(q
.empty());
108 for (unsigned i
= 100; i
< 105; i
+=2) {
109 q
.enqueue(create_item(i
, client1
, op_scheduler_class::client
));
110 std::this_thread::sleep_for(std::chrono::microseconds(1));
113 ASSERT_FALSE(q
.empty());
115 std::list
<OpSchedulerItem
> reqs
;
117 reqs
.push_back(get_item(q
.dequeue()));
118 reqs
.push_back(get_item(q
.dequeue()));
120 ASSERT_EQ(2u, reqs
.size());
121 ASSERT_FALSE(q
.empty());
123 for (auto &&i
: reqs
) {
124 q
.enqueue_front(std::move(i
));
128 ASSERT_FALSE(q
.empty());
130 for (int i
= 0; i
< 3; ++i
) {
131 ASSERT_FALSE(q
.empty());
135 ASSERT_TRUE(q
.empty());
138 TEST_F(mClockSchedulerTest
, TestSingleClientOrderedEnqueueDequeue
) {
139 ASSERT_TRUE(q
.empty());
141 for (unsigned i
= 100; i
< 105; ++i
) {
142 q
.enqueue(create_item(i
, client1
, op_scheduler_class::client
));
143 std::this_thread::sleep_for(std::chrono::microseconds(1));
146 auto r
= get_item(q
.dequeue());
147 ASSERT_EQ(100u, r
.get_map_epoch());
149 r
= get_item(q
.dequeue());
150 ASSERT_EQ(101u, r
.get_map_epoch());
152 r
= get_item(q
.dequeue());
153 ASSERT_EQ(102u, r
.get_map_epoch());
155 r
= get_item(q
.dequeue());
156 ASSERT_EQ(103u, r
.get_map_epoch());
158 r
= get_item(q
.dequeue());
159 ASSERT_EQ(104u, r
.get_map_epoch());
162 TEST_F(mClockSchedulerTest
, TestMultiClientOrderedEnqueueDequeue
) {
163 const unsigned NUM
= 1000;
164 for (unsigned i
= 0; i
< NUM
; ++i
) {
165 for (auto &&c
: {client1
, client2
, client3
}) {
166 q
.enqueue(create_item(i
, c
));
167 std::this_thread::sleep_for(std::chrono::microseconds(1));
171 std::map
<uint64_t, epoch_t
> next
;
172 for (auto &&c
: {client1
, client2
, client3
}) {
175 for (unsigned i
= 0; i
< NUM
* 3; ++i
) {
176 ASSERT_FALSE(q
.empty());
177 auto r
= get_item(q
.dequeue());
178 auto owner
= r
.get_owner();
179 auto niter
= next
.find(owner
);
180 ASSERT_FALSE(niter
== next
.end());
181 ASSERT_EQ(niter
->second
, r
.get_map_epoch());
184 ASSERT_TRUE(q
.empty());
187 TEST_F(mClockSchedulerTest
, TestHighPriorityQueueEnqueueDequeue
) {
188 ASSERT_TRUE(q
.empty());
189 for (unsigned i
= 200; i
< 205; ++i
) {
190 q
.enqueue(create_high_prio_item(i
, i
, client1
, op_scheduler_class::client
));
191 std::this_thread::sleep_for(std::chrono::milliseconds(1));
194 ASSERT_FALSE(q
.empty());
195 // Higher priority ops should be dequeued first
196 auto r
= get_item(q
.dequeue());
197 ASSERT_EQ(204u, r
.get_map_epoch());
199 r
= get_item(q
.dequeue());
200 ASSERT_EQ(203u, r
.get_map_epoch());
202 r
= get_item(q
.dequeue());
203 ASSERT_EQ(202u, r
.get_map_epoch());
205 r
= get_item(q
.dequeue());
206 ASSERT_EQ(201u, r
.get_map_epoch());
208 r
= get_item(q
.dequeue());
209 ASSERT_EQ(200u, r
.get_map_epoch());
211 ASSERT_TRUE(q
.empty());
214 TEST_F(mClockSchedulerTest
, TestAllQueuesEnqueueDequeue
) {
215 ASSERT_TRUE(q
.empty());
217 // Insert ops into the mClock queue
218 for (unsigned i
= 100; i
< 102; ++i
) {
219 q
.enqueue(create_item(i
, client1
, op_scheduler_class::client
));
220 std::this_thread::sleep_for(std::chrono::microseconds(1));
223 // Insert Immediate ops
224 for (unsigned i
= 103; i
< 105; ++i
) {
225 q
.enqueue(create_item(i
, client1
, op_scheduler_class::immediate
));
226 std::this_thread::sleep_for(std::chrono::microseconds(1));
229 // Insert ops into the high queue
230 for (unsigned i
= 200; i
< 202; ++i
) {
231 q
.enqueue(create_high_prio_item(i
, i
, client1
, op_scheduler_class::client
));
232 std::this_thread::sleep_for(std::chrono::milliseconds(1));
235 ASSERT_FALSE(q
.empty());
236 auto r
= get_item(q
.dequeue());
237 // Ops classified as Immediate should be dequeued first
238 ASSERT_EQ(103u, r
.get_map_epoch());
239 r
= get_item(q
.dequeue());
240 ASSERT_EQ(104u, r
.get_map_epoch());
242 // High priority queue should be dequeued second
243 // higher priority operation first
244 r
= get_item(q
.dequeue());
245 ASSERT_EQ(201u, r
.get_map_epoch());
246 r
= get_item(q
.dequeue());
247 ASSERT_EQ(200u, r
.get_map_epoch());
249 // mClock queue will be dequeued last
250 r
= get_item(q
.dequeue());
251 ASSERT_EQ(100u, r
.get_map_epoch());
252 r
= get_item(q
.dequeue());
253 ASSERT_EQ(101u, r
.get_map_epoch());
255 ASSERT_TRUE(q
.empty());