1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
5 * Copyright (C) 2016 Red Hat Inc.
7 * Author: J. Eric Ivancich <ivancich@redhat.com>
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
23 #include "dmclock_server.h"
24 #include "dmclock_util.h"
25 #include "gtest/gtest.h"
27 // process control to prevent core dumps during gtest death tests
31 namespace dmc
= crimson::dmclock
;
34 // we need a request object; an empty one will do
43 * Allows us to test the code provided with the mutex provided locked.
45 static void test_locked(std::mutex
& mtx
, std::function
<void()> code
) {
46 std::unique_lock
<std::mutex
> l(mtx
);
51 TEST(dmclock_server
, bad_tag_deathtest
) {
53 using Queue
= dmc::PullPriorityQueue
<ClientId
,Request
,true>;
54 using QueueRef
= std::unique_ptr
<Queue
>;
56 ClientId client1
= 17;
57 ClientId client2
= 18;
59 double reservation
= 0.0;
62 dmc::ClientInfo
ci1(reservation
, weight
, 0.0);
63 dmc::ClientInfo
ci2(reservation
, weight
, 1.0);
65 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
66 if (client1
== c
) return &ci1
;
67 else if (client2
== c
) return &ci2
;
69 ADD_FAILURE() << "got request from neither of two clients";
74 QueueRef
pq(new Queue(client_info_f
, AtLimit::Wait
));
75 ReqParams
req_params(1,1);
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";
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";
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";
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;
106 dmc::ClientInfo
ci(reservation
, 1.0, 0.0);
107 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
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
,
118 Queue
pq(client_info_f
,
121 std::chrono::seconds(3),
122 std::chrono::seconds(5),
123 std::chrono::seconds(2),
126 auto lock_pq
= [&](std::function
<void()> code
) {
127 test_locked(pq
.data_mtx
, code
);
131 /* The timeline should be as follows:
133 * 0 seconds : request created
135 * 1 seconds : map is size 1, idle is false
137 * 2 seconds : clean notes first mark; +2 is base for further calcs
139 * 4 seconds : clean does nothing except makes another mark
141 * 5 seconds : when we're secheduled to idle (+2 + 3)
143 * 6 seconds : clean idles client
145 * 7 seconds : when we're secheduled to erase (+2 + 5)
147 * 7 seconds : verified client is idle
149 * 8 seconds : clean erases client info
151 * 9 seconds : verified client is erased
155 EXPECT_EQ(0u, pq
.client_map
.size()) <<
156 "client map initially has size 0";
160 dmc::ReqParams
req_params(1, 1);
161 EXPECT_EQ(0, pq
.add_request_time(req
, client
, req_params
, dmc::get_time()));
163 std::this_thread::sleep_for(std::chrono::seconds(1));
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.";
172 std::this_thread::sleep_for(std::chrono::seconds(6));
175 EXPECT_TRUE(pq
.client_map
.at(client
)->idle
) <<
176 "after idle age client map entry shows idle.";
179 std::this_thread::sleep_for(std::chrono::seconds(2));
182 EXPECT_EQ(0u, pq
.client_map
.size()) <<
183 "client map loses its entry after erase age";
188 TEST(dmclock_server
, delayed_tag_calc
) {
189 using ClientId
= int;
190 constexpr ClientId client1
= 17;
192 using DelayedQueue
= PullPriorityQueue
<ClientId
, Request
, true>;
193 using ImmediateQueue
= PullPriorityQueue
<ClientId
, Request
, false>;
195 ClientInfo
info(0.0, 1.0, 1.0);
196 auto client_info_f
= [&] (ClientId c
) -> const ClientInfo
* {
202 DelayedQueue
queue(client_info_f
);
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);
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());
216 ImmediateQueue
queue(client_info_f
);
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);
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());
234 TEST(dmclock_server
, reservation_timing
) {
235 using ClientId
= int;
237 using Queue
= std::unique_ptr
<dmc::PriorityQueue
<ClientId
,Request
>>;
238 using std::chrono::steady_clock
;
242 std::vector
<dmc::Time
> times
;
243 std::mutex times_mtx
;
244 using Guard
= std::lock_guard
<decltype(times_mtx
)>;
246 // reservation every second
247 dmc::ClientInfo
ci(1.0, 0.0, 0.0);
250 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
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
) {
259 times
.emplace_back(dmc::get_time());
261 std::thread
complete([&](){ pq
->request_completed(); });
266 pq
= Queue(new dmc::PriorityQueue
<ClientId
,Request
>(client_info_f
,
272 ReqParams
<ClientId
> req_params(client
, 1, 1);
274 for (int i
= 0; i
< 5; ++i
) {
275 pq
->add_request_time(req
, req_params
, dmc::get_time());
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";
288 TEST(dmclock_server
, remove_by_req_filter
) {
299 using ClientId
= int;
300 using Queue
= dmc::PullPriorityQueue
<ClientId
,MyReq
>;
301 using MyReqRef
= typename
Queue::RequestRef
;
303 ClientId client1
= 17;
304 ClientId client2
= 98;
306 dmc::ClientInfo
info1(0.0, 1.0, 0.0);
308 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
312 Queue
pq(client_info_f
, AtLimit::Allow
);
314 EXPECT_EQ(0u, pq
.client_count());
315 EXPECT_EQ(0u, pq
.request_count());
317 ReqParams
req_params(1,1);
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
));
329 EXPECT_EQ(2u, pq
.client_count());
330 EXPECT_EQ(9u, pq
.request_count());
332 pq
.remove_by_req_filter([](MyReqRef
&& r
) -> bool {return 1 == r
->id
% 2;});
334 EXPECT_EQ(5u, pq
.request_count());
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
);
348 EXPECT_EQ(0u, pq
.request_count());
349 EXPECT_EQ(5u, capture
.size());
351 for (auto i
: capture
) {
354 EXPECT_EQ(146, total
) << " sum of captured items should be 146";
358 TEST(dmclock_server
, remove_by_req_filter_ordering_forwards_visit
) {
369 using ClientId
= int;
370 using Queue
= dmc::PullPriorityQueue
<ClientId
,MyReq
>;
371 using MyReqRef
= typename
Queue::RequestRef
;
373 ClientId client1
= 17;
375 dmc::ClientInfo
info1(0.0, 1.0, 0.0);
377 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
381 Queue
pq(client_info_f
, AtLimit::Allow
);
383 EXPECT_EQ(0u, pq
.client_count());
384 EXPECT_EQ(0u, pq
.request_count());
386 ReqParams
req_params(1,1);
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
));
395 EXPECT_EQ(1u, pq
.client_count());
396 EXPECT_EQ(6u, pq
.request_count());
398 // remove odd ids in forward order and append to end
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
);
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";
418 // remove even ids in reverse order but insert at front so comes
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
);
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";
441 TEST(dmclock_server
, remove_by_req_filter_ordering_backwards_visit
) {
452 using ClientId
= int;
453 using Queue
= dmc::PullPriorityQueue
<ClientId
,MyReq
>;
454 using MyReqRef
= typename
Queue::RequestRef
;
456 ClientId client1
= 17;
458 dmc::ClientInfo
info1(0.0, 1.0, 0.0);
460 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
464 Queue
pq(client_info_f
, AtLimit::Allow
);
466 EXPECT_EQ(0u, pq
.client_count());
467 EXPECT_EQ(0u, pq
.request_count());
469 ReqParams
req_params(1,1);
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
));
478 EXPECT_EQ(1u, pq
.client_count());
479 EXPECT_EQ(6u, pq
.request_count());
481 // now remove odd ids in forward order
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
);
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";
501 // now remove even ids in reverse order
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
);
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";
523 TEST(dmclock_server
, remove_by_client
) {
534 using ClientId
= int;
535 using Queue
= dmc::PullPriorityQueue
<ClientId
,MyReq
>;
536 using MyReqRef
= typename
Queue::RequestRef
;
538 ClientId client1
= 17;
539 ClientId client2
= 98;
541 dmc::ClientInfo
info1(0.0, 1.0, 0.0);
543 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
547 Queue
pq(client_info_f
, AtLimit::Allow
);
549 EXPECT_EQ(0u, pq
.client_count());
550 EXPECT_EQ(0u, pq
.request_count());
552 ReqParams
req_params(1,1);
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
));
564 EXPECT_EQ(2u, pq
.client_count());
565 EXPECT_EQ(9u, pq
.request_count());
567 std::list
<MyReq
> removed
;
569 pq
.remove_by_client(client1
,
571 [&removed
] (MyReqRef
&& r
) {
572 removed
.push_front(*r
);
575 EXPECT_EQ(3u, removed
.size());
576 EXPECT_EQ(1, removed
.front().id
);
578 EXPECT_EQ(11, removed
.front().id
);
580 EXPECT_EQ(44, removed
.front().id
);
583 EXPECT_EQ(6u, pq
.request_count());
585 Queue::PullReq pr
= pq
.pull_request();
586 EXPECT_TRUE(pr
.is_retn());
587 EXPECT_EQ(2, pr
.get_retn().request
->id
);
589 pr
= pq
.pull_request();
590 EXPECT_TRUE(pr
.is_retn());
591 EXPECT_EQ(0, pr
.get_retn().request
->id
);
593 pq
.remove_by_client(client2
);
594 EXPECT_EQ(0u, pq
.request_count()) <<
595 "after second client removed, none left";
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
>;
604 ClientId client1
= 17;
605 ClientId client2
= 98;
607 dmc::ClientInfo
info1(0.0, 1.0, 0.0);
608 dmc::ClientInfo
info2(0.0, 2.0, 0.0);
612 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
613 if (client1
== c
) return &info1
;
614 else if (client2
== c
) return &info2
;
616 ADD_FAILURE() << "client info looked up for non-existent client";
621 pq
= QueueRef(new Queue(client_info_f
, AtLimit::Wait
));
623 ReqParams
req_params(1,1);
625 auto now
= dmc::get_time();
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
));
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
);
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";
644 EXPECT_EQ(PhaseType::priority
, retn
.phase
);
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";
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
>;
659 ClientId client1
= 52;
660 ClientId client2
= 8;
662 dmc::ClientInfo
info1(2.0, 0.0, 0.0);
663 dmc::ClientInfo
info2(1.0, 0.0, 0.0);
665 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
666 if (client1
== c
) return &info1
;
667 else if (client2
== c
) return &info2
;
669 ADD_FAILURE() << "client info looked up for non-existent client";
674 QueueRef
pq(new Queue(client_info_f
, AtLimit::Wait
));
676 ReqParams
req_params(1,1);
678 // make sure all times are well before now
679 auto old_time
= dmc::get_time() - 100.0;
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
));
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
);
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";
699 EXPECT_EQ(PhaseType::reservation
, retn
.phase
);
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
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
>;
714 ClientId client1
= 17;
715 ClientId client2
= 98;
717 dmc::ClientInfo
info1(0.0, 100.0, 0.0);
718 dmc::ClientInfo
info2(0.0, 200.0, 0.0);
722 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
723 if (client1
== c
) return &info1
;
724 else if (client2
== c
) return &info2
;
726 ADD_FAILURE() << "client info looked up for non-existent client";
731 pq
= QueueRef(new Queue(client_info_f
, AtLimit::Wait
));
733 ReqParams
req_params(1,1);
735 auto now
= dmc::get_time();
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
));
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
);
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";
755 EXPECT_EQ(PhaseType::priority
, retn
.phase
);
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";
763 std::chrono::seconds
dura(1);
764 std::this_thread::sleep_for(dura
);
766 info1
= dmc::ClientInfo(0.0, 200.0, 0.0);
767 pq
->update_client_info(17);
769 now
= dmc::get_time();
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
));
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
);
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";
788 EXPECT_EQ(PhaseType::priority
, retn
.phase
);
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";
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
>;
803 ClientId client1
= 17;
804 ClientId client2
= 98;
806 std::vector
<dmc::ClientInfo
> info1
;
807 std::vector
<dmc::ClientInfo
> info2
;
809 info1
.push_back(dmc::ClientInfo(0.0, 100.0, 0.0));
810 info1
.push_back(dmc::ClientInfo(0.0, 150.0, 0.0));
812 info2
.push_back(dmc::ClientInfo(0.0, 200.0, 0.0));
813 info2
.push_back(dmc::ClientInfo(0.0, 50.0, 0.0));
815 size_t cli_info_group
= 0;
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
];
823 ADD_FAILURE() << "client info looked up for non-existent client";
828 pq
= QueueRef(new Queue(client_info_f
, AtLimit::Wait
));
830 ReqParams
req_params(1,1);
832 auto now
= dmc::get_time();
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
));
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
);
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";
852 EXPECT_EQ(PhaseType::priority
, retn
.phase
);
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";
860 std::chrono::seconds
dura(1);
861 std::this_thread::sleep_for(dura
);
865 now
= dmc::get_time();
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
));
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
);
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";
884 EXPECT_EQ(PhaseType::priority
, retn
.phase
);
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";
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
>;
902 ClientId client1
= 52;
903 ClientId client2
= 8;
905 dmc::ClientInfo
info1(1.0, 0.0, 0.0);
906 dmc::ClientInfo
info2(1.0, 0.0, 0.0);
908 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
909 if (client1
== c
) return &info1
;
910 else if (client2
== c
) return &info2
;
912 ADD_FAILURE() << "client info looked up for non-existent client";
917 QueueRef
pq(new Queue(client_info_f
, AtLimit::Wait
));
919 ReqParams
req_params(0, 0);
921 // make sure all times are well before now
922 auto start_time
= dmc::get_time() - 100.0;
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
));
930 Queue::PullReq pr
= pq
->pull_request(start_time
+ 0.5);
931 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
933 pr
= pq
->pull_request(start_time
+ 0.5);
934 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
936 pr
= pq
->pull_request(start_time
+ 0.5);
937 EXPECT_EQ(Queue::NextReqType::future
, pr
.type
) <<
938 "too soon for next reservation";
940 pr
= pq
->pull_request(start_time
+ 1.5);
941 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
943 pr
= pq
->pull_request(start_time
+ 1.5);
944 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
946 pr
= pq
->pull_request(start_time
+ 1.5);
947 EXPECT_EQ(Queue::NextReqType::future
, pr
.type
) <<
948 "too soon for next reservation";
950 pr
= pq
->pull_request(start_time
+ 2.5);
951 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
953 pr
= pq
->pull_request(start_time
+ 2.5);
954 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
956 pr
= pq
->pull_request(start_time
+ 2.5);
957 EXPECT_EQ(Queue::NextReqType::none
, pr
.type
) << "no more requests left";
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
>;
966 dmc::ClientInfo
info(1.0, 1.0, 1.0);
968 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
972 QueueRef
pq(new Queue(client_info_f
, AtLimit::Wait
));
975 ReqParams
req_params(1,1);
977 auto now
= dmc::get_time();
979 Queue::PullReq pr
= pq
->pull_request(now
+ 100);
981 EXPECT_EQ(Queue::NextReqType::none
, pr
.type
);
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
>;
990 ClientId client1
= 52;
991 // ClientId client2 = 8;
993 dmc::ClientInfo
info(1.0, 0.0, 1.0);
995 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
999 QueueRef
pq(new Queue(client_info_f
, AtLimit::Wait
));
1001 ReqParams
req_params(1,1);
1003 // make sure all times are well before now
1004 auto now
= dmc::get_time();
1006 EXPECT_EQ(0, pq
->add_request_time(Request
{}, client1
, req_params
, now
+ 100));
1007 Queue::PullReq pr
= pq
->pull_request(now
);
1009 EXPECT_EQ(Queue::NextReqType::future
, pr
.type
);
1011 Time when
= boost::get
<Time
>(pr
.data
);
1012 EXPECT_EQ(now
+ 100, when
);
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
>;
1021 ClientId client1
= 52;
1022 // ClientId client2 = 8;
1024 dmc::ClientInfo
info(0.0, 1.0, 1.0);
1026 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
1030 QueueRef
pq(new Queue(client_info_f
, AtLimit::Allow
));
1032 ReqParams
req_params(1,1);
1034 // make sure all times are well before now
1035 auto now
= dmc::get_time();
1037 EXPECT_EQ(0, pq
->add_request_time(Request
{}, client1
, req_params
, now
+ 100));
1038 Queue::PullReq pr
= pq
->pull_request(now
);
1040 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
1042 auto& retn
= boost::get
<Queue::PullReq::Retn
>(pr
.data
);
1043 EXPECT_EQ(client1
, retn
.client
);
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
>;
1052 ClientId client1
= 52;
1053 // ClientId client2 = 8;
1055 dmc::ClientInfo
info(1.0, 0.0, 1.0);
1057 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
1061 QueueRef
pq(new Queue(client_info_f
, AtLimit::Allow
));
1063 ReqParams
req_params(1,1);
1065 // make sure all times are well before now
1066 auto now
= dmc::get_time();
1068 EXPECT_EQ(0, pq
->add_request_time(Request
{}, client1
, req_params
, now
+ 100));
1069 Queue::PullReq pr
= pq
->pull_request(now
);
1071 EXPECT_EQ(Queue::NextReqType::returning
, pr
.type
);
1073 auto& retn
= boost::get
<Queue::PullReq::Retn
>(pr
.data
);
1074 EXPECT_EQ(client1
, retn
.client
);
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
;
1083 ClientId client1
= 52;
1084 ClientId client2
= 53;
1086 dmc::ClientInfo
info(0.0, 1.0, 1.0);
1088 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
1092 Queue
pq(client_info_f
, AtLimit::Reject
);
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}));
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}));
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
1116 TEST(dmclock_server_pull
, pull_reject_threshold
) {
1117 using ClientId
= int;
1118 using Queue
= dmc::PullPriorityQueue
<ClientId
, Request
, false>;
1120 ClientId client1
= 52;
1122 dmc::ClientInfo
info(0.0, 1.0, 1.0);
1124 auto client_info_f
= [&] (ClientId c
) -> const dmc::ClientInfo
* {
1128 // allow up to 3 seconds worth of limit before rejecting
1129 Queue
pq(client_info_f
, RejectThreshold
{3.0});
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
1139 } // namespace dmclock
1140 } // namespace crimson