summaryrefslogtreecommitdiffstats
path: root/src/test/osd/TestMClockScheduler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/osd/TestMClockScheduler.cc')
-rw-r--r--src/test/osd/TestMClockScheduler.cc256
1 files changed, 256 insertions, 0 deletions
diff --git a/src/test/osd/TestMClockScheduler.cc b/src/test/osd/TestMClockScheduler.cc
new file mode 100644
index 000000000..8291da268
--- /dev/null
+++ b/src/test/osd/TestMClockScheduler.cc
@@ -0,0 +1,256 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+
+#include <chrono>
+
+#include "gtest/gtest.h"
+
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "common/common_init.h"
+
+#include "osd/scheduler/mClockScheduler.h"
+#include "osd/scheduler/OpSchedulerItem.h"
+
+using namespace ceph::osd::scheduler;
+
+int main(int argc, char **argv) {
+ std::vector<const char*> args(argv, argv+argc);
+ auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_OSD,
+ CODE_ENVIRONMENT_UTILITY,
+ CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+
+class mClockSchedulerTest : public testing::Test {
+public:
+ int whoami;
+ uint32_t num_shards;
+ int shard_id;
+ bool is_rotational;
+ MonClient *monc;
+ mClockScheduler q;
+
+ uint64_t client1;
+ uint64_t client2;
+ uint64_t client3;
+
+ mClockSchedulerTest() :
+ whoami(0),
+ num_shards(1),
+ shard_id(0),
+ is_rotational(false),
+ monc(nullptr),
+ q(g_ceph_context, whoami, num_shards, shard_id, is_rotational, monc),
+ client1(1001),
+ client2(9999),
+ client3(100000001)
+ {}
+
+ struct MockDmclockItem : public PGOpQueueable {
+ op_scheduler_class scheduler_class;
+
+ MockDmclockItem(op_scheduler_class _scheduler_class) :
+ PGOpQueueable(spg_t()),
+ scheduler_class(_scheduler_class) {}
+
+ MockDmclockItem()
+ : MockDmclockItem(op_scheduler_class::background_best_effort) {}
+
+ ostream &print(ostream &rhs) const final { return rhs; }
+
+ std::optional<OpRequestRef> maybe_get_op() const final {
+ return std::nullopt;
+ }
+
+ op_scheduler_class get_scheduler_class() const final {
+ return scheduler_class;
+ }
+
+ void run(OSD *osd, OSDShard *sdata, PGRef& pg, ThreadPool::TPHandle &handle) final {}
+ };
+};
+
+template <typename... Args>
+OpSchedulerItem create_item(
+ epoch_t e, uint64_t owner, Args&&... args)
+{
+ return OpSchedulerItem(
+ std::make_unique<mClockSchedulerTest::MockDmclockItem>(
+ std::forward<Args>(args)...),
+ 12, 12,
+ utime_t(), owner, e);
+}
+
+template <typename... Args>
+OpSchedulerItem create_high_prio_item(
+ unsigned priority, epoch_t e, uint64_t owner, Args&&... args)
+{
+ // Create high priority item for testing high prio queue
+ return OpSchedulerItem(
+ std::make_unique<mClockSchedulerTest::MockDmclockItem>(
+ std::forward<Args>(args)...),
+ 12, priority,
+ utime_t(), owner, e);
+}
+
+OpSchedulerItem get_item(WorkItem item)
+{
+ return std::move(std::get<OpSchedulerItem>(item));
+}
+
+TEST_F(mClockSchedulerTest, TestEmpty) {
+ ASSERT_TRUE(q.empty());
+
+ for (unsigned i = 100; i < 105; i+=2) {
+ q.enqueue(create_item(i, client1, op_scheduler_class::client));
+ std::this_thread::sleep_for(std::chrono::microseconds(1));
+ }
+
+ ASSERT_FALSE(q.empty());
+
+ std::list<OpSchedulerItem> reqs;
+
+ reqs.push_back(get_item(q.dequeue()));
+ reqs.push_back(get_item(q.dequeue()));
+
+ ASSERT_EQ(2u, reqs.size());
+ ASSERT_FALSE(q.empty());
+
+ for (auto &&i : reqs) {
+ q.enqueue_front(std::move(i));
+ }
+ reqs.clear();
+
+ ASSERT_FALSE(q.empty());
+
+ for (int i = 0; i < 3; ++i) {
+ ASSERT_FALSE(q.empty());
+ q.dequeue();
+ }
+
+ ASSERT_TRUE(q.empty());
+}
+
+TEST_F(mClockSchedulerTest, TestSingleClientOrderedEnqueueDequeue) {
+ ASSERT_TRUE(q.empty());
+
+ for (unsigned i = 100; i < 105; ++i) {
+ q.enqueue(create_item(i, client1, op_scheduler_class::client));
+ std::this_thread::sleep_for(std::chrono::microseconds(1));
+ }
+
+ auto r = get_item(q.dequeue());
+ ASSERT_EQ(100u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(101u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(102u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(103u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(104u, r.get_map_epoch());
+}
+
+TEST_F(mClockSchedulerTest, TestMultiClientOrderedEnqueueDequeue) {
+ const unsigned NUM = 1000;
+ for (unsigned i = 0; i < NUM; ++i) {
+ for (auto &&c: {client1, client2, client3}) {
+ q.enqueue(create_item(i, c));
+ std::this_thread::sleep_for(std::chrono::microseconds(1));
+ }
+ }
+
+ std::map<uint64_t, epoch_t> next;
+ for (auto &&c: {client1, client2, client3}) {
+ next[c] = 0;
+ }
+ for (unsigned i = 0; i < NUM * 3; ++i) {
+ ASSERT_FALSE(q.empty());
+ auto r = get_item(q.dequeue());
+ auto owner = r.get_owner();
+ auto niter = next.find(owner);
+ ASSERT_FALSE(niter == next.end());
+ ASSERT_EQ(niter->second, r.get_map_epoch());
+ niter->second++;
+ }
+ ASSERT_TRUE(q.empty());
+}
+
+TEST_F(mClockSchedulerTest, TestHighPriorityQueueEnqueueDequeue) {
+ ASSERT_TRUE(q.empty());
+ for (unsigned i = 200; i < 205; ++i) {
+ q.enqueue(create_high_prio_item(i, i, client1, op_scheduler_class::client));
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ ASSERT_FALSE(q.empty());
+ // Higher priority ops should be dequeued first
+ auto r = get_item(q.dequeue());
+ ASSERT_EQ(204u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(203u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(202u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(201u, r.get_map_epoch());
+
+ r = get_item(q.dequeue());
+ ASSERT_EQ(200u, r.get_map_epoch());
+
+ ASSERT_TRUE(q.empty());
+}
+
+TEST_F(mClockSchedulerTest, TestAllQueuesEnqueueDequeue) {
+ ASSERT_TRUE(q.empty());
+
+ // Insert ops into the mClock queue
+ for (unsigned i = 100; i < 102; ++i) {
+ q.enqueue(create_item(i, client1, op_scheduler_class::client));
+ std::this_thread::sleep_for(std::chrono::microseconds(1));
+ }
+
+ // Insert Immediate ops
+ for (unsigned i = 103; i < 105; ++i) {
+ q.enqueue(create_item(i, client1, op_scheduler_class::immediate));
+ std::this_thread::sleep_for(std::chrono::microseconds(1));
+ }
+
+ // Insert ops into the high queue
+ for (unsigned i = 200; i < 202; ++i) {
+ q.enqueue(create_high_prio_item(i, i, client1, op_scheduler_class::client));
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ ASSERT_FALSE(q.empty());
+ auto r = get_item(q.dequeue());
+ // Ops classified as Immediate should be dequeued first
+ ASSERT_EQ(103u, r.get_map_epoch());
+ r = get_item(q.dequeue());
+ ASSERT_EQ(104u, r.get_map_epoch());
+
+ // High priority queue should be dequeued second
+ // higher priority operation first
+ r = get_item(q.dequeue());
+ ASSERT_EQ(201u, r.get_map_epoch());
+ r = get_item(q.dequeue());
+ ASSERT_EQ(200u, r.get_map_epoch());
+
+ // mClock queue will be dequeued last
+ r = get_item(q.dequeue());
+ ASSERT_EQ(100u, r.get_map_epoch());
+ r = get_item(q.dequeue());
+ ASSERT_EQ(101u, r.get_map_epoch());
+
+ ASSERT_TRUE(q.empty());
+}