]> git.proxmox.com Git - ceph.git/blob - ceph/src/dmclock/test/test_dmclock_server.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / dmclock / test / test_dmclock_server.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 /*
5 * Copyright (C) 2016 Red Hat Inc.
6 *
7 * Author: J. Eric Ivancich <ivancich@redhat.com>
8 *
9 * This is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License version
11 * 2.1, as published by the Free Software Foundation. See file
12 * COPYING.
13 */
14
15
16 #include <memory>
17 #include <chrono>
18 #include <iostream>
19 #include <list>
20 #include <vector>
21
22
23 #include "dmclock_server.h"
24 #include "dmclock_util.h"
25 #include "gtest/gtest.h"
26
27 // process control to prevent core dumps during gtest death tests
28 #include "dmcPrCtl.h"
29
30
31 namespace dmc = crimson::dmclock;
32
33
34 // we need a request object; an empty one will do
35 struct Request {
36 };
37
38
39 namespace crimson {
40 namespace dmclock {
41
42 /*
43 * Allows us to test the code provided with the mutex provided locked.
44 */
45 static void test_locked(std::mutex& mtx, std::function<void()> code) {
46 std::unique_lock<std::mutex> l(mtx);
47 code();
48 }
49
50
51 TEST(dmclock_server, bad_tag_deathtest) {
52 using ClientId = int;
53 using Queue = dmc::PullPriorityQueue<ClientId,Request,true>;
54 using QueueRef = std::unique_ptr<Queue>;
55
56 ClientId client1 = 17;
57 ClientId client2 = 18;
58
59 double reservation = 0.0;
60 double weight = 0.0;
61
62 dmc::ClientInfo ci1(reservation, weight, 0.0);
63 dmc::ClientInfo ci2(reservation, weight, 1.0);
64
65 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
66 if (client1 == c) return &ci1;
67 else if (client2 == c) return &ci2;
68 else {
69 ADD_FAILURE() << "got request from neither of two clients";
70 return nullptr;
71 }
72 };
73
74 QueueRef pq(new Queue(client_info_f, AtLimit::Wait));
75 ReqParams req_params(1,1);
76
77 // Disable coredumps
78 PrCtl unset_dumpable;
79
80 EXPECT_DEATH_IF_SUPPORTED(pq->add_request(Request{}, client1, req_params),
81 "Assertion.*reservation.*max_tag.*"
82 "proportion.*max_tag") <<
83 "we should fail if a client tries to generate a reservation tag "
84 "where reservation and proportion are both 0";
85
86
87 EXPECT_DEATH_IF_SUPPORTED(pq->add_request(Request{}, client2, req_params),
88 "Assertion.*reservation.*max_tag.*"
89 "proportion.*max_tag") <<
90 "we should fail if a client tries to generate a reservation tag "
91 "where reservation and proportion are both 0";
92
93 EXPECT_DEATH_IF_SUPPORTED(Queue(client_info_f, AtLimit::Reject),
94 "Assertion.*Reject.*Delayed") <<
95 "we should fail if a client tries to construct a queue with both "
96 "DelayedTagCalc and AtLimit::Reject";
97 }
98
99
100 TEST(dmclock_server, client_idle_erase) {
101 using ClientId = int;
102 using Queue = dmc::PushPriorityQueue<ClientId,Request>;
103 ClientId client = 17;
104 double reservation = 100.0;
105
106 dmc::ClientInfo ci(reservation, 1.0, 0.0);
107 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
108 return &ci;
109 };
110 auto server_ready_f = [] () -> bool { return true; };
111 auto submit_req_f = [] (const ClientId& c,
112 std::unique_ptr<Request> req,
113 dmc::PhaseType phase,
114 uint64_t req_cost) {
115 // empty; do nothing
116 };
117
118 Queue pq(client_info_f,
119 server_ready_f,
120 submit_req_f,
121 std::chrono::seconds(3),
122 std::chrono::seconds(5),
123 std::chrono::seconds(2),
124 AtLimit::Wait);
125
126 auto lock_pq = [&](std::function<void()> code) {
127 test_locked(pq.data_mtx, code);
128 };
129
130
131 /* The timeline should be as follows:
132 *
133 * 0 seconds : request created
134 *
135 * 1 seconds : map is size 1, idle is false
136 *
137 * 2 seconds : clean notes first mark; +2 is base for further calcs
138 *
139 * 4 seconds : clean does nothing except makes another mark
140 *
141 * 5 seconds : when we're secheduled to idle (+2 + 3)
142 *
143 * 6 seconds : clean idles client
144 *
145 * 7 seconds : when we're secheduled to erase (+2 + 5)
146 *
147 * 7 seconds : verified client is idle
148 *
149 * 8 seconds : clean erases client info
150 *
151 * 9 seconds : verified client is erased
152 */
153
154 lock_pq([&] () {
155 EXPECT_EQ(0u, pq.client_map.size()) <<
156 "client map initially has size 0";
157 });
158
159 Request req;
160 dmc::ReqParams req_params(1, 1);
161 EXPECT_EQ(0, pq.add_request_time(req, client, req_params, dmc::get_time()));
162
163 std::this_thread::sleep_for(std::chrono::seconds(1));
164
165 lock_pq([&] () {
166 EXPECT_EQ(1u, pq.client_map.size()) <<
167 "client map has 1 after 1 client";
168 EXPECT_FALSE(pq.client_map.at(client)->idle) <<
169 "initially client map entry shows not idle.";
170 });
171
172 std::this_thread::sleep_for(std::chrono::seconds(6));
173
174 lock_pq([&] () {
175 EXPECT_TRUE(pq.client_map.at(client)->idle) <<
176 "after idle age client map entry shows idle.";
177 });
178
179 std::this_thread::sleep_for(std::chrono::seconds(2));
180
181 lock_pq([&] () {
182 EXPECT_EQ(0u, pq.client_map.size()) <<
183 "client map loses its entry after erase age";
184 });
185 } // TEST
186
187
188 TEST(dmclock_server, delayed_tag_calc) {
189 using ClientId = int;
190 constexpr ClientId client1 = 17;
191
192 using DelayedQueue = PullPriorityQueue<ClientId, Request, true>;
193 using ImmediateQueue = PullPriorityQueue<ClientId, Request, false>;
194
195 ClientInfo info(0.0, 1.0, 1.0);
196 auto client_info_f = [&] (ClientId c) -> const ClientInfo* {
197 return &info;
198 };
199
200 Time t{1};
201 {
202 DelayedQueue queue(client_info_f);
203
204 queue.add_request_time({}, client1, {0,0}, t);
205 queue.add_request_time({}, client1, {0,0}, t + 1);
206 queue.add_request_time({}, client1, {10,10}, t + 2);
207
208 auto pr1 = queue.pull_request(t);
209 ASSERT_TRUE(pr1.is_retn());
210 auto pr2 = queue.pull_request(t + 1);
211 // ReqParams{10,10} from request #3 pushes request #2 over limit by 10s
212 ASSERT_TRUE(pr2.is_future());
213 EXPECT_DOUBLE_EQ(t + 11, pr2.getTime());
214 }
215 {
216 ImmediateQueue queue(client_info_f);
217
218 queue.add_request_time({}, client1, {0,0}, t);
219 queue.add_request_time({}, client1, {0,0}, t + 1);
220 queue.add_request_time({}, client1, {10,10}, t + 2);
221
222 auto pr1 = queue.pull_request(t);
223 ASSERT_TRUE(pr1.is_retn());
224 auto pr2 = queue.pull_request(t + 1);
225 // ReqParams{10,10} from request #3 has no effect on request #2
226 ASSERT_TRUE(pr2.is_retn());
227 auto pr3 = queue.pull_request(t + 2);
228 ASSERT_TRUE(pr3.is_future());
229 EXPECT_DOUBLE_EQ(t + 12, pr3.getTime());
230 }
231 }
232
233 #if 0
234 TEST(dmclock_server, reservation_timing) {
235 using ClientId = int;
236 // NB? PUSH OR PULL
237 using Queue = std::unique_ptr<dmc::PriorityQueue<ClientId,Request>>;
238 using std::chrono::steady_clock;
239
240 int client = 17;
241
242 std::vector<dmc::Time> times;
243 std::mutex times_mtx;
244 using Guard = std::lock_guard<decltype(times_mtx)>;
245
246 // reservation every second
247 dmc::ClientInfo ci(1.0, 0.0, 0.0);
248 Queue pq;
249
250 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
251 return &ci;
252 };
253 auto server_ready_f = [] () -> bool { return true; };
254 auto submit_req_f = [&] (const ClientId& c,
255 std::unique_ptr<Request> req,
256 dmc::PhaseType phase) {
257 {
258 Guard g(times_mtx);
259 times.emplace_back(dmc::get_time());
260 }
261 std::thread complete([&](){ pq->request_completed(); });
262 complete.detach();
263 };
264
265 // NB? PUSH OR PULL
266 pq = Queue(new dmc::PriorityQueue<ClientId,Request>(client_info_f,
267 server_ready_f,
268 submit_req_f,
269 false));
270
271 Request req;
272 ReqParams<ClientId> req_params(client, 1, 1);
273
274 for (int i = 0; i < 5; ++i) {
275 pq->add_request_time(req, req_params, dmc::get_time());
276 }
277
278 {
279 Guard g(times_mtx);
280 std::this_thread::sleep_for(std::chrono::milliseconds(5500));
281 EXPECT_EQ(5, times.size()) <<
282 "after 5.5 seconds, we should have 5 requests times at 1 second apart";
283 }
284 } // TEST
285 #endif
286
287
288 TEST(dmclock_server, remove_by_req_filter) {
289 struct MyReq {
290 int id;
291
292 MyReq(int _id) :
293 id(_id)
294 {
295 // empty
296 }
297 }; // MyReq
298
299 using ClientId = int;
300 using Queue = dmc::PullPriorityQueue<ClientId,MyReq>;
301 using MyReqRef = typename Queue::RequestRef;
302
303 ClientId client1 = 17;
304 ClientId client2 = 98;
305
306 dmc::ClientInfo info1(0.0, 1.0, 0.0);
307
308 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
309 return &info1;
310 };
311
312 Queue pq(client_info_f, AtLimit::Allow);
313
314 EXPECT_EQ(0u, pq.client_count());
315 EXPECT_EQ(0u, pq.request_count());
316
317 ReqParams req_params(1,1);
318
319 EXPECT_EQ(0, pq.add_request(MyReq(1), client1, req_params));
320 EXPECT_EQ(0, pq.add_request(MyReq(11), client1, req_params));
321 EXPECT_EQ(0, pq.add_request(MyReq(2), client2, req_params));
322 EXPECT_EQ(0, pq.add_request(MyReq(0), client2, req_params));
323 EXPECT_EQ(0, pq.add_request(MyReq(13), client2, req_params));
324 EXPECT_EQ(0, pq.add_request(MyReq(2), client2, req_params));
325 EXPECT_EQ(0, pq.add_request(MyReq(13), client2, req_params));
326 EXPECT_EQ(0, pq.add_request(MyReq(98), client2, req_params));
327 EXPECT_EQ(0, pq.add_request(MyReq(44), client1, req_params));
328
329 EXPECT_EQ(2u, pq.client_count());
330 EXPECT_EQ(9u, pq.request_count());
331
332 pq.remove_by_req_filter([](MyReqRef&& r) -> bool {return 1 == r->id % 2;});
333
334 EXPECT_EQ(5u, pq.request_count());
335
336 std::list<MyReq> capture;
337 pq.remove_by_req_filter(
338 [&capture] (MyReqRef&& r) -> bool {
339 if (0 == r->id % 2) {
340 capture.push_front(*r);
341 return true;
342 } else {
343 return false;
344 }
345 },
346 true);
347
348 EXPECT_EQ(0u, pq.request_count());
349 EXPECT_EQ(5u, capture.size());
350 int total = 0;
351 for (auto i : capture) {
352 total += i.id;
353 }
354 EXPECT_EQ(146, total) << " sum of captured items should be 146";
355 } // TEST
356
357
358 TEST(dmclock_server, remove_by_req_filter_ordering_forwards_visit) {
359 struct MyReq {
360 int id;
361
362 MyReq(int _id) :
363 id(_id)
364 {
365 // empty
366 }
367 }; // MyReq
368
369 using ClientId = int;
370 using Queue = dmc::PullPriorityQueue<ClientId,MyReq>;
371 using MyReqRef = typename Queue::RequestRef;
372
373 ClientId client1 = 17;
374
375 dmc::ClientInfo info1(0.0, 1.0, 0.0);
376
377 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
378 return &info1;
379 };
380
381 Queue pq(client_info_f, AtLimit::Allow);
382
383 EXPECT_EQ(0u, pq.client_count());
384 EXPECT_EQ(0u, pq.request_count());
385
386 ReqParams req_params(1,1);
387
388 EXPECT_EQ(0, pq.add_request(MyReq(1), client1, req_params));
389 EXPECT_EQ(0, pq.add_request(MyReq(2), client1, req_params));
390 EXPECT_EQ(0, pq.add_request(MyReq(3), client1, req_params));
391 EXPECT_EQ(0, pq.add_request(MyReq(4), client1, req_params));
392 EXPECT_EQ(0, pq.add_request(MyReq(5), client1, req_params));
393 EXPECT_EQ(0, pq.add_request(MyReq(6), client1, req_params));
394
395 EXPECT_EQ(1u, pq.client_count());
396 EXPECT_EQ(6u, pq.request_count());
397
398 // remove odd ids in forward order and append to end
399
400 std::vector<MyReq> capture;
401 pq.remove_by_req_filter(
402 [&capture] (MyReqRef&& r) -> bool {
403 if (1 == r->id % 2) {
404 capture.push_back(*r);
405 return true;
406 } else {
407 return false;
408 }
409 },
410 false);
411
412 EXPECT_EQ(3u, pq.request_count());
413 EXPECT_EQ(3u, capture.size());
414 EXPECT_EQ(1, capture[0].id) << "items should come out in forward order";
415 EXPECT_EQ(3, capture[1].id) << "items should come out in forward order";
416 EXPECT_EQ(5, capture[2].id) << "items should come out in forward order";
417
418 // remove even ids in reverse order but insert at front so comes
419 // out forwards
420
421 std::vector<MyReq> capture2;
422 pq.remove_by_req_filter(
423 [&capture2] (MyReqRef&& r) -> bool {
424 if (0 == r->id % 2) {
425 capture2.insert(capture2.begin(), *r);
426 return true;
427 } else {
428 return false;
429 }
430 },
431 false);
432
433 EXPECT_EQ(0u, pq.request_count());
434 EXPECT_EQ(3u, capture2.size());
435 EXPECT_EQ(6, capture2[0].id) << "items should come out in reverse order";
436 EXPECT_EQ(4, capture2[1].id) << "items should come out in reverse order";
437 EXPECT_EQ(2, capture2[2].id) << "items should come out in reverse order";
438 } // TEST
439
440
441 TEST(dmclock_server, remove_by_req_filter_ordering_backwards_visit) {
442 struct MyReq {
443 int id;
444
445 MyReq(int _id) :
446 id(_id)
447 {
448 // empty
449 }
450 }; // MyReq
451
452 using ClientId = int;
453 using Queue = dmc::PullPriorityQueue<ClientId,MyReq>;
454 using MyReqRef = typename Queue::RequestRef;
455
456 ClientId client1 = 17;
457
458 dmc::ClientInfo info1(0.0, 1.0, 0.0);
459
460 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
461 return &info1;
462 };
463
464 Queue pq(client_info_f, AtLimit::Allow);
465
466 EXPECT_EQ(0u, pq.client_count());
467 EXPECT_EQ(0u, pq.request_count());
468
469 ReqParams req_params(1,1);
470
471 EXPECT_EQ(0, pq.add_request(MyReq(1), client1, req_params));
472 EXPECT_EQ(0, pq.add_request(MyReq(2), client1, req_params));
473 EXPECT_EQ(0, pq.add_request(MyReq(3), client1, req_params));
474 EXPECT_EQ(0, pq.add_request(MyReq(4), client1, req_params));
475 EXPECT_EQ(0, pq.add_request(MyReq(5), client1, req_params));
476 EXPECT_EQ(0, pq.add_request(MyReq(6), client1, req_params));
477
478 EXPECT_EQ(1u, pq.client_count());
479 EXPECT_EQ(6u, pq.request_count());
480
481 // now remove odd ids in forward order
482
483 std::vector<MyReq> capture;
484 pq.remove_by_req_filter(
485 [&capture] (MyReqRef&& r) -> bool {
486 if (1 == r->id % 2) {
487 capture.insert(capture.begin(), *r);
488 return true;
489 } else {
490 return false;
491 }
492 },
493 true);
494
495 EXPECT_EQ(3u, pq.request_count());
496 EXPECT_EQ(3u, capture.size());
497 EXPECT_EQ(1, capture[0].id) << "items should come out in forward order";
498 EXPECT_EQ(3, capture[1].id) << "items should come out in forward order";
499 EXPECT_EQ(5, capture[2].id) << "items should come out in forward order";
500
501 // now remove even ids in reverse order
502
503 std::vector<MyReq> capture2;
504 pq.remove_by_req_filter(
505 [&capture2] (MyReqRef&& r) -> bool {
506 if (0 == r->id % 2) {
507 capture2.push_back(*r);
508 return true;
509 } else {
510 return false;
511 }
512 },
513 true);
514
515 EXPECT_EQ(0u, pq.request_count());
516 EXPECT_EQ(3u, capture2.size());
517 EXPECT_EQ(6, capture2[0].id) << "items should come out in reverse order";
518 EXPECT_EQ(4, capture2[1].id) << "items should come out in reverse order";
519 EXPECT_EQ(2, capture2[2].id) << "items should come out in reverse order";
520 } // TEST
521
522
523 TEST(dmclock_server, remove_by_client) {
524 struct MyReq {
525 int id;
526
527 MyReq(int _id) :
528 id(_id)
529 {
530 // empty
531 }
532 }; // MyReq
533
534 using ClientId = int;
535 using Queue = dmc::PullPriorityQueue<ClientId,MyReq>;
536 using MyReqRef = typename Queue::RequestRef;
537
538 ClientId client1 = 17;
539 ClientId client2 = 98;
540
541 dmc::ClientInfo info1(0.0, 1.0, 0.0);
542
543 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
544 return &info1;
545 };
546
547 Queue pq(client_info_f, AtLimit::Allow);
548
549 EXPECT_EQ(0u, pq.client_count());
550 EXPECT_EQ(0u, pq.request_count());
551
552 ReqParams req_params(1,1);
553
554 EXPECT_EQ(0, pq.add_request(MyReq(1), client1, req_params));
555 EXPECT_EQ(0, pq.add_request(MyReq(11), client1, req_params));
556 EXPECT_EQ(0, pq.add_request(MyReq(2), client2, req_params));
557 EXPECT_EQ(0, pq.add_request(MyReq(0), client2, req_params));
558 EXPECT_EQ(0, pq.add_request(MyReq(13), client2, req_params));
559 EXPECT_EQ(0, pq.add_request(MyReq(2), client2, req_params));
560 EXPECT_EQ(0, pq.add_request(MyReq(13), client2, req_params));
561 EXPECT_EQ(0, pq.add_request(MyReq(98), client2, req_params));
562 EXPECT_EQ(0, pq.add_request(MyReq(44), client1, req_params));
563
564 EXPECT_EQ(2u, pq.client_count());
565 EXPECT_EQ(9u, pq.request_count());
566
567 std::list<MyReq> removed;
568
569 pq.remove_by_client(client1,
570 true,
571 [&removed] (MyReqRef&& r) {
572 removed.push_front(*r);
573 });
574
575 EXPECT_EQ(3u, removed.size());
576 EXPECT_EQ(1, removed.front().id);
577 removed.pop_front();
578 EXPECT_EQ(11, removed.front().id);
579 removed.pop_front();
580 EXPECT_EQ(44, removed.front().id);
581 removed.pop_front();
582
583 EXPECT_EQ(6u, pq.request_count());
584
585 Queue::PullReq pr = pq.pull_request();
586 EXPECT_TRUE(pr.is_retn());
587 EXPECT_EQ(2, pr.get_retn().request->id);
588
589 pr = pq.pull_request();
590 EXPECT_TRUE(pr.is_retn());
591 EXPECT_EQ(0, pr.get_retn().request->id);
592
593 pq.remove_by_client(client2);
594 EXPECT_EQ(0u, pq.request_count()) <<
595 "after second client removed, none left";
596 } // TEST
597
598
599 TEST(dmclock_server_pull, pull_weight) {
600 using ClientId = int;
601 using Queue = dmc::PullPriorityQueue<ClientId,Request>;
602 using QueueRef = std::unique_ptr<Queue>;
603
604 ClientId client1 = 17;
605 ClientId client2 = 98;
606
607 dmc::ClientInfo info1(0.0, 1.0, 0.0);
608 dmc::ClientInfo info2(0.0, 2.0, 0.0);
609
610 QueueRef pq;
611
612 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
613 if (client1 == c) return &info1;
614 else if (client2 == c) return &info2;
615 else {
616 ADD_FAILURE() << "client info looked up for non-existent client";
617 return nullptr;
618 }
619 };
620
621 pq = QueueRef(new Queue(client_info_f, AtLimit::Wait));
622
623 ReqParams req_params(1,1);
624
625 auto now = dmc::get_time();
626
627 for (int i = 0; i < 5; ++i) {
628 EXPECT_EQ(0, pq->add_request(Request{}, client1, req_params));
629 EXPECT_EQ(0, pq->add_request(Request{}, client2, req_params));
630 now += 0.0001;
631 }
632
633 int c1_count = 0;
634 int c2_count = 0;
635 for (int i = 0; i < 6; ++i) {
636 Queue::PullReq pr = pq->pull_request();
637 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
638 auto& retn = boost::get<Queue::PullReq::Retn>(pr.data);
639
640 if (client1 == retn.client) ++c1_count;
641 else if (client2 == retn.client) ++c2_count;
642 else ADD_FAILURE() << "got request from neither of two clients";
643
644 EXPECT_EQ(PhaseType::priority, retn.phase);
645 }
646
647 EXPECT_EQ(2, c1_count) <<
648 "one-third of request should have come from first client";
649 EXPECT_EQ(4, c2_count) <<
650 "two-thirds of request should have come from second client";
651 }
652
653
654 TEST(dmclock_server_pull, pull_reservation) {
655 using ClientId = int;
656 using Queue = dmc::PullPriorityQueue<ClientId,Request>;
657 using QueueRef = std::unique_ptr<Queue>;
658
659 ClientId client1 = 52;
660 ClientId client2 = 8;
661
662 dmc::ClientInfo info1(2.0, 0.0, 0.0);
663 dmc::ClientInfo info2(1.0, 0.0, 0.0);
664
665 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
666 if (client1 == c) return &info1;
667 else if (client2 == c) return &info2;
668 else {
669 ADD_FAILURE() << "client info looked up for non-existent client";
670 return nullptr;
671 }
672 };
673
674 QueueRef pq(new Queue(client_info_f, AtLimit::Wait));
675
676 ReqParams req_params(1,1);
677
678 // make sure all times are well before now
679 auto old_time = dmc::get_time() - 100.0;
680
681 for (int i = 0; i < 5; ++i) {
682 EXPECT_EQ(0, pq->add_request_time(Request{}, client1, req_params, old_time));
683 EXPECT_EQ(0, pq->add_request_time(Request{}, client2, req_params, old_time));
684 old_time += 0.001;
685 }
686
687 int c1_count = 0;
688 int c2_count = 0;
689
690 for (int i = 0; i < 6; ++i) {
691 Queue::PullReq pr = pq->pull_request();
692 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
693 auto& retn = boost::get<Queue::PullReq::Retn>(pr.data);
694
695 if (client1 == retn.client) ++c1_count;
696 else if (client2 == retn.client) ++c2_count;
697 else ADD_FAILURE() << "got request from neither of two clients";
698
699 EXPECT_EQ(PhaseType::reservation, retn.phase);
700 }
701
702 EXPECT_EQ(4, c1_count) <<
703 "two-thirds of request should have come from first client";
704 EXPECT_EQ(2, c2_count) <<
705 "one-third of request should have come from second client";
706 } // dmclock_server_pull.pull_reservation
707
708
709 TEST(dmclock_server_pull, update_client_info) {
710 using ClientId = int;
711 using Queue = dmc::PullPriorityQueue<ClientId,Request,false>;
712 using QueueRef = std::unique_ptr<Queue>;
713
714 ClientId client1 = 17;
715 ClientId client2 = 98;
716
717 dmc::ClientInfo info1(0.0, 100.0, 0.0);
718 dmc::ClientInfo info2(0.0, 200.0, 0.0);
719
720 QueueRef pq;
721
722 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
723 if (client1 == c) return &info1;
724 else if (client2 == c) return &info2;
725 else {
726 ADD_FAILURE() << "client info looked up for non-existent client";
727 return nullptr;
728 }
729 };
730
731 pq = QueueRef(new Queue(client_info_f, AtLimit::Wait));
732
733 ReqParams req_params(1,1);
734
735 auto now = dmc::get_time();
736
737 for (int i = 0; i < 5; ++i) {
738 EXPECT_EQ(0, pq->add_request(Request{}, client1, req_params));
739 EXPECT_EQ(0, pq->add_request(Request{}, client2, req_params));
740 now += 0.0001;
741 }
742
743 int c1_count = 0;
744 int c2_count = 0;
745 for (int i = 0; i < 10; ++i) {
746 Queue::PullReq pr = pq->pull_request();
747 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
748 auto& retn = boost::get<Queue::PullReq::Retn>(pr.data);
749
750 if (i > 5) continue;
751 if (client1 == retn.client) ++c1_count;
752 else if (client2 == retn.client) ++c2_count;
753 else ADD_FAILURE() << "got request from neither of two clients";
754
755 EXPECT_EQ(PhaseType::priority, retn.phase);
756 }
757
758 EXPECT_EQ(2, c1_count) <<
759 "before: one-third of request should have come from first client";
760 EXPECT_EQ(4, c2_count) <<
761 "before: two-thirds of request should have come from second client";
762
763 std::chrono::seconds dura(1);
764 std::this_thread::sleep_for(dura);
765
766 info1 = dmc::ClientInfo(0.0, 200.0, 0.0);
767 pq->update_client_info(17);
768
769 now = dmc::get_time();
770
771 for (int i = 0; i < 5; ++i) {
772 EXPECT_EQ(0, pq->add_request(Request{}, client1, req_params));
773 EXPECT_EQ(0, pq->add_request(Request{}, client2, req_params));
774 now += 0.0001;
775 }
776
777 c1_count = 0;
778 c2_count = 0;
779 for (int i = 0; i < 6; ++i) {
780 Queue::PullReq pr = pq->pull_request();
781 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
782 auto& retn = boost::get<Queue::PullReq::Retn>(pr.data);
783
784 if (client1 == retn.client) ++c1_count;
785 else if (client2 == retn.client) ++c2_count;
786 else ADD_FAILURE() << "got request from neither of two clients";
787
788 EXPECT_EQ(PhaseType::priority, retn.phase);
789 }
790
791 EXPECT_EQ(3, c1_count) <<
792 "after: one-third of request should have come from first client";
793 EXPECT_EQ(3, c2_count) <<
794 "after: two-thirds of request should have come from second client";
795 }
796
797
798 TEST(dmclock_server_pull, dynamic_cli_info_f) {
799 using ClientId = int;
800 using Queue = dmc::PullPriorityQueue<ClientId,Request,true,true>;
801 using QueueRef = std::unique_ptr<Queue>;
802
803 ClientId client1 = 17;
804 ClientId client2 = 98;
805
806 std::vector<dmc::ClientInfo> info1;
807 std::vector<dmc::ClientInfo> info2;
808
809 info1.push_back(dmc::ClientInfo(0.0, 100.0, 0.0));
810 info1.push_back(dmc::ClientInfo(0.0, 150.0, 0.0));
811
812 info2.push_back(dmc::ClientInfo(0.0, 200.0, 0.0));
813 info2.push_back(dmc::ClientInfo(0.0, 50.0, 0.0));
814
815 size_t cli_info_group = 0;
816
817 QueueRef pq;
818
819 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
820 if (client1 == c) return &info1[cli_info_group];
821 else if (client2 == c) return &info2[cli_info_group];
822 else {
823 ADD_FAILURE() << "client info looked up for non-existent client";
824 return nullptr;
825 }
826 };
827
828 pq = QueueRef(new Queue(client_info_f, AtLimit::Wait));
829
830 ReqParams req_params(1,1);
831
832 auto now = dmc::get_time();
833
834 for (int i = 0; i < 5; ++i) {
835 EXPECT_EQ(0, pq->add_request(Request{}, client1, req_params));
836 EXPECT_EQ(0, pq->add_request(Request{}, client2, req_params));
837 now += 0.0001;
838 }
839
840 int c1_count = 0;
841 int c2_count = 0;
842 for (int i = 0; i < 10; ++i) {
843 Queue::PullReq pr = pq->pull_request();
844 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
845 auto& retn = boost::get<Queue::PullReq::Retn>(pr.data);
846
847 if (i > 5) continue;
848 if (client1 == retn.client) ++c1_count;
849 else if (client2 == retn.client) ++c2_count;
850 else ADD_FAILURE() << "got request from neither of two clients";
851
852 EXPECT_EQ(PhaseType::priority, retn.phase);
853 }
854
855 EXPECT_EQ(2, c1_count) <<
856 "before: one-third of request should have come from first client";
857 EXPECT_EQ(4, c2_count) <<
858 "before: two-thirds of request should have come from second client";
859
860 std::chrono::seconds dura(1);
861 std::this_thread::sleep_for(dura);
862
863 cli_info_group = 1;
864
865 now = dmc::get_time();
866
867 for (int i = 0; i < 6; ++i) {
868 EXPECT_EQ(0, pq->add_request(Request{}, client1, req_params));
869 EXPECT_EQ(0, pq->add_request(Request{}, client2, req_params));
870 now += 0.0001;
871 }
872
873 c1_count = 0;
874 c2_count = 0;
875 for (int i = 0; i < 8; ++i) {
876 Queue::PullReq pr = pq->pull_request();
877 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
878 auto& retn = boost::get<Queue::PullReq::Retn>(pr.data);
879
880 if (client1 == retn.client) ++c1_count;
881 else if (client2 == retn.client) ++c2_count;
882 else ADD_FAILURE() << "got request from neither of two clients";
883
884 EXPECT_EQ(PhaseType::priority, retn.phase);
885 }
886
887 EXPECT_EQ(6, c1_count) <<
888 "after: one-third of request should have come from first client";
889 EXPECT_EQ(2, c2_count) <<
890 "after: two-thirds of request should have come from second client";
891 }
892
893
894 // This test shows what happens when a request can be ready (under
895 // limit) but not schedulable since proportion tag is 0. We expect
896 // to get some future and none responses.
897 TEST(dmclock_server_pull, ready_and_under_limit) {
898 using ClientId = int;
899 using Queue = dmc::PullPriorityQueue<ClientId,Request>;
900 using QueueRef = std::unique_ptr<Queue>;
901
902 ClientId client1 = 52;
903 ClientId client2 = 8;
904
905 dmc::ClientInfo info1(1.0, 0.0, 0.0);
906 dmc::ClientInfo info2(1.0, 0.0, 0.0);
907
908 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
909 if (client1 == c) return &info1;
910 else if (client2 == c) return &info2;
911 else {
912 ADD_FAILURE() << "client info looked up for non-existent client";
913 return nullptr;
914 }
915 };
916
917 QueueRef pq(new Queue(client_info_f, AtLimit::Wait));
918
919 ReqParams req_params(0, 0);
920
921 // make sure all times are well before now
922 auto start_time = dmc::get_time() - 100.0;
923
924 // add six requests; for same client reservations spaced one apart
925 for (int i = 0; i < 3; ++i) {
926 EXPECT_EQ(0, pq->add_request_time(Request{}, client1, req_params, start_time));
927 EXPECT_EQ(0, pq->add_request_time(Request{}, client2, req_params, start_time));
928 }
929
930 Queue::PullReq pr = pq->pull_request(start_time + 0.5);
931 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
932
933 pr = pq->pull_request(start_time + 0.5);
934 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
935
936 pr = pq->pull_request(start_time + 0.5);
937 EXPECT_EQ(Queue::NextReqType::future, pr.type) <<
938 "too soon for next reservation";
939
940 pr = pq->pull_request(start_time + 1.5);
941 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
942
943 pr = pq->pull_request(start_time + 1.5);
944 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
945
946 pr = pq->pull_request(start_time + 1.5);
947 EXPECT_EQ(Queue::NextReqType::future, pr.type) <<
948 "too soon for next reservation";
949
950 pr = pq->pull_request(start_time + 2.5);
951 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
952
953 pr = pq->pull_request(start_time + 2.5);
954 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
955
956 pr = pq->pull_request(start_time + 2.5);
957 EXPECT_EQ(Queue::NextReqType::none, pr.type) << "no more requests left";
958 }
959
960
961 TEST(dmclock_server_pull, pull_none) {
962 using ClientId = int;
963 using Queue = dmc::PullPriorityQueue<ClientId,Request>;
964 using QueueRef = std::unique_ptr<Queue>;
965
966 dmc::ClientInfo info(1.0, 1.0, 1.0);
967
968 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
969 return &info;
970 };
971
972 QueueRef pq(new Queue(client_info_f, AtLimit::Wait));
973
974 // Request req;
975 ReqParams req_params(1,1);
976
977 auto now = dmc::get_time();
978
979 Queue::PullReq pr = pq->pull_request(now + 100);
980
981 EXPECT_EQ(Queue::NextReqType::none, pr.type);
982 }
983
984
985 TEST(dmclock_server_pull, pull_future) {
986 using ClientId = int;
987 using Queue = dmc::PullPriorityQueue<ClientId,Request>;
988 using QueueRef = std::unique_ptr<Queue>;
989
990 ClientId client1 = 52;
991 // ClientId client2 = 8;
992
993 dmc::ClientInfo info(1.0, 0.0, 1.0);
994
995 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
996 return &info;
997 };
998
999 QueueRef pq(new Queue(client_info_f, AtLimit::Wait));
1000
1001 ReqParams req_params(1,1);
1002
1003 // make sure all times are well before now
1004 auto now = dmc::get_time();
1005
1006 EXPECT_EQ(0, pq->add_request_time(Request{}, client1, req_params, now + 100));
1007 Queue::PullReq pr = pq->pull_request(now);
1008
1009 EXPECT_EQ(Queue::NextReqType::future, pr.type);
1010
1011 Time when = boost::get<Time>(pr.data);
1012 EXPECT_EQ(now + 100, when);
1013 }
1014
1015
1016 TEST(dmclock_server_pull, pull_future_limit_break_weight) {
1017 using ClientId = int;
1018 using Queue = dmc::PullPriorityQueue<ClientId,Request>;
1019 using QueueRef = std::unique_ptr<Queue>;
1020
1021 ClientId client1 = 52;
1022 // ClientId client2 = 8;
1023
1024 dmc::ClientInfo info(0.0, 1.0, 1.0);
1025
1026 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
1027 return &info;
1028 };
1029
1030 QueueRef pq(new Queue(client_info_f, AtLimit::Allow));
1031
1032 ReqParams req_params(1,1);
1033
1034 // make sure all times are well before now
1035 auto now = dmc::get_time();
1036
1037 EXPECT_EQ(0, pq->add_request_time(Request{}, client1, req_params, now + 100));
1038 Queue::PullReq pr = pq->pull_request(now);
1039
1040 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
1041
1042 auto& retn = boost::get<Queue::PullReq::Retn>(pr.data);
1043 EXPECT_EQ(client1, retn.client);
1044 }
1045
1046
1047 TEST(dmclock_server_pull, pull_future_limit_break_reservation) {
1048 using ClientId = int;
1049 using Queue = dmc::PullPriorityQueue<ClientId,Request>;
1050 using QueueRef = std::unique_ptr<Queue>;
1051
1052 ClientId client1 = 52;
1053 // ClientId client2 = 8;
1054
1055 dmc::ClientInfo info(1.0, 0.0, 1.0);
1056
1057 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
1058 return &info;
1059 };
1060
1061 QueueRef pq(new Queue(client_info_f, AtLimit::Allow));
1062
1063 ReqParams req_params(1,1);
1064
1065 // make sure all times are well before now
1066 auto now = dmc::get_time();
1067
1068 EXPECT_EQ(0, pq->add_request_time(Request{}, client1, req_params, now + 100));
1069 Queue::PullReq pr = pq->pull_request(now);
1070
1071 EXPECT_EQ(Queue::NextReqType::returning, pr.type);
1072
1073 auto& retn = boost::get<Queue::PullReq::Retn>(pr.data);
1074 EXPECT_EQ(client1, retn.client);
1075 }
1076
1077
1078 TEST(dmclock_server_pull, pull_reject_at_limit) {
1079 using ClientId = int;
1080 using Queue = dmc::PullPriorityQueue<ClientId, Request, false>;
1081 using MyReqRef = typename Queue::RequestRef;
1082
1083 ClientId client1 = 52;
1084 ClientId client2 = 53;
1085
1086 dmc::ClientInfo info(0.0, 1.0, 1.0);
1087
1088 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
1089 return &info;
1090 };
1091
1092 Queue pq(client_info_f, AtLimit::Reject);
1093
1094 {
1095 // success at 1 request per second
1096 EXPECT_EQ(0, pq.add_request_time({}, client1, {}, Time{1}));
1097 EXPECT_EQ(0, pq.add_request_time({}, client1, {}, Time{2}));
1098 EXPECT_EQ(0, pq.add_request_time({}, client1, {}, Time{3}));
1099 // request too soon
1100 EXPECT_EQ(EAGAIN, pq.add_request_time({}, client1, {}, Time{3.9}));
1101 // previous rejected request counts against limit
1102 EXPECT_EQ(EAGAIN, pq.add_request_time({}, client1, {}, Time{4}));
1103 EXPECT_EQ(0, pq.add_request_time({}, client1, {}, Time{6}));
1104 }
1105 {
1106 auto r1 = MyReqRef{new Request};
1107 ASSERT_EQ(0, pq.add_request(std::move(r1), client2, {}, Time{1}));
1108 EXPECT_EQ(nullptr, r1); // add_request takes r1 on success
1109 auto r2 = MyReqRef{new Request};
1110 ASSERT_EQ(EAGAIN, pq.add_request(std::move(r2), client2, {}, Time{1}));
1111 EXPECT_NE(nullptr, r2); // add_request does not take r2 on failure
1112 }
1113 }
1114
1115
1116 TEST(dmclock_server_pull, pull_reject_threshold) {
1117 using ClientId = int;
1118 using Queue = dmc::PullPriorityQueue<ClientId, Request, false>;
1119
1120 ClientId client1 = 52;
1121
1122 dmc::ClientInfo info(0.0, 1.0, 1.0);
1123
1124 auto client_info_f = [&] (ClientId c) -> const dmc::ClientInfo* {
1125 return &info;
1126 };
1127
1128 // allow up to 3 seconds worth of limit before rejecting
1129 Queue pq(client_info_f, RejectThreshold{3.0});
1130
1131 EXPECT_EQ(0, pq.add_request_time({}, client1, {}, Time{1})); // at limit=1
1132 EXPECT_EQ(0, pq.add_request_time({}, client1, {}, Time{1})); // 1 over
1133 EXPECT_EQ(0, pq.add_request_time({}, client1, {}, Time{1})); // 2 over
1134 EXPECT_EQ(0, pq.add_request_time({}, client1, {}, Time{1})); // 3 over
1135 EXPECT_EQ(EAGAIN, pq.add_request_time({}, client1, {}, Time{1})); // reject
1136 EXPECT_EQ(0, pq.add_request_time({}, client1, {}, Time{3})); // 3 over
1137 }
1138
1139 } // namespace dmclock
1140 } // namespace crimson