X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Ftest%2Fosd%2Ftypes.cc;fp=src%2Fceph%2Fsrc%2Ftest%2Fosd%2Ftypes.cc;h=2a26c395a70df2d22ebdd1cea4ea5ebd298d9a86;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/test/osd/types.cc b/src/ceph/src/test/osd/types.cc new file mode 100644 index 0000000..2a26c39 --- /dev/null +++ b/src/ceph/src/test/osd/types.cc @@ -0,0 +1,1828 @@ +// -*- 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) 2011 New Dream Network + * Copyright (C) 2013 Cloudwatt + * + * Author: Loic Dachary + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "include/types.h" +#include "osd/osd_types.h" +#include "osd/OSDMap.h" +#include "gtest/gtest.h" +#include "include/coredumpctl.h" +#include "common/Thread.h" +#include "include/stringify.h" +#include "osd/ReplicatedBackend.h" + +#include + +TEST(hobject, prefixes0) +{ + uint32_t mask = 0xE947FA20; + uint32_t bits = 12; + int64_t pool = 0; + + set prefixes_correct; + prefixes_correct.insert(string("0000000000000000.02A")); + + set prefixes_out(hobject_t::get_prefixes(bits, mask, pool)); + ASSERT_EQ(prefixes_out, prefixes_correct); +} + +TEST(hobject, prefixes1) +{ + uint32_t mask = 0x0000000F; + uint32_t bits = 6; + int64_t pool = 20; + + set prefixes_correct; + prefixes_correct.insert(string("0000000000000014.F0")); + prefixes_correct.insert(string("0000000000000014.F4")); + prefixes_correct.insert(string("0000000000000014.F8")); + prefixes_correct.insert(string("0000000000000014.FC")); + + set prefixes_out(hobject_t::get_prefixes(bits, mask, pool)); + ASSERT_EQ(prefixes_out, prefixes_correct); +} + +TEST(hobject, prefixes2) +{ + uint32_t mask = 0xDEADBEAF; + uint32_t bits = 25; + int64_t pool = 0; + + set prefixes_correct; + prefixes_correct.insert(string("0000000000000000.FAEBDA0")); + prefixes_correct.insert(string("0000000000000000.FAEBDA2")); + prefixes_correct.insert(string("0000000000000000.FAEBDA4")); + prefixes_correct.insert(string("0000000000000000.FAEBDA6")); + prefixes_correct.insert(string("0000000000000000.FAEBDA8")); + prefixes_correct.insert(string("0000000000000000.FAEBDAA")); + prefixes_correct.insert(string("0000000000000000.FAEBDAC")); + prefixes_correct.insert(string("0000000000000000.FAEBDAE")); + + set prefixes_out(hobject_t::get_prefixes(bits, mask, pool)); + ASSERT_EQ(prefixes_out, prefixes_correct); +} + +TEST(hobject, prefixes3) +{ + uint32_t mask = 0xE947FA20; + uint32_t bits = 32; + int64_t pool = 0x23; + + set prefixes_correct; + prefixes_correct.insert(string("0000000000000023.02AF749E")); + + set prefixes_out(hobject_t::get_prefixes(bits, mask, pool)); + ASSERT_EQ(prefixes_out, prefixes_correct); +} + +TEST(hobject, prefixes4) +{ + uint32_t mask = 0xE947FA20; + uint32_t bits = 0; + int64_t pool = 0x23; + + set prefixes_correct; + prefixes_correct.insert(string("0000000000000023.")); + + set prefixes_out(hobject_t::get_prefixes(bits, mask, pool)); + ASSERT_EQ(prefixes_out, prefixes_correct); +} + +TEST(hobject, prefixes5) +{ + uint32_t mask = 0xDEADBEAF; + uint32_t bits = 1; + int64_t pool = 0x34AC5D00; + + set prefixes_correct; + prefixes_correct.insert(string("0000000034AC5D00.1")); + prefixes_correct.insert(string("0000000034AC5D00.3")); + prefixes_correct.insert(string("0000000034AC5D00.5")); + prefixes_correct.insert(string("0000000034AC5D00.7")); + prefixes_correct.insert(string("0000000034AC5D00.9")); + prefixes_correct.insert(string("0000000034AC5D00.B")); + prefixes_correct.insert(string("0000000034AC5D00.D")); + prefixes_correct.insert(string("0000000034AC5D00.F")); + + set prefixes_out(hobject_t::get_prefixes(bits, mask, pool)); + ASSERT_EQ(prefixes_out, prefixes_correct); +} + +TEST(pg_interval_t, check_new_interval) +{ +// iterate through all 4 combinations +for (unsigned i = 0; i < 4; ++i) { + bool compact = i & 1; + bool ec_pool = i & 2; + // + // Create a situation where osdmaps are the same so that + // each test case can diverge from it using minimal code. + // + int osd_id = 1; + epoch_t epoch = 40; + ceph::shared_ptr osdmap(new OSDMap()); + osdmap->set_max_osd(10); + osdmap->set_state(osd_id, CEPH_OSD_EXISTS); + osdmap->set_epoch(epoch); + ceph::shared_ptr lastmap(new OSDMap()); + lastmap->set_max_osd(10); + lastmap->set_state(osd_id, CEPH_OSD_EXISTS); + lastmap->set_epoch(epoch); + epoch_t same_interval_since = epoch; + epoch_t last_epoch_clean = same_interval_since; + int64_t pool_id = 200; + int pg_num = 4; + __u8 min_size = 2; + boost::scoped_ptr recoverable(new ReplicatedBackend::RPCRecPred()); + { + OSDMap::Incremental inc(epoch + 1); + inc.new_pools[pool_id].min_size = min_size; + inc.new_pools[pool_id].set_pg_num(pg_num); + inc.new_up_thru[osd_id] = epoch + 1; + osdmap->apply_incremental(inc); + lastmap->apply_incremental(inc); + } + vector new_acting; + new_acting.push_back(osd_id); + new_acting.push_back(osd_id + 1); + vector old_acting = new_acting; + int old_primary = osd_id; + int new_primary = osd_id; + vector new_up; + new_up.push_back(osd_id); + int old_up_primary = osd_id; + int new_up_primary = osd_id; + vector old_up = new_up; + pg_t pgid; + pgid.set_pool(pool_id); + + // + // Do nothing if there are no modifications in + // acting, up or pool size and that the pool is not + // being split + // + { + PastIntervals past_intervals; past_intervals.update_type(ec_pool, compact); + + ASSERT_TRUE(past_intervals.empty()); + ASSERT_FALSE(PastIntervals::check_new_interval(old_primary, + new_primary, + old_acting, + new_acting, + old_up_primary, + new_up_primary, + old_up, + new_up, + same_interval_since, + last_epoch_clean, + osdmap, + lastmap, + pgid, + recoverable.get(), + &past_intervals)); + ASSERT_TRUE(past_intervals.empty()); + } + + // + // The acting set has changed + // + { + vector new_acting; + int _new_primary = osd_id + 1; + new_acting.push_back(_new_primary); + + PastIntervals past_intervals; past_intervals.update_type(ec_pool, compact); + + ASSERT_TRUE(past_intervals.empty()); + ASSERT_TRUE(PastIntervals::check_new_interval(old_primary, + new_primary, + old_acting, + new_acting, + old_up_primary, + new_up_primary, + old_up, + new_up, + same_interval_since, + last_epoch_clean, + osdmap, + lastmap, + pgid, + recoverable.get(), + &past_intervals)); + old_primary = new_primary; + } + + // + // The up set has changed + // + { + vector new_up; + int _new_primary = osd_id + 1; + new_up.push_back(_new_primary); + + PastIntervals past_intervals; past_intervals.update_type(ec_pool, compact); + + ASSERT_TRUE(past_intervals.empty()); + ASSERT_TRUE(PastIntervals::check_new_interval(old_primary, + new_primary, + old_acting, + new_acting, + old_up_primary, + new_up_primary, + old_up, + new_up, + same_interval_since, + last_epoch_clean, + osdmap, + lastmap, + pgid, + recoverable.get(), + &past_intervals)); + } + + // + // The up primary has changed + // + { + vector new_up; + int _new_up_primary = osd_id + 1; + + PastIntervals past_intervals; past_intervals.update_type(ec_pool, compact); + + ASSERT_TRUE(past_intervals.empty()); + ASSERT_TRUE(PastIntervals::check_new_interval(old_primary, + new_primary, + old_acting, + new_acting, + old_up_primary, + _new_up_primary, + old_up, + new_up, + same_interval_since, + last_epoch_clean, + osdmap, + lastmap, + pgid, + recoverable.get(), + &past_intervals)); + } + + // + // PG is splitting + // + { + ceph::shared_ptr osdmap(new OSDMap()); + osdmap->set_max_osd(10); + osdmap->set_state(osd_id, CEPH_OSD_EXISTS); + osdmap->set_epoch(epoch); + int new_pg_num = pg_num ^ 2; + OSDMap::Incremental inc(epoch + 1); + inc.new_pools[pool_id].min_size = min_size; + inc.new_pools[pool_id].set_pg_num(new_pg_num); + osdmap->apply_incremental(inc); + + PastIntervals past_intervals; past_intervals.update_type(ec_pool, compact); + + ASSERT_TRUE(past_intervals.empty()); + ASSERT_TRUE(PastIntervals::check_new_interval(old_primary, + new_primary, + old_acting, + new_acting, + old_up_primary, + new_up_primary, + old_up, + new_up, + same_interval_since, + last_epoch_clean, + osdmap, + lastmap, + pgid, + recoverable.get(), + &past_intervals)); + } + + // + // PG size has changed + // + { + ceph::shared_ptr osdmap(new OSDMap()); + osdmap->set_max_osd(10); + osdmap->set_state(osd_id, CEPH_OSD_EXISTS); + osdmap->set_epoch(epoch); + OSDMap::Incremental inc(epoch + 1); + __u8 new_min_size = min_size + 1; + inc.new_pools[pool_id].min_size = new_min_size; + inc.new_pools[pool_id].set_pg_num(pg_num); + osdmap->apply_incremental(inc); + + PastIntervals past_intervals; past_intervals.update_type(ec_pool, compact); + + ASSERT_TRUE(past_intervals.empty()); + ASSERT_TRUE(PastIntervals::check_new_interval(old_primary, + new_primary, + old_acting, + new_acting, + old_up_primary, + new_up_primary, + old_up, + new_up, + same_interval_since, + last_epoch_clean, + osdmap, + lastmap, + pgid, + recoverable.get(), + &past_intervals)); + } + + // + // The old acting set was empty : the previous interval could not + // have been rw + // + { + vector old_acting; + + PastIntervals past_intervals; past_intervals.update_type(ec_pool, compact); + + ostringstream out; + + ASSERT_TRUE(past_intervals.empty()); + ASSERT_TRUE(PastIntervals::check_new_interval(old_primary, + new_primary, + old_acting, + new_acting, + old_up_primary, + new_up_primary, + old_up, + new_up, + same_interval_since, + last_epoch_clean, + osdmap, + lastmap, + pgid, + recoverable.get(), + &past_intervals, + &out)); + ASSERT_NE(string::npos, out.str().find("acting set is too small")); + } + + // + // The old acting set did not have enough osd : it could + // not have been rw + // + { + vector old_acting; + old_acting.push_back(osd_id); + + // + // see http://tracker.ceph.com/issues/5780 + // the size of the old acting set should be compared + // with the min_size of the old osdmap + // + // The new osdmap is created so that it triggers the + // bug. + // + ceph::shared_ptr osdmap(new OSDMap()); + osdmap->set_max_osd(10); + osdmap->set_state(osd_id, CEPH_OSD_EXISTS); + osdmap->set_epoch(epoch); + OSDMap::Incremental inc(epoch + 1); + __u8 new_min_size = old_acting.size(); + inc.new_pools[pool_id].min_size = new_min_size; + inc.new_pools[pool_id].set_pg_num(pg_num); + osdmap->apply_incremental(inc); + + ostringstream out; + + PastIntervals past_intervals; past_intervals.update_type(ec_pool, compact); + + ASSERT_TRUE(past_intervals.empty()); + ASSERT_TRUE(PastIntervals::check_new_interval(old_primary, + new_primary, + old_acting, + new_acting, + old_up_primary, + new_up_primary, + old_up, + new_up, + same_interval_since, + last_epoch_clean, + osdmap, + lastmap, + pgid, + recoverable.get(), + &past_intervals, + &out)); + ASSERT_NE(string::npos, out.str().find("acting set is too small")); + } + + // + // The acting set changes. The old acting set primary was up during the + // previous interval and may have been rw. + // + { + vector new_acting; + new_acting.push_back(osd_id + 4); + new_acting.push_back(osd_id + 5); + + ostringstream out; + + PastIntervals past_intervals; past_intervals.update_type(ec_pool, compact); + + ASSERT_TRUE(past_intervals.empty()); + ASSERT_TRUE(PastIntervals::check_new_interval(old_primary, + new_primary, + old_acting, + new_acting, + old_up_primary, + new_up_primary, + old_up, + new_up, + same_interval_since, + last_epoch_clean, + osdmap, + lastmap, + pgid, + recoverable.get(), + &past_intervals, + &out)); + ASSERT_NE(string::npos, out.str().find("includes interval")); + } + // + // The acting set changes. The old acting set primary was not up + // during the old interval but last_epoch_clean is in the + // old interval and it may have been rw. + // + { + vector new_acting; + new_acting.push_back(osd_id + 4); + new_acting.push_back(osd_id + 5); + + ceph::shared_ptr lastmap(new OSDMap()); + lastmap->set_max_osd(10); + lastmap->set_state(osd_id, CEPH_OSD_EXISTS); + lastmap->set_epoch(epoch); + OSDMap::Incremental inc(epoch + 1); + inc.new_pools[pool_id].min_size = min_size; + inc.new_pools[pool_id].set_pg_num(pg_num); + inc.new_up_thru[osd_id] = epoch - 10; + lastmap->apply_incremental(inc); + + ostringstream out; + + PastIntervals past_intervals; past_intervals.update_type(ec_pool, compact); + + ASSERT_TRUE(past_intervals.empty()); + ASSERT_TRUE(PastIntervals::check_new_interval(old_primary, + new_primary, + old_acting, + new_acting, + old_up_primary, + new_up_primary, + old_up, + new_up, + same_interval_since, + last_epoch_clean, + osdmap, + lastmap, + pgid, + recoverable.get(), + &past_intervals, + &out)); + ASSERT_NE(string::npos, out.str().find("presumed to have been rw")); + } + + // + // The acting set changes. The old acting set primary was not up + // during the old interval and last_epoch_clean is before the + // old interval : the previous interval could not possibly have + // been rw. + // + { + vector new_acting; + new_acting.push_back(osd_id + 4); + new_acting.push_back(osd_id + 5); + + epoch_t last_epoch_clean = epoch - 10; + + ceph::shared_ptr lastmap(new OSDMap()); + lastmap->set_max_osd(10); + lastmap->set_state(osd_id, CEPH_OSD_EXISTS); + lastmap->set_epoch(epoch); + OSDMap::Incremental inc(epoch + 1); + inc.new_pools[pool_id].min_size = min_size; + inc.new_pools[pool_id].set_pg_num(pg_num); + inc.new_up_thru[osd_id] = last_epoch_clean; + lastmap->apply_incremental(inc); + + ostringstream out; + + PastIntervals past_intervals; past_intervals.update_type(ec_pool, compact); + + ASSERT_TRUE(past_intervals.empty()); + ASSERT_TRUE(PastIntervals::check_new_interval(old_primary, + new_primary, + old_acting, + new_acting, + old_up_primary, + new_up_primary, + old_up, + new_up, + same_interval_since, + last_epoch_clean, + osdmap, + lastmap, + pgid, + recoverable.get(), + &past_intervals, + &out)); + ASSERT_NE(string::npos, out.str().find("does not include interval")); + } +} // end for, didn't want to reindent +} + +TEST(pg_t, get_ancestor) +{ + ASSERT_EQ(pg_t(0, 0, -1), pg_t(16, 0, -1).get_ancestor(16)); + ASSERT_EQ(pg_t(1, 0, -1), pg_t(17, 0, -1).get_ancestor(16)); + ASSERT_EQ(pg_t(0, 0, -1), pg_t(16, 0, -1).get_ancestor(8)); + ASSERT_EQ(pg_t(16, 0, -1), pg_t(16, 0, -1).get_ancestor(80)); + ASSERT_EQ(pg_t(16, 0, -1), pg_t(16, 0, -1).get_ancestor(83)); + ASSERT_EQ(pg_t(1, 0, -1), pg_t(1321, 0, -1).get_ancestor(123).get_ancestor(8)); + ASSERT_EQ(pg_t(3, 0, -1), pg_t(1323, 0, -1).get_ancestor(123).get_ancestor(8)); + ASSERT_EQ(pg_t(3, 0, -1), pg_t(1323, 0, -1).get_ancestor(8)); +} + +TEST(pg_t, split) +{ + pg_t pgid(0, 0, -1); + set s; + bool b; + + s.clear(); + b = pgid.is_split(1, 1, &s); + ASSERT_TRUE(!b); + + s.clear(); + b = pgid.is_split(2, 4, NULL); + ASSERT_TRUE(b); + b = pgid.is_split(2, 4, &s); + ASSERT_TRUE(b); + ASSERT_EQ(1u, s.size()); + ASSERT_TRUE(s.count(pg_t(2, 0, -1))); + + s.clear(); + b = pgid.is_split(2, 8, &s); + ASSERT_TRUE(b); + ASSERT_EQ(3u, s.size()); + ASSERT_TRUE(s.count(pg_t(2, 0, -1))); + ASSERT_TRUE(s.count(pg_t(4, 0, -1))); + ASSERT_TRUE(s.count(pg_t(6, 0, -1))); + + s.clear(); + b = pgid.is_split(3, 8, &s); + ASSERT_TRUE(b); + ASSERT_EQ(1u, s.size()); + ASSERT_TRUE(s.count(pg_t(4, 0, -1))); + + s.clear(); + b = pgid.is_split(6, 8, NULL); + ASSERT_TRUE(!b); + b = pgid.is_split(6, 8, &s); + ASSERT_TRUE(!b); + ASSERT_EQ(0u, s.size()); + + pgid = pg_t(1, 0, -1); + + s.clear(); + b = pgid.is_split(2, 4, &s); + ASSERT_TRUE(b); + ASSERT_EQ(1u, s.size()); + ASSERT_TRUE(s.count(pg_t(3, 0, -1))); + + s.clear(); + b = pgid.is_split(2, 6, &s); + ASSERT_TRUE(b); + ASSERT_EQ(2u, s.size()); + ASSERT_TRUE(s.count(pg_t(3, 0, -1))); + ASSERT_TRUE(s.count(pg_t(5, 0, -1))); + + s.clear(); + b = pgid.is_split(2, 8, &s); + ASSERT_TRUE(b); + ASSERT_EQ(3u, s.size()); + ASSERT_TRUE(s.count(pg_t(3, 0, -1))); + ASSERT_TRUE(s.count(pg_t(5, 0, -1))); + ASSERT_TRUE(s.count(pg_t(7, 0, -1))); + + s.clear(); + b = pgid.is_split(4, 8, &s); + ASSERT_TRUE(b); + ASSERT_EQ(1u, s.size()); + ASSERT_TRUE(s.count(pg_t(5, 0, -1))); + + s.clear(); + b = pgid.is_split(3, 8, &s); + ASSERT_TRUE(b); + ASSERT_EQ(3u, s.size()); + ASSERT_TRUE(s.count(pg_t(3, 0, -1))); + ASSERT_TRUE(s.count(pg_t(5, 0, -1))); + ASSERT_TRUE(s.count(pg_t(7, 0, -1))); + + s.clear(); + b = pgid.is_split(6, 8, &s); + ASSERT_TRUE(!b); + ASSERT_EQ(0u, s.size()); + + pgid = pg_t(3, 0, -1); + + s.clear(); + b = pgid.is_split(7, 8, &s); + ASSERT_TRUE(b); + ASSERT_EQ(1u, s.size()); + ASSERT_TRUE(s.count(pg_t(7, 0, -1))); + + s.clear(); + b = pgid.is_split(7, 12, &s); + ASSERT_TRUE(b); + ASSERT_EQ(2u, s.size()); + ASSERT_TRUE(s.count(pg_t(7, 0, -1))); + ASSERT_TRUE(s.count(pg_t(11, 0, -1))); + + s.clear(); + b = pgid.is_split(7, 11, &s); + ASSERT_TRUE(b); + ASSERT_EQ(1u, s.size()); + ASSERT_TRUE(s.count(pg_t(7, 0, -1))); + +} + +TEST(pg_missing_t, constructor) +{ + pg_missing_t missing; + EXPECT_EQ((unsigned int)0, missing.num_missing()); + EXPECT_FALSE(missing.have_missing()); +} + +TEST(pg_missing_t, have_missing) +{ + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + pg_missing_t missing; + EXPECT_FALSE(missing.have_missing()); + missing.add(oid, eversion_t(), eversion_t(), false); + EXPECT_TRUE(missing.have_missing()); +} + +TEST(pg_missing_t, claim) +{ + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + pg_missing_t missing; + EXPECT_FALSE(missing.have_missing()); + missing.add(oid, eversion_t(), eversion_t(), false); + EXPECT_TRUE(missing.have_missing()); + + pg_missing_t other; + EXPECT_FALSE(other.have_missing()); + + other.claim(missing); + EXPECT_TRUE(other.have_missing()); +} + +TEST(pg_missing_t, is_missing) +{ + // pg_missing_t::is_missing(const hobject_t& oid) const + { + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + pg_missing_t missing; + EXPECT_FALSE(missing.is_missing(oid)); + missing.add(oid, eversion_t(), eversion_t(), false); + EXPECT_TRUE(missing.is_missing(oid)); + } + + // bool pg_missing_t::is_missing(const hobject_t& oid, eversion_t v) const + { + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + pg_missing_t missing; + eversion_t need(10,5); + EXPECT_FALSE(missing.is_missing(oid, eversion_t())); + missing.add(oid, need, eversion_t(), false); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_FALSE(missing.is_missing(oid, eversion_t())); + EXPECT_TRUE(missing.is_missing(oid, need)); + } +} + +TEST(pg_missing_t, have_old) +{ + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + pg_missing_t missing; + EXPECT_EQ(eversion_t(), missing.have_old(oid)); + missing.add(oid, eversion_t(), eversion_t(), false); + EXPECT_EQ(eversion_t(), missing.have_old(oid)); + eversion_t have(1,1); + missing.revise_have(oid, have); + EXPECT_EQ(have, missing.have_old(oid)); +} + +TEST(pg_missing_t, add_next_event) +{ + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + hobject_t oid_other(object_t("other"), "key", 9123, 9456, 0, ""); + eversion_t version(10,5); + eversion_t prior_version(3,4); + pg_log_entry_t sample_e(pg_log_entry_t::DELETE, oid, version, prior_version, + 0, osd_reqid_t(entity_name_t::CLIENT(777), 8, 999), + utime_t(8,9), 0); + + // new object (MODIFY) + { + pg_missing_t missing; + pg_log_entry_t e = sample_e; + + e.op = pg_log_entry_t::MODIFY; + e.prior_version = eversion_t(); + EXPECT_TRUE(e.is_update()); + EXPECT_TRUE(e.object_is_indexed()); + EXPECT_TRUE(e.reqid_is_indexed()); + EXPECT_FALSE(missing.is_missing(oid)); + missing.add_next_event(e); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_EQ(eversion_t(), missing.get_items().at(oid).have); + EXPECT_EQ(oid, missing.get_rmissing().at(e.version.version)); + EXPECT_EQ(1U, missing.num_missing()); + EXPECT_EQ(1U, missing.get_rmissing().size()); + + // adding the same object replaces the previous one + missing.add_next_event(e); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_EQ(1U, missing.num_missing()); + EXPECT_EQ(1U, missing.get_rmissing().size()); + } + + // new object (CLONE) + { + pg_missing_t missing; + pg_log_entry_t e = sample_e; + + e.op = pg_log_entry_t::CLONE; + e.prior_version = eversion_t(); + EXPECT_TRUE(e.is_clone()); + EXPECT_TRUE(e.object_is_indexed()); + EXPECT_FALSE(e.reqid_is_indexed()); + EXPECT_FALSE(missing.is_missing(oid)); + missing.add_next_event(e); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_EQ(eversion_t(), missing.get_items().at(oid).have); + EXPECT_EQ(oid, missing.get_rmissing().at(e.version.version)); + EXPECT_EQ(1U, missing.num_missing()); + EXPECT_EQ(1U, missing.get_rmissing().size()); + + // adding the same object replaces the previous one + missing.add_next_event(e); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_EQ(1U, missing.num_missing()); + EXPECT_EQ(1U, missing.get_rmissing().size()); + } + + // existing object (MODIFY) + { + pg_missing_t missing; + pg_log_entry_t e = sample_e; + + e.op = pg_log_entry_t::MODIFY; + e.prior_version = eversion_t(); + EXPECT_TRUE(e.is_update()); + EXPECT_TRUE(e.object_is_indexed()); + EXPECT_TRUE(e.reqid_is_indexed()); + EXPECT_FALSE(missing.is_missing(oid)); + missing.add_next_event(e); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_EQ(eversion_t(), missing.get_items().at(oid).have); + EXPECT_EQ(oid, missing.get_rmissing().at(e.version.version)); + EXPECT_EQ(1U, missing.num_missing()); + EXPECT_EQ(1U, missing.get_rmissing().size()); + + // adding the same object with a different version + e.prior_version = prior_version; + missing.add_next_event(e); + EXPECT_EQ(eversion_t(), missing.get_items().at(oid).have); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_EQ(1U, missing.num_missing()); + EXPECT_EQ(1U, missing.get_rmissing().size()); + } + + // object with prior version (MODIFY) + { + pg_missing_t missing; + pg_log_entry_t e = sample_e; + + e.op = pg_log_entry_t::MODIFY; + EXPECT_TRUE(e.is_update()); + EXPECT_TRUE(e.object_is_indexed()); + EXPECT_TRUE(e.reqid_is_indexed()); + EXPECT_FALSE(missing.is_missing(oid)); + missing.add_next_event(e); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_EQ(prior_version, missing.get_items().at(oid).have); + EXPECT_EQ(version, missing.get_items().at(oid).need); + EXPECT_EQ(oid, missing.get_rmissing().at(e.version.version)); + EXPECT_EQ(1U, missing.num_missing()); + EXPECT_EQ(1U, missing.get_rmissing().size()); + } + + // obsolete (BACKLOG) + { + pg_missing_t missing; + pg_log_entry_t e = sample_e; + + e.op = pg_log_entry_t::BACKLOG; + EXPECT_TRUE(e.is_backlog()); + EXPECT_TRUE(e.object_is_indexed()); + EXPECT_FALSE(e.reqid_is_indexed()); + EXPECT_FALSE(missing.is_missing(oid)); + PrCtl unset_dumpable; + EXPECT_DEATH(missing.add_next_event(e), ""); + } + + // adding a DELETE matching an existing event + { + pg_missing_t missing; + pg_log_entry_t e = sample_e; + + e.op = pg_log_entry_t::MODIFY; + EXPECT_TRUE(e.is_update()); + EXPECT_TRUE(e.object_is_indexed()); + EXPECT_TRUE(e.reqid_is_indexed()); + EXPECT_FALSE(missing.is_missing(oid)); + missing.add_next_event(e); + EXPECT_TRUE(missing.is_missing(oid)); + + e.op = pg_log_entry_t::DELETE; + EXPECT_TRUE(e.is_delete()); + missing.add_next_event(e); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_TRUE(missing.get_items().at(oid).is_delete()); + EXPECT_EQ(prior_version, missing.get_items().at(oid).have); + EXPECT_EQ(version, missing.get_items().at(oid).need); + EXPECT_EQ(oid, missing.get_rmissing().at(e.version.version)); + EXPECT_EQ(1U, missing.num_missing()); + EXPECT_EQ(1U, missing.get_rmissing().size()); + } + + // adding a LOST_DELETE after an existing event + { + pg_missing_t missing; + pg_log_entry_t e = sample_e; + + e.op = pg_log_entry_t::MODIFY; + EXPECT_TRUE(e.is_update()); + EXPECT_TRUE(e.object_is_indexed()); + EXPECT_TRUE(e.reqid_is_indexed()); + EXPECT_FALSE(missing.is_missing(oid)); + missing.add_next_event(e); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_FALSE(missing.get_items().at(oid).is_delete()); + + e.op = pg_log_entry_t::LOST_DELETE; + e.version.version++; + EXPECT_TRUE(e.is_delete()); + missing.add_next_event(e); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_TRUE(missing.get_items().at(oid).is_delete()); + EXPECT_EQ(prior_version, missing.get_items().at(oid).have); + EXPECT_EQ(e.version, missing.get_items().at(oid).need); + EXPECT_EQ(oid, missing.get_rmissing().at(e.version.version)); + EXPECT_EQ(1U, missing.num_missing()); + EXPECT_EQ(1U, missing.get_rmissing().size()); + } +} + +TEST(pg_missing_t, revise_need) +{ + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + pg_missing_t missing; + // create a new entry + EXPECT_FALSE(missing.is_missing(oid)); + eversion_t need(10,10); + missing.revise_need(oid, need, false); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_EQ(eversion_t(), missing.get_items().at(oid).have); + EXPECT_EQ(need, missing.get_items().at(oid).need); + // update an existing entry and preserve have + eversion_t have(1,1); + missing.revise_have(oid, have); + eversion_t new_need(10,12); + EXPECT_EQ(have, missing.get_items().at(oid).have); + missing.revise_need(oid, new_need, false); + EXPECT_EQ(have, missing.get_items().at(oid).have); + EXPECT_EQ(new_need, missing.get_items().at(oid).need); +} + +TEST(pg_missing_t, revise_have) +{ + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + pg_missing_t missing; + // a non existing entry means noop + EXPECT_FALSE(missing.is_missing(oid)); + eversion_t have(1,1); + missing.revise_have(oid, have); + EXPECT_FALSE(missing.is_missing(oid)); + // update an existing entry + eversion_t need(10,12); + missing.add(oid, need, have, false); + EXPECT_TRUE(missing.is_missing(oid)); + eversion_t new_have(2,2); + EXPECT_EQ(have, missing.get_items().at(oid).have); + missing.revise_have(oid, new_have); + EXPECT_EQ(new_have, missing.get_items().at(oid).have); + EXPECT_EQ(need, missing.get_items().at(oid).need); +} + +TEST(pg_missing_t, add) +{ + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + pg_missing_t missing; + EXPECT_FALSE(missing.is_missing(oid)); + eversion_t have(1,1); + eversion_t need(10,10); + missing.add(oid, need, have, false); + EXPECT_TRUE(missing.is_missing(oid)); + EXPECT_EQ(have, missing.get_items().at(oid).have); + EXPECT_EQ(need, missing.get_items().at(oid).need); +} + +TEST(pg_missing_t, rm) +{ + // void pg_missing_t::rm(const hobject_t& oid, eversion_t v) + { + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + pg_missing_t missing; + EXPECT_FALSE(missing.is_missing(oid)); + epoch_t epoch = 10; + eversion_t need(epoch,10); + missing.add(oid, need, eversion_t(), false); + EXPECT_TRUE(missing.is_missing(oid)); + // rm of an older version is a noop + missing.rm(oid, eversion_t(epoch / 2,20)); + EXPECT_TRUE(missing.is_missing(oid)); + // rm of a later version removes the object + missing.rm(oid, eversion_t(epoch * 2,20)); + EXPECT_FALSE(missing.is_missing(oid)); + } + // void pg_missing_t::rm(const std::map::iterator &m) + { + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + pg_missing_t missing; + EXPECT_FALSE(missing.is_missing(oid)); + missing.add(oid, eversion_t(), eversion_t(), false); + EXPECT_TRUE(missing.is_missing(oid)); + auto m = missing.get_items().find(oid); + missing.rm(m); + EXPECT_FALSE(missing.is_missing(oid)); + } +} + +TEST(pg_missing_t, got) +{ + // void pg_missing_t::got(const hobject_t& oid, eversion_t v) + { + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + pg_missing_t missing; + // assert if the oid does not exist + { + PrCtl unset_dumpable; + EXPECT_DEATH(missing.got(oid, eversion_t()), ""); + } + EXPECT_FALSE(missing.is_missing(oid)); + epoch_t epoch = 10; + eversion_t need(epoch,10); + missing.add(oid, need, eversion_t(), false); + EXPECT_TRUE(missing.is_missing(oid)); + // assert if that the version to be removed is lower than the version of the object + { + PrCtl unset_dumpable; + EXPECT_DEATH(missing.got(oid, eversion_t(epoch / 2,20)), ""); + } + // remove of a later version removes the object + missing.got(oid, eversion_t(epoch * 2,20)); + EXPECT_FALSE(missing.is_missing(oid)); + } + // void pg_missing_t::got(const std::map::iterator &m) + { + hobject_t oid(object_t("objname"), "key", 123, 456, 0, ""); + pg_missing_t missing; + EXPECT_FALSE(missing.is_missing(oid)); + missing.add(oid, eversion_t(), eversion_t(), false); + EXPECT_TRUE(missing.is_missing(oid)); + auto m = missing.get_items().find(oid); + missing.got(m); + EXPECT_FALSE(missing.is_missing(oid)); + } +} + +TEST(pg_missing_t, split_into) +{ + uint32_t hash1 = 1; + hobject_t oid1(object_t("objname"), "key1", 123, hash1, 0, ""); + uint32_t hash2 = 2; + hobject_t oid2(object_t("objname"), "key2", 123, hash2, 0, ""); + pg_missing_t missing; + missing.add(oid1, eversion_t(), eversion_t(), false); + missing.add(oid2, eversion_t(), eversion_t(), false); + pg_t child_pgid; + child_pgid.m_seed = 1; + pg_missing_t child; + unsigned split_bits = 1; + missing.split_into(child_pgid, split_bits, &child); + EXPECT_TRUE(child.is_missing(oid1)); + EXPECT_FALSE(child.is_missing(oid2)); + EXPECT_FALSE(missing.is_missing(oid1)); + EXPECT_TRUE(missing.is_missing(oid2)); +} + +class ObjectContextTest : public ::testing::Test { +protected: + + static const useconds_t DELAY_MAX = 20 * 1000 * 1000; + + class Thread_read_lock : public Thread { + public: + ObjectContext &obc; + + explicit Thread_read_lock(ObjectContext& _obc) : + obc(_obc) + { + } + + void *entry() override { + obc.ondisk_read_lock(); + return NULL; + } + }; + + class Thread_write_lock : public Thread { + public: + ObjectContext &obc; + + explicit Thread_write_lock(ObjectContext& _obc) : + obc(_obc) + { + } + + void *entry() override { + obc.ondisk_write_lock(); + return NULL; + } + }; + +}; + +TEST_F(ObjectContextTest, read_write_lock) +{ + { + ObjectContext obc; + + // + // write_lock + // write_lock + // write_unlock + // write_unlock + // + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + obc.ondisk_write_lock(); + + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(1, obc.unstable_writes); + + obc.ondisk_write_lock(); + + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(2, obc.unstable_writes); + + obc.ondisk_write_unlock(); + + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(1, obc.unstable_writes); + + obc.ondisk_write_unlock(); + + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + } + + useconds_t delay = 0; + + { + ObjectContext obc; + + // + // write_lock + // read_lock => wait + // write_unlock => signal + // read_unlock + // + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + obc.ondisk_write_lock(); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(1, obc.unstable_writes); + + Thread_read_lock t(obc); + t.create("obc_read"); + + do { + cout << "Trying (1) with delay " << delay << "us\n"; + usleep(delay); + } while (obc.readers_waiting == 0 && + ( delay = delay * 2 + 1) < DELAY_MAX); + + EXPECT_EQ(1, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(1, obc.unstable_writes); + + obc.ondisk_write_unlock(); + + do { + cout << "Trying (2) with delay " << delay << "us\n"; + usleep(delay); + } while ((obc.readers == 0 || obc.readers_waiting == 1) && + ( delay = delay * 2 + 1) < DELAY_MAX); + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(1, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + obc.ondisk_read_unlock(); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + t.join(); + } + + { + ObjectContext obc; + + // + // read_lock + // write_lock => wait + // read_unlock => signal + // write_unlock + // + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + obc.ondisk_read_lock(); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(1, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + Thread_write_lock t(obc); + t.create("obc_write"); + + do { + cout << "Trying (3) with delay " << delay << "us\n"; + usleep(delay); + } while ((obc.writers_waiting == 0) && + ( delay = delay * 2 + 1) < DELAY_MAX); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(1, obc.readers); + EXPECT_EQ(1, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + obc.ondisk_read_unlock(); + + do { + cout << "Trying (4) with delay " << delay << "us\n"; + usleep(delay); + } while ((obc.unstable_writes == 0 || obc.writers_waiting == 1) && + ( delay = delay * 2 + 1) < DELAY_MAX); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(1, obc.unstable_writes); + + obc.ondisk_write_unlock(); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + t.join(); + } + +} + +TEST(pg_pool_t_test, get_pg_num_divisor) { + pg_pool_t p; + p.set_pg_num(16); + p.set_pgp_num(16); + + for (int i = 0; i < 16; ++i) + ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(i, 1))); + + p.set_pg_num(12); + p.set_pgp_num(12); + + ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(0, 1))); + ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(1, 1))); + ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(2, 1))); + ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(3, 1))); + ASSERT_EQ(8u, p.get_pg_num_divisor(pg_t(4, 1))); + ASSERT_EQ(8u, p.get_pg_num_divisor(pg_t(5, 1))); + ASSERT_EQ(8u, p.get_pg_num_divisor(pg_t(6, 1))); + ASSERT_EQ(8u, p.get_pg_num_divisor(pg_t(7, 1))); + ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(8, 1))); + ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(9, 1))); + ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(10, 1))); + ASSERT_EQ(16u, p.get_pg_num_divisor(pg_t(11, 1))); +} + +TEST(pg_pool_t_test, get_random_pg_position) { + srand(getpid()); + for (int i = 0; i < 100; ++i) { + pg_pool_t p; + p.set_pg_num(1 + (rand() % 1000)); + p.set_pgp_num(p.get_pg_num()); + pg_t pgid(rand() % p.get_pg_num(), 1); + uint32_t h = p.get_random_pg_position(pgid, rand()); + uint32_t ps = p.raw_hash_to_pg(h); + cout << p.get_pg_num() << " " << pgid << ": " + << h << " -> " << pg_t(ps, 1) << std::endl; + ASSERT_EQ(pgid.ps(), ps); + } +} + +TEST(shard_id_t, iostream) { + set shards; + shards.insert(shard_id_t(0)); + shards.insert(shard_id_t(1)); + shards.insert(shard_id_t(2)); + ostringstream out; + out << shards; + ASSERT_EQ(out.str(), "0,1,2"); + + shard_id_t noshard = shard_id_t::NO_SHARD; + shard_id_t zero(0); + ASSERT_GT(zero, noshard); +} + +TEST(spg_t, parse) { + spg_t a(pg_t(1,2), shard_id_t::NO_SHARD); + spg_t aa, bb; + spg_t b(pg_t(3,2), shard_id_t(2)); + std::string s = stringify(a); + ASSERT_TRUE(aa.parse(s.c_str())); + ASSERT_EQ(a, aa); + + s = stringify(b); + ASSERT_TRUE(bb.parse(s.c_str())); + ASSERT_EQ(b, bb); +} + +TEST(coll_t, parse) { + const char *ok[] = { + "meta", + "1.2_head", + "1.2_TEMP", + "1.2s3_head", + "1.3s2_TEMP", + "1.2s0_head", + 0 + }; + const char *bad[] = { + "foo", + "1.2_food", + "1.2_head ", + //" 1.2_head", // hrm, this parses, which is not ideal.. pg_t's fault? + "1.2_temp", + "1.2_HEAD", + "1.xS3_HEAD", + "1.2s_HEAD", + "1.2sfoo_HEAD", + 0 + }; + coll_t a; + for (int i = 0; ok[i]; ++i) { + cout << "check ok " << ok[i] << std::endl; + ASSERT_TRUE(a.parse(ok[i])); + ASSERT_EQ(string(ok[i]), a.to_str()); + } + for (int i = 0; bad[i]; ++i) { + cout << "check bad " << bad[i] << std::endl; + ASSERT_FALSE(a.parse(bad[i])); + } +} + +TEST(coll_t, temp) { + spg_t pgid; + coll_t foo(pgid); + ASSERT_EQ(foo.to_str(), string("0.0_head")); + + coll_t temp = foo.get_temp(); + ASSERT_EQ(temp.to_str(), string("0.0_TEMP")); + + spg_t pgid2; + ASSERT_TRUE(temp.is_temp()); + ASSERT_TRUE(temp.is_temp(&pgid2)); + ASSERT_EQ(pgid, pgid2); +} + +TEST(coll_t, assigment) { + spg_t pgid; + coll_t right(pgid); + ASSERT_EQ(right.to_str(), string("0.0_head")); + + coll_t left, middle; + + ASSERT_EQ(left.to_str(), string("meta")); + ASSERT_EQ(middle.to_str(), string("meta")); + + left = middle = right; + + ASSERT_EQ(left.to_str(), string("0.0_head")); + ASSERT_EQ(middle.to_str(), string("0.0_head")); + + ASSERT_NE(middle.c_str(), right.c_str()); + ASSERT_NE(left.c_str(), middle.c_str()); +} + +TEST(hobject_t, parse) { + const char *v[] = { + "MIN", + "MAX", + "-1:60c2fa6d:::inc_osdmap.1:0", + "-1:60c2fa6d:::inc_osdmap.1:333", + "0:00000000::::head", + "1:00000000:nspace:key:obj:head", + "-40:00000000:nspace::obj:head", + "20:00000000::key:obj:head", + "20:00000000:::o%fdj:head", + "20:00000000:::o%02fdj:head", + "20:00000000:::_zero_%00_:head", + NULL + }; + + for (unsigned i=0; v[i]; ++i) { + hobject_t o; + bool b = o.parse(v[i]); + if (!b) { + cout << "failed to parse " << v[i] << std::endl; + ASSERT_TRUE(false); + } + string s = stringify(o); + if (s != v[i]) { + cout << v[i] << " -> " << o << " -> " << s << std::endl; + ASSERT_EQ(s, string(v[i])); + } + } +} + +TEST(ghobject_t, cmp) { + ghobject_t min; + ghobject_t sep; + sep.set_shard(shard_id_t(1)); + sep.hobj.pool = -1; + cout << min << " < " << sep << std::endl; + ASSERT_TRUE(min < sep); + + sep.set_shard(shard_id_t::NO_SHARD); + cout << "sep shard " << sep.shard_id << std::endl; + ghobject_t o(hobject_t(object_t(), string(), CEPH_NOSNAP, 0x42, + 1, string())); + cout << "o " << o << std::endl; + ASSERT_TRUE(o > sep); +} + +TEST(ghobject_t, parse) { + const char *v[] = { + "GHMIN", + "GHMAX", + "13#0:00000000::::head#", + "13#0:00000000::::head#deadbeef", + "#-1:60c2fa6d:::inc_osdmap.1:333#deadbeef", + "#-1:60c2fa6d:::inc%02osdmap.1:333#deadbeef", + "#-1:60c2fa6d:::inc_osdmap.1:333#", + "1#MIN#deadbeefff", + "1#MAX#", + "#MAX#123", + "#-40:00000000:nspace::obj:head#", + NULL + }; + + for (unsigned i=0; v[i]; ++i) { + ghobject_t o; + bool b = o.parse(v[i]); + if (!b) { + cout << "failed to parse " << v[i] << std::endl; + ASSERT_TRUE(false); + } + string s = stringify(o); + if (s != v[i]) { + cout << v[i] << " -> " << o << " -> " << s << std::endl; + ASSERT_EQ(s, string(v[i])); + } + } +} + +TEST(pool_opts_t, invalid_opt) { + EXPECT_FALSE(pool_opts_t::is_opt_name("INVALID_OPT")); + PrCtl unset_dumpable; + EXPECT_DEATH(pool_opts_t::get_opt_desc("INVALID_OPT"), ""); +} + +TEST(pool_opts_t, scrub_min_interval) { + EXPECT_TRUE(pool_opts_t::is_opt_name("scrub_min_interval")); + EXPECT_EQ(pool_opts_t::get_opt_desc("scrub_min_interval"), + pool_opts_t::opt_desc_t(pool_opts_t::SCRUB_MIN_INTERVAL, + pool_opts_t::DOUBLE)); + + pool_opts_t opts; + EXPECT_FALSE(opts.is_set(pool_opts_t::SCRUB_MIN_INTERVAL)); + { + PrCtl unset_dumpable; + EXPECT_DEATH(opts.get(pool_opts_t::SCRUB_MIN_INTERVAL), ""); + } + double val; + EXPECT_FALSE(opts.get(pool_opts_t::SCRUB_MIN_INTERVAL, &val)); + opts.set(pool_opts_t::SCRUB_MIN_INTERVAL, static_cast(2015)); + EXPECT_TRUE(opts.get(pool_opts_t::SCRUB_MIN_INTERVAL, &val)); + EXPECT_EQ(val, 2015); + opts.unset(pool_opts_t::SCRUB_MIN_INTERVAL); + EXPECT_FALSE(opts.is_set(pool_opts_t::SCRUB_MIN_INTERVAL)); +} + +TEST(pool_opts_t, scrub_max_interval) { + EXPECT_TRUE(pool_opts_t::is_opt_name("scrub_max_interval")); + EXPECT_EQ(pool_opts_t::get_opt_desc("scrub_max_interval"), + pool_opts_t::opt_desc_t(pool_opts_t::SCRUB_MAX_INTERVAL, + pool_opts_t::DOUBLE)); + + pool_opts_t opts; + EXPECT_FALSE(opts.is_set(pool_opts_t::SCRUB_MAX_INTERVAL)); + { + PrCtl unset_dumpable; + EXPECT_DEATH(opts.get(pool_opts_t::SCRUB_MAX_INTERVAL), ""); + } + double val; + EXPECT_FALSE(opts.get(pool_opts_t::SCRUB_MAX_INTERVAL, &val)); + opts.set(pool_opts_t::SCRUB_MAX_INTERVAL, static_cast(2015)); + EXPECT_TRUE(opts.get(pool_opts_t::SCRUB_MAX_INTERVAL, &val)); + EXPECT_EQ(val, 2015); + opts.unset(pool_opts_t::SCRUB_MAX_INTERVAL); + EXPECT_FALSE(opts.is_set(pool_opts_t::SCRUB_MAX_INTERVAL)); +} + +TEST(pool_opts_t, deep_scrub_interval) { + EXPECT_TRUE(pool_opts_t::is_opt_name("deep_scrub_interval")); + EXPECT_EQ(pool_opts_t::get_opt_desc("deep_scrub_interval"), + pool_opts_t::opt_desc_t(pool_opts_t::DEEP_SCRUB_INTERVAL, + pool_opts_t::DOUBLE)); + + pool_opts_t opts; + EXPECT_FALSE(opts.is_set(pool_opts_t::DEEP_SCRUB_INTERVAL)); + { + PrCtl unset_dumpable; + EXPECT_DEATH(opts.get(pool_opts_t::DEEP_SCRUB_INTERVAL), ""); + } + double val; + EXPECT_FALSE(opts.get(pool_opts_t::DEEP_SCRUB_INTERVAL, &val)); + opts.set(pool_opts_t::DEEP_SCRUB_INTERVAL, static_cast(2015)); + EXPECT_TRUE(opts.get(pool_opts_t::DEEP_SCRUB_INTERVAL, &val)); + EXPECT_EQ(val, 2015); + opts.unset(pool_opts_t::DEEP_SCRUB_INTERVAL); + EXPECT_FALSE(opts.is_set(pool_opts_t::DEEP_SCRUB_INTERVAL)); +} + +struct RequiredPredicate : IsPGRecoverablePredicate { + unsigned required_size; + RequiredPredicate(unsigned required_size) : required_size(required_size) {} + bool operator()(const set &have) const override { + return have.size() >= required_size; + } +}; + +using namespace std; +struct MapPredicate { + map> states; + MapPredicate( + vector>> _states) + : states(_states.begin(), _states.end()) {} + PastIntervals::osd_state_t operator()(epoch_t start, int osd, epoch_t *lost_at) { + auto val = states.at(osd); + if (lost_at) + *lost_at = val.second; + return val.first; + } +}; + +using sit = shard_id_t; +using PI = PastIntervals; +using pst = pg_shard_t; +using ival = PastIntervals::pg_interval_t; +using ivallst = std::list; +const int N = 0x7fffffff /* CRUSH_ITEM_NONE, can't import crush.h here */; + +struct PITest : ::testing::Test { + PITest() {} + void run( + bool ec_pool, + ivallst intervals, + epoch_t last_epoch_started, + unsigned min_to_peer, + vector>> osd_states, + vector up, + vector acting, + set probe, + set down, + map blocked_by, + bool pg_down) { + RequiredPredicate rec_pred(min_to_peer); + MapPredicate map_pred(osd_states); + + PI::PriorSet correct( + ec_pool, + probe, + down, + blocked_by, + pg_down, + new RequiredPredicate(rec_pred)); + + PastIntervals simple, compact; + simple.update_type(ec_pool, false); + compact.update_type(ec_pool, true); + for (auto &&i: intervals) { + simple.add_interval(ec_pool, i); + compact.add_interval(ec_pool, i); + } + PI::PriorSet simple_ps = simple.get_prior_set( + ec_pool, + last_epoch_started, + new RequiredPredicate(rec_pred), + map_pred, + up, + acting, + nullptr); + PI::PriorSet compact_ps = compact.get_prior_set( + ec_pool, + last_epoch_started, + new RequiredPredicate(rec_pred), + map_pred, + up, + acting, + nullptr); + ASSERT_EQ(correct, simple_ps); + ASSERT_EQ(correct, compact_ps); + } +}; + +TEST_F(PITest, past_intervals_rep) { + run( + /* ec_pool */ false, + /* intervals */ + { ival{{0, 1, 2}, {0, 1, 2}, 10, 20, true, 0, 0} + , ival{{ 1, 2}, { 1, 2}, 21, 30, true, 1, 1} + , ival{{ 2}, { 2}, 31, 35, false, 2, 2} + , ival{{0, 2}, {0, 2}, 36, 50, true, 0, 0} + }, + /* les */ 5, + /* min_peer */ 1, + /* osd states at end */ + { make_pair(0, make_pair(PI::UP , 0)) + , make_pair(1, make_pair(PI::UP , 0)) + , make_pair(2, make_pair(PI::DOWN , 0)) + }, + /* acting */ {0, 1 }, + /* up */ {0, 1 }, + /* probe */ {pst(0), pst(1)}, + /* down */ {2}, + /* blocked_by */ {}, + /* pg_down */ false); +} + +TEST_F(PITest, past_intervals_ec) { + run( + /* ec_pool */ true, + /* intervals */ + { ival{{0, 1, 2}, {0, 1, 2}, 10, 20, true, 0, 0} + , ival{{N, 1, 2}, {N, 1, 2}, 21, 30, true, 1, 1} + }, + /* les */ 5, + /* min_peer */ 2, + /* osd states at end */ + { make_pair(0, make_pair(PI::DOWN , 0)) + , make_pair(1, make_pair(PI::UP , 0)) + , make_pair(2, make_pair(PI::UP , 0)) + }, + /* acting */ {N, 1, 2}, + /* up */ {N, 1, 2}, + /* probe */ {pst(1, sit(1)), pst(2, sit(2))}, + /* down */ {0}, + /* blocked_by */ {}, + /* pg_down */ false); +} + +TEST_F(PITest, past_intervals_rep_down) { + run( + /* ec_pool */ false, + /* intervals */ + { ival{{0, 1, 2}, {0, 1, 2}, 10, 20, true, 0, 0} + , ival{{ 1, 2}, { 1, 2}, 21, 30, true, 1, 1} + , ival{{ 2}, { 2}, 31, 35, true, 2, 2} + , ival{{0, 2}, {0, 2}, 36, 50, true, 0, 0} + }, + /* les */ 5, + /* min_peer */ 1, + /* osd states at end */ + { make_pair(0, make_pair(PI::UP , 0)) + , make_pair(1, make_pair(PI::UP , 0)) + , make_pair(2, make_pair(PI::DOWN , 0)) + }, + /* acting */ {0, 1 }, + /* up */ {0, 1 }, + /* probe */ {pst(0), pst(1)}, + /* down */ {2}, + /* blocked_by */ {{2, 0}}, + /* pg_down */ true); +} + +TEST_F(PITest, past_intervals_ec_down) { + run( + /* ec_pool */ true, + /* intervals */ + { ival{{0, 1, 2}, {0, 1, 2}, 10, 20, true, 0, 0} + , ival{{N, 1, 2}, {N, 1, 2}, 21, 30, true, 1, 1} + , ival{{N, N, 2}, {N, N, 2}, 31, 35, false, 2, 2} + }, + /* les */ 5, + /* min_peer */ 2, + /* osd states at end */ + { make_pair(0, make_pair(PI::UP , 0)) + , make_pair(1, make_pair(PI::DOWN , 0)) + , make_pair(2, make_pair(PI::UP , 0)) + }, + /* acting */ {0, N, 2}, + /* up */ {0, N, 2}, + /* probe */ {pst(0, sit(0)), pst(2, sit(2))}, + /* down */ {1}, + /* blocked_by */ {{1, 0}}, + /* pg_down */ true); +} + +TEST_F(PITest, past_intervals_rep_no_subsets) { + run( + /* ec_pool */ false, + /* intervals */ + { ival{{0, 2}, {0, 2}, 10, 20, true, 0, 0} + , ival{{ 1, 2}, { 1, 2}, 21, 30, true, 1, 1} + , ival{{0, 1 }, {0, 1 }, 31, 35, true, 0, 0} + }, + /* les */ 5, + /* min_peer */ 1, + /* osd states at end */ + { make_pair(0, make_pair(PI::UP , 0)) + , make_pair(1, make_pair(PI::UP , 0)) + , make_pair(2, make_pair(PI::DOWN , 0)) + }, + /* acting */ {0, 1 }, + /* up */ {0, 1 }, + /* probe */ {pst(0), pst(1)}, + /* down */ {2}, + /* blocked_by */ {}, + /* pg_down */ false); +} + +TEST_F(PITest, past_intervals_ec_no_subsets) { + run( + /* ec_pool */ true, + /* intervals */ + { ival{{0, N, 2}, {0, N, 2}, 10, 20, true, 0, 0} + , ival{{N, 1, 2}, {N, 1, 2}, 21, 30, true, 1, 1} + , ival{{0, 1, N}, {0, 1, N}, 31, 35, true, 0, 0} + }, + /* les */ 5, + /* min_peer */ 2, + /* osd states at end */ + { make_pair(0, make_pair(PI::UP , 0)) + , make_pair(1, make_pair(PI::DOWN , 0)) + , make_pair(2, make_pair(PI::UP , 0)) + }, + /* acting */ {0, N, 2}, + /* up */ {0, N, 2}, + /* probe */ {pst(0, sit(0)), pst(2, sit(2))}, + /* down */ {1}, + /* blocked_by */ {{1, 0}}, + /* pg_down */ true); +} + +TEST_F(PITest, past_intervals_ec_no_subsets2) { + run( + /* ec_pool */ true, + /* intervals */ + { ival{{N, 1, 2}, {N, 1, 2}, 10, 20, true, 0, 0} + , ival{{0, N, 2}, {0, N, 2}, 21, 30, true, 1, 1} + , ival{{0, 3, N}, {0, 3, N}, 31, 35, true, 0, 0} + }, + /* les */ 31, + /* min_peer */ 2, + /* osd states at end */ + { make_pair(0, make_pair(PI::UP , 0)) + , make_pair(1, make_pair(PI::DOWN , 0)) + , make_pair(2, make_pair(PI::UP , 0)) + , make_pair(3, make_pair(PI::UP , 0)) + }, + /* acting */ {0, N, 2}, + /* up */ {0, N, 2}, + /* probe */ {pst(0, sit(0)), pst(2, sit(2)), pst(3, sit(1))}, + /* down */ {1}, + /* blocked_by */ {}, + /* pg_down */ false); +} + +TEST_F(PITest, past_intervals_rep_lost) { + run( + /* ec_pool */ false, + /* intervals */ + { ival{{0, 1, 2}, {0, 1, 2}, 10, 20, true, 0, 0} + , ival{{ 1, 2}, { 1, 2}, 21, 30, true, 1, 1} + , ival{{ 2}, { 2}, 31, 35, true, 2, 2} + , ival{{0, 2}, {0, 2}, 36, 50, true, 0, 0} + }, + /* les */ 5, + /* min_peer */ 1, + /* osd states at end */ + { make_pair(0, make_pair(PI::UP , 0)) + , make_pair(1, make_pair(PI::UP , 0)) + , make_pair(2, make_pair(PI::LOST , 55)) + }, + /* acting */ {0, 1 }, + /* up */ {0, 1 }, + /* probe */ {pst(0), pst(1)}, + /* down */ {2}, + /* blocked_by */ {}, + /* pg_down */ false); +} + +TEST_F(PITest, past_intervals_ec_lost) { + run( + /* ec_pool */ true, + /* intervals */ + { ival{{0, N, 2}, {0, N, 2}, 10, 20, true, 0, 0} + , ival{{N, 1, 2}, {N, 1, 2}, 21, 30, true, 1, 1} + , ival{{0, 1, N}, {0, 1, N}, 31, 35, true, 0, 0} + }, + /* les */ 5, + /* min_peer */ 2, + /* osd states at end */ + { make_pair(0, make_pair(PI::UP , 0)) + , make_pair(1, make_pair(PI::LOST , 36)) + , make_pair(2, make_pair(PI::UP , 0)) + }, + /* acting */ {0, N, 2}, + /* up */ {0, N, 2}, + /* probe */ {pst(0, sit(0)), pst(2, sit(2))}, + /* down */ {1}, + /* blocked_by */ {}, + /* pg_down */ false); +} + + +/* + * Local Variables: + * compile-command: "cd ../.. ; + * make unittest_osd_types ; + * ./unittest_osd_types # --gtest_filter=pg_missing_t.constructor + * " + * End: + */