summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoic Dachary <loic@dachary.org>2013-06-02 12:53:48 +0200
committerLoic Dachary <loic@dachary.org>2013-06-04 13:43:20 +0200
commitb70868e007ffa56e0eabf22b4ca836327c5f82d4 (patch)
tree359146174764b23b317ec89d383caed5438e0808
parent6cdc4f8fda11dd397f4b05acdcf66e7522e5a681 (diff)
downloadceph-b70868e007ffa56e0eabf22b4ca836327c5f82d4.tar.gz
unit tests for PGLog::merge_old_entry
The tests covers 100% of the LOC of merge_old_entry. It is broken down in 13 cases to enumerate all the situations it must address. Each case is isolated in a independant code block where the conditions are reproduced. For instance: info.last_backfill = hobject_t(); info.last_backfill.hash = 1; oe.soid.hash = 2; creates the conditions where merge_log_entry is expected to silently ignore entries containing an object that is greater than last_backfill. PGLogTest is derived from PGLog to get access to the protected members. Signed-off-by: Loic Dachary <loic@dachary.org>
-rw-r--r--src/Makefile.am5
-rw-r--r--src/test/osd/TestPGLog.cc546
2 files changed, 551 insertions, 0 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/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: