diff options
author | athanatos <rexludorum@gmail.com> | 2013-06-04 12:39:29 -0700 |
---|---|---|
committer | athanatos <rexludorum@gmail.com> | 2013-06-04 12:39:29 -0700 |
commit | 38d3c3cb5337104eb3dd9c3c4bdad1d7006b3e25 (patch) | |
tree | 34d0fd414cf4208f64e84d6e7735420d6e578ca9 | |
parent | 8d948f6109e0ac600ab450e8d71b9747ab1044bd (diff) | |
parent | b70868e007ffa56e0eabf22b4ca836327c5f82d4 (diff) | |
download | ceph-38d3c3cb5337104eb3dd9c3c4bdad1d7006b3e25.tar.gz |
Merge pull request #340 from dachary/wip-5213
PGLog::merge_old_entry unit tests
Reviewed-by: Sam Just <sam.just@inktank.com>
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/osd/PGLog.cc | 12 | ||||
-rw-r--r-- | src/osd/PGLog.h | 7 | ||||
-rw-r--r-- | src/test/osd/TestPGLog.cc | 546 |
4 files changed, 566 insertions, 4 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index ad80eac4a74..448797bde6a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -736,6 +736,11 @@ unittest_osd_types_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} unittest_osd_types_LDADD = libglobal.la libcommon.la $(PTHREAD_LIBS) -lm ${UNITTEST_LDADD} $(CRYPTO_LIBS) $(EXTRALIBS) check_PROGRAMS += unittest_osd_types +unittest_pglog_SOURCES = test/osd/TestPGLog.cc objclass/class_api.cc perfglue/disabled_heap_profiler.cc +unittest_pglog_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} +unittest_pglog_LDADD = libosd.a $(LIBOS_LDA) $(LIBGLOBAL_LDA) ${UNITTEST_LDADD} +check_PROGRAMS += unittest_pglog + unittest_gather_SOURCES = test/gather.cc unittest_gather_LDADD = ${LIBGLOBAL_LDA} ${UNITTEST_LDADD} unittest_gather_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} diff --git a/src/osd/PGLog.cc b/src/osd/PGLog.cc index 638a78697db..cc897e00ae8 100644 --- a/src/osd/PGLog.cc +++ b/src/osd/PGLog.cc @@ -21,7 +21,7 @@ #define dout_subsys ceph_subsys_osd -//////////////////// PGLog //////////////////// +//////////////////// PGLog::IndexedLog //////////////////// void PGLog::IndexedLog::split_into( pg_t child_pgid, @@ -103,6 +103,14 @@ ostream& PGLog::IndexedLog::print(ostream& out) const //////////////////// PGLog //////////////////// +void PGLog::clear() { + ondisklog.zero(); + ondisklog.has_checksums = true; + ondisklog.divergent_priors.clear(); + missing.clear(); + log.zero(); +} + void PGLog::clear_info_log( pg_t pgid, const hobject_t &infos_oid, @@ -251,7 +259,7 @@ void PGLog::proc_replica_log(ObjectStore::Transaction& t, * * return true if entry is not divergent. */ -bool PGLog::merge_old_entry(ObjectStore::Transaction& t, pg_log_entry_t& oe, pg_info_t& info, list<hobject_t>& remove_snap, bool &dirty_log) +bool PGLog::merge_old_entry(ObjectStore::Transaction& t, const pg_log_entry_t& oe, const pg_info_t& info, list<hobject_t>& remove_snap, bool &dirty_log) { if (oe.soid > info.last_backfill) { dout(20) << "merge_old_entry had " << oe << " : beyond last_backfill" << dendl; diff --git a/src/osd/PGLog.h b/src/osd/PGLog.h index 2cd85692c87..00bb72ad3ad 100644 --- a/src/osd/PGLog.h +++ b/src/osd/PGLog.h @@ -236,6 +236,9 @@ protected: IndexedLog log; public: + + void clear(); + //////////////////// get or set missing //////////////////// const pg_missing_t& get_missing() const { return missing; } @@ -353,8 +356,8 @@ public: pg_missing_t& omissing, int from); protected: - bool merge_old_entry(ObjectStore::Transaction& t, pg_log_entry_t& oe, - pg_info_t& info, list<hobject_t>& remove_snap, bool &dirty_log); + bool merge_old_entry(ObjectStore::Transaction& t, const pg_log_entry_t& oe, + const pg_info_t& info, list<hobject_t>& remove_snap, bool &dirty_log); public: void rewind_divergent_log(ObjectStore::Transaction& t, eversion_t newhead, pg_info_t &info, list<hobject_t>& remove_snap, diff --git a/src/test/osd/TestPGLog.cc b/src/test/osd/TestPGLog.cc new file mode 100644 index 00000000000..10d32db22e2 --- /dev/null +++ b/src/test/osd/TestPGLog.cc @@ -0,0 +1,546 @@ +// -*- 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 Cloudwatt <libre.licensing@cloudwatt.com> + * + * Author: Loic Dachary <loic@dachary.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library Public License for more details. + * + */ + +#include <stdio.h> +#include <signal.h> +#include "osd/PGLog.h" +#include "common/ceph_argparse.h" +#include "global/global_init.h" +#include <gtest/gtest.h> + +class PGLogTest : public ::testing::Test, protected PGLog { +public: + virtual void SetUp() { } + + virtual void TearDown() { + clear(); + } +}; + +TEST_F(PGLogTest, merge_old_entry) { + // entries > last_backfill are silently ignored + { + clear(); + + ObjectStore::Transaction t; + pg_log_entry_t oe; + pg_info_t info; + list<hobject_t> remove_snap; + bool dirty_log = false; + + info.last_backfill = hobject_t(); + info.last_backfill.hash = 1; + oe.soid.hash = 2; + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(0U, ondisklog.length()); + + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(0U, ondisklog.length()); + } + + // a clone with no non-divergent log entry is deleted + { + clear(); + + ObjectStore::Transaction t; + pg_log_entry_t oe; + pg_info_t info; + list<hobject_t> remove_snap; + bool dirty_log = false; + + oe.op = pg_log_entry_t::CLONE; + + oe.soid.snap = CEPH_NOSNAP; + EXPECT_THROW(merge_old_entry(t, oe, info, remove_snap, dirty_log), FailedAssertion); + oe.soid.snap = 1U; + missing.add(oe.soid, eversion_t(), eversion_t()); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_TRUE(missing.have_missing()); + EXPECT_TRUE(missing.is_missing(oe.soid)); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(0U, ondisklog.length()); + + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + EXPECT_FALSE(dirty_log); + EXPECT_EQ(oe.soid, remove_snap.front()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(0U, ondisklog.length()); + } + + // the new entry (from the logs) old entry (from the log entry + // given in argument) have the same version : do nothing and return true. + { + clear(); + + ObjectStore::Transaction t; + pg_log_entry_t oe; + pg_info_t info; + list<hobject_t> remove_snap; + bool dirty_log = false; + + oe.version = eversion_t(1,1); + log.add(oe); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_EQ(1U, log.log.size()); + EXPECT_EQ(0U, ondisklog.length()); + + EXPECT_TRUE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_EQ(1U, log.log.size()); + EXPECT_EQ(0U, ondisklog.length()); + } + + // the new entry (from the logs) has a version that is higher than + // the old entry (from the log entry given in argument) : do + // nothing and return false + { + clear(); + + ObjectStore::Transaction t; + pg_info_t info; + list<hobject_t> remove_snap; + bool dirty_log = false; + + pg_log_entry_t ne; + ne.version = eversion_t(2,1); + log.add(ne); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_EQ(1U, log.log.size()); + EXPECT_EQ(ne.version, log.log.front().version); + EXPECT_EQ(0U, ondisklog.length()); + + // the newer entry ( from the logs ) can be DELETE + { + log.log.front().op = pg_log_entry_t::DELETE; + pg_log_entry_t oe; + oe.version = eversion_t(1,1); + + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + } + + // if the newer entry is not DELETE, the object must be in missing + { + pg_log_entry_t &ne = log.log.front(); + ne.op = pg_log_entry_t::MODIFY; + missing.add_next_event(ne); + pg_log_entry_t oe; + oe.version = eversion_t(1,1); + + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + missing.rm(ne.soid, ne.version); + } + + // throw if the newer entry is not DELETE and not in missing + { + pg_log_entry_t &ne = log.log.front(); + ne.op = pg_log_entry_t::MODIFY; + pg_log_entry_t oe; + oe.version = eversion_t(1,1); + + EXPECT_THROW(merge_old_entry(t, oe, info, remove_snap, dirty_log), FailedAssertion); + } + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_EQ(1U, log.log.size()); + EXPECT_EQ(ne.version, log.log.front().version); + EXPECT_EQ(0U, ondisklog.length()); + + } + + // the new entry (from the logs) has a version that is lower than + // the old entry (from the log entry given in argument) and + // old and new are delete : do nothing and return false + { + clear(); + + ObjectStore::Transaction t; + pg_log_entry_t oe; + pg_info_t info; + list<hobject_t> remove_snap; + bool dirty_log = false; + + pg_log_entry_t ne; + ne.version = eversion_t(1,1); + ne.op = pg_log_entry_t::DELETE; + log.add(ne); + + oe.version = eversion_t(2,1); + oe.op = pg_log_entry_t::DELETE; + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_EQ(1U, log.log.size()); + EXPECT_EQ(0U, ondisklog.length()); + + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_EQ(1U, log.log.size()); + EXPECT_EQ(0U, ondisklog.length()); + } + + // the new entry (from the logs) has a version that is lower than + // the old entry (from the log entry given in argument) and + // new is update : + // if the object is not already in missing, add it + // if the object is already in missing, revise the version it needs + // return false + { + __s32 ops[2] = { pg_log_entry_t::MODIFY, pg_log_entry_t::DELETE }; + for (int i = 0; i < 2; i++) { + __s32 oe_op = ops[i]; + + clear(); + + ObjectStore::Transaction t; + pg_log_entry_t oe; + pg_info_t info; + list<hobject_t> remove_snap; + bool dirty_log = false; + + pg_log_entry_t ne; + ne.version = eversion_t(1,1); + ne.op = pg_log_entry_t::MODIFY; + log.add(ne); + + oe.version = eversion_t(2,1); + oe.op = oe_op; + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_EQ(1U, log.log.size()); + EXPECT_EQ(0U, ondisklog.length()); + + eversion_t old_version(0, 0); + // if the object is not already in missing, add it + { + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + EXPECT_TRUE(missing.is_missing(ne.soid, ne.version)); + EXPECT_FALSE(missing.is_missing(ne.soid, old_version)); + } + // if the object is already in missing, revise the version it needs + { + missing.revise_need(ne.soid, old_version); + EXPECT_TRUE(missing.is_missing(ne.soid, old_version)); + + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + EXPECT_TRUE(missing.is_missing(ne.soid, ne.version)); + EXPECT_FALSE(missing.is_missing(ne.soid, old_version)); + } + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_TRUE(missing.is_missing(ne.soid)); + EXPECT_EQ(1U, log.log.size()); + EXPECT_EQ(0U, ondisklog.length()); + } + } + + // the new entry (from the logs) has a version that is lower than + // the old entry (from the log entry given in argument) and + // old is update and new is DELETE : + // if the object is in missing, it is removed + { + clear(); + + ObjectStore::Transaction t; + pg_log_entry_t oe; + pg_info_t info; + list<hobject_t> remove_snap; + bool dirty_log = false; + + pg_log_entry_t ne; + ne.version = eversion_t(1,1); + ne.op = pg_log_entry_t::DELETE; + log.add(ne); + + oe.version = eversion_t(2,1); + oe.op = pg_log_entry_t::MODIFY; + missing.add_next_event(oe); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_TRUE(missing.is_missing(oe.soid)); + EXPECT_EQ(1U, log.log.size()); + EXPECT_EQ(0U, ondisklog.length()); + + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_EQ(1U, log.log.size()); + EXPECT_EQ(0U, ondisklog.length()); + } + + // there is no new entry (from the logs) and + // the old entry (from the log entry given in argument) is not a CLONE and + // the old entry prior_version is greater than the tail of the log : + // do nothing and return false + { + clear(); + + ObjectStore::Transaction t; + pg_log_entry_t oe; + pg_info_t info; + list<hobject_t> remove_snap; + bool dirty_log = false; + + info.log_tail = eversion_t(1,1); + oe.op = pg_log_entry_t::MODIFY; + oe.prior_version = eversion_t(2,1); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(0U, ondisklog.length()); + + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(0U, ondisklog.length()); + } + + // there is no new entry (from the logs) and + // the old entry (from the log entry given in argument) is not a CLONE and + // the old entry (from the log entry given in argument) is not a DELETE and + // the old entry prior_version is lower than the tail of the log : + // add the old object to the remove_snap list and + // add the old object to ondisklog divergent priors and + // set dirty_log to true and + // add or update the prior_version of the object to missing and + // return false + { + clear(); + + ObjectStore::Transaction t; + pg_log_entry_t oe; + pg_info_t info; + list<hobject_t> remove_snap; + bool dirty_log = false; + + info.log_tail = eversion_t(2,1); + oe.soid.hash = 1; + oe.op = pg_log_entry_t::MODIFY; + oe.prior_version = eversion_t(1,1); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(0U, ondisklog.length()); + + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + EXPECT_TRUE(dirty_log); + EXPECT_EQ(oe.soid, remove_snap.front()); + EXPECT_TRUE(t.empty()); + EXPECT_TRUE(missing.is_missing(oe.soid)); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(oe.soid, ondisklog.divergent_priors[oe.prior_version]); + } + + // there is no new entry (from the logs) and + // the old entry (from the log entry given in argument) is not a CLONE and + // the old entry (from the log entry given in argument) is a DELETE and + // the old entry prior_version is lower than the tail of the log : + // add the old object to ondisklog divergent priors and + // set dirty_log to true and + // add or update the prior_version of the object to missing and + // return false + { + clear(); + + ObjectStore::Transaction t; + pg_log_entry_t oe; + pg_info_t info; + list<hobject_t> remove_snap; + bool dirty_log = false; + + info.log_tail = eversion_t(2,1); + oe.soid.hash = 1; + oe.op = pg_log_entry_t::DELETE; + oe.prior_version = eversion_t(1,1); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(0U, ondisklog.length()); + + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + EXPECT_TRUE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_TRUE(missing.is_missing(oe.soid)); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(oe.soid, ondisklog.divergent_priors[oe.prior_version]); + } + + // there is no new entry (from the logs) and + // the old entry (from the log entry given in argument) is not a CLONE and + // the old entry (from the log entry given in argument) is a DELETE and + // the old entry prior_version is eversion_t() : + // remove the prior_version of the object from missing, if any and + // return false + { + clear(); + + ObjectStore::Transaction t; + pg_log_entry_t oe; + pg_info_t info; + list<hobject_t> remove_snap; + bool dirty_log = false; + + info.log_tail = eversion_t(10,1); + oe.soid.hash = 1; + oe.op = pg_log_entry_t::DELETE; + oe.prior_version = eversion_t(); + + missing.add(oe.soid, eversion_t(1,1), eversion_t()); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_TRUE(missing.is_missing(oe.soid)); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(0U, ondisklog.length()); + + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(0U, ondisklog.length()); + } + + // there is no new entry (from the logs) and + // the old entry (from the log entry given in argument) is not a CLONE and + // the old entry (from the log entry given in argument) is not a DELETE and + // the old entry prior_version is eversion_t() : + // add the old object to the remove_snap list and + // remove the prior_version of the object from missing, if any and + // return false + { + clear(); + + ObjectStore::Transaction t; + pg_log_entry_t oe; + pg_info_t info; + list<hobject_t> remove_snap; + bool dirty_log = false; + + info.log_tail = eversion_t(10,1); + oe.soid.hash = 1; + oe.op = pg_log_entry_t::MODIFY; + oe.prior_version = eversion_t(); + + missing.add(oe.soid, eversion_t(1,1), eversion_t()); + + EXPECT_FALSE(dirty_log); + EXPECT_TRUE(remove_snap.empty()); + EXPECT_TRUE(t.empty()); + EXPECT_TRUE(missing.is_missing(oe.soid)); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(0U, ondisklog.length()); + + EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log)); + + EXPECT_FALSE(dirty_log); + EXPECT_EQ(oe.soid, remove_snap.front()); + EXPECT_TRUE(t.empty()); + EXPECT_FALSE(missing.have_missing()); + EXPECT_TRUE(log.empty()); + EXPECT_EQ(0U, ondisklog.length()); + } + +} + +int main(int argc, char **argv) { + vector<const char*> args; + argv_to_vec(argc, (const char **)argv, args); + + global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0); + common_init_finish(g_ceph_context); + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +// Local Variables: +// compile-command: "cd ../.. ; make unittest_pglog ; ./unittest_pglog --log-to-stderr=true --debug-osd=20 # --gtest_filter=*.* " +// End: |