// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Ceph - scalable distributed file system * * Copyright (C) 2013 Inktank * * LGPL2.1 (see COPYING-LGPL2.1) or later */ #include #include #include #include #include "common/ceph_argparse.h" #include "common/common_init.h" #include "include/stringify.h" #include "crush/CrushWrapper.h" #include "osd/osd_types.h" std::unique_ptr build_indep_map(CephContext *cct, int num_rack, int num_host, int num_osd) { std::unique_ptr c(new CrushWrapper); c->create(); c->set_type_name(5, "root"); c->set_type_name(4, "row"); c->set_type_name(3, "rack"); c->set_type_name(2, "chasis"); c->set_type_name(1, "host"); c->set_type_name(0, "osd"); int rootno; c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1, 5, 0, NULL, NULL, &rootno); c->set_item_name(rootno, "default"); map loc; loc["root"] = "default"; int osd = 0; for (int r=0; rinsert_item(cct, osd, 1.0, string("osd.") + stringify(osd), loc); } } } int ret; int ruleno = 0; ret = c->add_rule(ruleno, 4, 123, 1, 20); ceph_assert(ret == ruleno); ret = c->set_rule_step(ruleno, 0, CRUSH_RULE_SET_CHOOSELEAF_TRIES, 10, 0); ceph_assert(ret == 0); ret = c->set_rule_step(ruleno, 1, CRUSH_RULE_TAKE, rootno, 0); ceph_assert(ret == 0); ret = c->set_rule_step(ruleno, 2, CRUSH_RULE_CHOOSELEAF_INDEP, CRUSH_CHOOSE_N, 1); ceph_assert(ret == 0); ret = c->set_rule_step(ruleno, 3, CRUSH_RULE_EMIT, 0, 0); ceph_assert(ret == 0); c->set_rule_name(ruleno, "data"); c->finalize(); if (false) { Formatter *f = Formatter::create("json-pretty"); f->open_object_section("crush_map"); c->dump(f); f->close_section(); f->flush(cout); delete f; } return c; } int get_num_dups(const vector& v) { std::set s; int dups = 0; for (auto n : v) { if (s.count(n)) ++dups; else if (n != CRUSH_ITEM_NONE) s.insert(n); } return dups; } class CRUSHTest : public ::testing::Test { public: void SetUp() final { CephInitParameters params(CEPH_ENTITY_TYPE_CLIENT); cct = common_preinit(params, CODE_ENVIRONMENT_UTILITY, CINIT_FLAG_NO_DEFAULT_CONFIG_FILE); } void TearDown() final { cct->put(); cct = nullptr; } protected: CephContext *cct = nullptr; }; TEST_F(CRUSHTest, indep_toosmall) { std::unique_ptr c(build_indep_map(cct, 1, 3, 1)); vector<__u32> weight(c->get_max_devices(), 0x10000); c->dump_tree(&cout, NULL); for (int x = 0; x < 100; ++x) { vector out; c->do_rule(0, x, out, 5, weight, 0); cout << x << " -> " << out << std::endl; int num_none = 0; for (unsigned i=0; i c(build_indep_map(cct, 3, 3, 3)); vector<__u32> weight(c->get_max_devices(), 0x10000); c->dump_tree(&cout, NULL); for (int x = 0; x < 100; ++x) { vector out; c->do_rule(0, x, out, 5, weight, 0); cout << x << " -> " << out << std::endl; int num_none = 0; for (unsigned i=0; i c(build_indep_map(cct, 3, 3, 3)); vector<__u32> weight(c->get_max_devices(), 0x10000); // mark a bunch of osds out int num = 3*3*3; for (int i=0; idump_tree(&cout, NULL); // need more retries to get 9/9 hosts for x in 0..99 c->set_choose_total_tries(100); for (int x = 0; x < 100; ++x) { vector out; c->do_rule(0, x, out, 9, weight, 0); cout << x << " -> " << out << std::endl; int num_none = 0; for (unsigned i=0; i c(build_indep_map(cct, 3, 3, 3)); vector<__u32> weight(c->get_max_devices(), 0x10000); // mark a bunch of osds out int num = 3*3*3; for (int i=0; idump_tree(&cout, NULL); c->set_choose_total_tries(100); for (int x = 0; x < 100; ++x) { vector out; c->do_rule(0, x, out, 7, weight, 0); cout << x << " -> " << out << std::endl; int num_none = 0; for (unsigned i=0; i c(build_indep_map(cct, 3, 3, 3)); c->set_choose_total_tries(100); vector<__u32> tweight(c->get_max_devices(), 0x10000); c->dump_tree(&cout, NULL); int tchanged = 0; for (int x = 1; x < 5; ++x) { vector<__u32> weight(c->get_max_devices(), 0x10000); std::map pos; vector prev; for (unsigned i=0; i out; c->do_rule(0, x, out, 7, weight, 0); cout << "(" << i << "/" << weight.size() << " out) " << x << " -> " << out << std::endl; int num_none = 0; for (unsigned k=0; k c(new CrushWrapper); const int ROOT_TYPE = 1; c->set_type_name(ROOT_TYPE, "root"); const int OSD_TYPE = 0; c->set_type_name(OSD_TYPE, "osd"); int n = 5; int items[n], weights[n]; for (int i=0; i set_max_devices(n); string root_name0("root0"); int root0; EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1, ROOT_TYPE, n, items, weights, &root0)); EXPECT_EQ(0, c->set_item_name(root0, root_name0)); string name0("rule0"); int rule0 = c->add_simple_rule(name0, root_name0, "osd", "", "firstn", pg_pool_t::TYPE_REPLICATED); EXPECT_EQ(0, rule0); string root_name1("root1"); int root1; EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1, ROOT_TYPE, n-1, items, weights, &root1)); EXPECT_EQ(0, c->set_item_name(root1, root_name1)); string name1("rule1"); int rule1 = c->add_simple_rule(name1, root_name1, "osd", "", "firstn", pg_pool_t::TYPE_REPLICATED); EXPECT_EQ(1, rule1); c->finalize(); vector reweight(n, 0x10000); for (int i=0; i<10000; ++i) { vector out0, out1; c->do_rule(rule0, i, out0, 1, reweight, 0); ASSERT_EQ(1u, out0.size()); c->do_rule(rule1, i, out1, 1, reweight, 0); ASSERT_EQ(1u, out1.size()); ASSERT_EQ(out0[0], out1[0]); //cout << i << "\t" << out0 << "\t" << out1 << std::endl; } } TEST_F(CRUSHTest, straw_same) { // items with the same weight should map about the same as items // with very similar weights. // // give the 0 vector a paired stair pattern, with dup weights. note // that the original straw flaw does not appear when there are 2 of // the initial weight, but it does when there is just 1. // // give the 1 vector a similar stair pattern, but make the same // steps weights slightly different (no dups). this works. // // compare the result and verify that the resulting mapping is // almost identical. std::unique_ptr c(new CrushWrapper); const int ROOT_TYPE = 1; c->set_type_name(ROOT_TYPE, "root"); const int OSD_TYPE = 0; c->set_type_name(OSD_TYPE, "osd"); int n = 10; int items[n], weights[n]; for (int i=0; i set_max_devices(n); string root_name0("root0"); int root0; EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1, ROOT_TYPE, n, items, weights, &root0)); EXPECT_EQ(0, c->set_item_name(root0, root_name0)); string name0("rule0"); int rule0 = c->add_simple_rule(name0, root_name0, "osd", "", "firstn", pg_pool_t::TYPE_REPLICATED); EXPECT_EQ(0, rule0); for (int i=0; i add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1, ROOT_TYPE, n, items, weights, &root1)); EXPECT_EQ(0, c->set_item_name(root1, root_name1)); string name1("rule1"); int rule1 = c->add_simple_rule(name1, root_name1, "osd", "", "firstn", pg_pool_t::TYPE_REPLICATED); EXPECT_EQ(1, rule1); if (0) { crush_bucket_straw *sb0 = reinterpret_cast(c->get_crush_map()->buckets[-1-root0]); crush_bucket_straw *sb1 = reinterpret_cast(c->get_crush_map()->buckets[-1-root1]); for (int i=0; iitem_weights[i] << "\t" << sb1->item_weights[i] << "\t" << "\t" << sb0->straws[i] << "\t" << sb1->straws[i] << std::endl; } } if (0) { JSONFormatter jf(true); jf.open_object_section("crush"); c->dump(&jf); jf.close_section(); jf.flush(cout); } c->finalize(); vector sum0(n, 0), sum1(n, 0); vector reweight(n, 0x10000); int different = 0; int max = 100000; for (int i=0; i out0, out1; c->do_rule(rule0, i, out0, 1, reweight, 0); ASSERT_EQ(1u, out0.size()); c->do_rule(rule1, i, out1, 1, reweight, 0); ASSERT_EQ(1u, out1.size()); sum0[out0[0]]++; sum1[out1[0]]++; if (out0[0] != out1[0]) different++; } for (int i=0; i c(new CrushWrapper); const int ROOT_TYPE = 2; c->set_type_name(ROOT_TYPE, "root"); const int HOST_TYPE = 1; c->set_type_name(HOST_TYPE, "host"); const int OSD_TYPE = 0; c->set_type_name(OSD_TYPE, "osd"); int items[n]; for (int i=0; i set_max_devices(n); string root_name0("root0"); int root0; crush_bucket *b0 = crush_make_bucket(c->get_crush_map(), CRUSH_BUCKET_STRAW2, CRUSH_HASH_RJENKINS1, ROOT_TYPE, n, items, weights); crush_add_bucket(c->get_crush_map(), 0, b0, &root0); c->set_item_name(root0, root_name0); string name0("rule0"); int rule0 = c->add_simple_rule(name0, root_name0, "osd", "", "firstn", pg_pool_t::TYPE_REPLICATED); int sum[n]; double totalweight = 0; vector reweight(n); for (int i=0; ifinalize(); int total = 1000000; for (int i=0; i out; c->do_rule(rule0, i, out, 1, reweight, 0); sum[out[0]]++; } double expected = (double)total / (double)n; if (verbose) cout << "expect\t\t\t" << expected << std::endl; double stddev = 0; double exptotal = 0; if (verbose) cout << "osd\tweight\tcount\tadjusted\n"; std::streamsize p = cout.precision(); cout << std::setprecision(4); for (int i=0; i c(new CrushWrapper); const int ROOT_TYPE = 2; c->set_type_name(ROOT_TYPE, "root"); const int HOST_TYPE = 1; c->set_type_name(HOST_TYPE, "host"); const int OSD_TYPE = 0; c->set_type_name(OSD_TYPE, "osd"); int items[n]; for (int i=0; i set_max_devices(n); string root_name0("root0"); int root0; crush_bucket *b0 = crush_make_bucket(c->get_crush_map(), CRUSH_BUCKET_STRAW2, CRUSH_HASH_RJENKINS1, ROOT_TYPE, n, items, weights); EXPECT_EQ(0, crush_add_bucket(c->get_crush_map(), 0, b0, &root0)); EXPECT_EQ(0, c->set_item_name(root0, root_name0)); string name0("rule0"); int rule0 = c->add_simple_rule(name0, root_name0, "osd", "", "firstn", pg_pool_t::TYPE_REPLICATED); EXPECT_EQ(0, rule0); int changed = 1; weights[changed] = weights[changed] / 10 * (rand() % 10); string root_name1("root1"); int root1; crush_bucket *b1 = crush_make_bucket(c->get_crush_map(), CRUSH_BUCKET_STRAW2, CRUSH_HASH_RJENKINS1, ROOT_TYPE, n, items, weights); EXPECT_EQ(0, crush_add_bucket(c->get_crush_map(), 0, b1, &root1)); EXPECT_EQ(0, c->set_item_name(root1, root_name1)); string name1("rule1"); int rule1 = c->add_simple_rule(name1, root_name1, "osd", "", "firstn", pg_pool_t::TYPE_REPLICATED); EXPECT_EQ(1, rule1); int sum[n]; double totalweight = 0; vector reweight(n); for (int i=0; ifinalize(); int total = 1000000; for (int i=0; i out0, out1; c->do_rule(rule0, i, out0, 1, reweight, 0); ASSERT_EQ(1u, out0.size()); c->do_rule(rule1, i, out1, 1, reweight, 0); ASSERT_EQ(1u, out1.size()); sum[out1[0]]++; //sum[rand()%n]++; if (out1[0] == changed) { ASSERT_EQ(changed, out0[0]); } else if (out0[0] != changed) { ASSERT_EQ(out0[0], out1[0]); } } double expected = (double)total / (double)n; cout << "expect\t\t\t" << expected << std::endl; double stddev = 0; cout << "osd\tweight\tcount\tadjusted\n"; std::streamsize p = cout.precision(); cout << std::setprecision(4); for (int i=0; i