]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/osd/TestMClockScheduler.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / test / osd / TestMClockScheduler.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2
3 #include <chrono>
4
5 #include "gtest/gtest.h"
6
7 #include "global/global_context.h"
8 #include "global/global_init.h"
9 #include "common/common_init.h"
10
11 #include "osd/scheduler/mClockScheduler.h"
12 #include "osd/scheduler/OpSchedulerItem.h"
13
14 using namespace ceph::osd::scheduler;
15
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);
22
23 ::testing::InitGoogleTest(&argc, argv);
24 return RUN_ALL_TESTS();
25 }
26
27
28 class mClockSchedulerTest : public testing::Test {
29 public:
30 int whoami;
31 uint32_t num_shards;
32 int shard_id;
33 bool is_rotational;
34 MonClient *monc;
35 mClockScheduler q;
36
37 uint64_t client1;
38 uint64_t client2;
39 uint64_t client3;
40
41 mClockSchedulerTest() :
42 whoami(0),
43 num_shards(1),
44 shard_id(0),
45 is_rotational(false),
46 monc(nullptr),
47 q(g_ceph_context, whoami, num_shards, shard_id, is_rotational, monc),
48 client1(1001),
49 client2(9999),
50 client3(100000001)
51 {}
52
53 struct MockDmclockItem : public PGOpQueueable {
54 op_scheduler_class scheduler_class;
55
56 MockDmclockItem(op_scheduler_class _scheduler_class) :
57 PGOpQueueable(spg_t()),
58 scheduler_class(_scheduler_class) {}
59
60 MockDmclockItem()
61 : MockDmclockItem(op_scheduler_class::background_best_effort) {}
62
63 ostream &print(ostream &rhs) const final { return rhs; }
64
65 std::optional<OpRequestRef> maybe_get_op() const final {
66 return std::nullopt;
67 }
68
69 op_scheduler_class get_scheduler_class() const final {
70 return scheduler_class;
71 }
72
73 void run(OSD *osd, OSDShard *sdata, PGRef& pg, ThreadPool::TPHandle &handle) final {}
74 };
75 };
76
77 template <typename... Args>
78 OpSchedulerItem create_item(
79 epoch_t e, uint64_t owner, Args&&... args)
80 {
81 return OpSchedulerItem(
82 std::make_unique<mClockSchedulerTest::MockDmclockItem>(
83 std::forward<Args>(args)...),
84 12, 12,
85 utime_t(), owner, e);
86 }
87
88 template <typename... Args>
89 OpSchedulerItem create_high_prio_item(
90 unsigned priority, epoch_t e, uint64_t owner, Args&&... args)
91 {
92 // Create high priority item for testing high prio queue
93 return OpSchedulerItem(
94 std::make_unique<mClockSchedulerTest::MockDmclockItem>(
95 std::forward<Args>(args)...),
96 12, priority,
97 utime_t(), owner, e);
98 }
99
100 OpSchedulerItem get_item(WorkItem item)
101 {
102 return std::move(std::get<OpSchedulerItem>(item));
103 }
104
105 TEST_F(mClockSchedulerTest, TestEmpty) {
106 ASSERT_TRUE(q.empty());
107
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));
111 }
112
113 ASSERT_FALSE(q.empty());
114
115 std::list<OpSchedulerItem> reqs;
116
117 reqs.push_back(get_item(q.dequeue()));
118 reqs.push_back(get_item(q.dequeue()));
119
120 ASSERT_EQ(2u, reqs.size());
121 ASSERT_FALSE(q.empty());
122
123 for (auto &&i : reqs) {
124 q.enqueue_front(std::move(i));
125 }
126 reqs.clear();
127
128 ASSERT_FALSE(q.empty());
129
130 for (int i = 0; i < 3; ++i) {
131 ASSERT_FALSE(q.empty());
132 q.dequeue();
133 }
134
135 ASSERT_TRUE(q.empty());
136 }
137
138 TEST_F(mClockSchedulerTest, TestSingleClientOrderedEnqueueDequeue) {
139 ASSERT_TRUE(q.empty());
140
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));
144 }
145
146 auto r = get_item(q.dequeue());
147 ASSERT_EQ(100u, r.get_map_epoch());
148
149 r = get_item(q.dequeue());
150 ASSERT_EQ(101u, r.get_map_epoch());
151
152 r = get_item(q.dequeue());
153 ASSERT_EQ(102u, r.get_map_epoch());
154
155 r = get_item(q.dequeue());
156 ASSERT_EQ(103u, r.get_map_epoch());
157
158 r = get_item(q.dequeue());
159 ASSERT_EQ(104u, r.get_map_epoch());
160 }
161
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));
168 }
169 }
170
171 std::map<uint64_t, epoch_t> next;
172 for (auto &&c: {client1, client2, client3}) {
173 next[c] = 0;
174 }
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());
182 niter->second++;
183 }
184 ASSERT_TRUE(q.empty());
185 }
186
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));
192 }
193
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());
198
199 r = get_item(q.dequeue());
200 ASSERT_EQ(203u, r.get_map_epoch());
201
202 r = get_item(q.dequeue());
203 ASSERT_EQ(202u, r.get_map_epoch());
204
205 r = get_item(q.dequeue());
206 ASSERT_EQ(201u, r.get_map_epoch());
207
208 r = get_item(q.dequeue());
209 ASSERT_EQ(200u, r.get_map_epoch());
210
211 ASSERT_TRUE(q.empty());
212 }
213
214 TEST_F(mClockSchedulerTest, TestAllQueuesEnqueueDequeue) {
215 ASSERT_TRUE(q.empty());
216
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));
221 }
222
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));
227 }
228
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));
233 }
234
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());
241
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());
248
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());
254
255 ASSERT_TRUE(q.empty());
256 }