diff --git a/src/mvcc/version_list.hpp b/src/mvcc/version_list.hpp index bc6c7a351..958ab90d4 100644 --- a/src/mvcc/version_list.hpp +++ b/src/mvcc/version_list.hpp @@ -12,7 +12,7 @@ namespace mvcc { class SerializationError : public utils::BasicException { - static constexpr const char* default_message = + static constexpr const char *default_message = "Can't serialize due to\ concurrent operation(s)"; @@ -194,20 +194,20 @@ class VersionList { auto record = find(t); // check if we found any visible records - if (!record) return nullptr; + permanent_assert(record != nullptr, "Updating nullptr record"); return update(record, t); } - bool remove(tx::Transaction &t) { + void remove(tx::Transaction &t) { debug_assert(head != nullptr, "Head is nullptr on removal."); auto record = find(t); - if (!record) return false; + permanent_assert(record != nullptr, "Removing nullptr record"); // TODO: Is this lock and validate necessary lock_and_validate(record, t); - return remove(record, t), true; + remove(record, t); } // TODO(flor): This should also be private but can't be right now because of diff --git a/tests/unit/mvcc_find_update_common.hpp b/tests/unit/mvcc_find_update_common.hpp index 5001c478d..e3b08a904 100644 --- a/tests/unit/mvcc_find_update_common.hpp +++ b/tests/unit/mvcc_find_update_common.hpp @@ -15,10 +15,22 @@ bool operator==(const Id &left, const int right) { } class TestClass : public mvcc::Record { + public: + // constructs first version, size should be 0 + TestClass(int &version_list_size) : version_list_size_(version_list_size) { + ++version_list_size_; + } + // version constructed in version list update + TestClass(TestClass &other) : version_list_size_(other.version_list_size_) { + version_list_size_++; + } friend std::ostream &operator<<(std::ostream &stream, TestClass &test_class) { stream << test_class.tx.cre() << " " << test_class.tx.exp(); return stream; } + // reference to variable version_list_size in test SetUp, increases when new + // TestClass is created + int &version_list_size_; }; /** @@ -41,17 +53,22 @@ class TestClass : public mvcc::Record { class Mvcc : public ::testing::Test { protected: virtual void SetUp() { + id0 = Id{0}; t1 = &engine.advance(t1->id); + id1 = t1->id; v1 = version_list.find(*t1); t1->commit(); t2 = engine.begin(); + id2 = t2->id; } - + // variable where number of versions is stored + int version_list_size = 0; tx::Engine engine; tx::Transaction *t1 = engine.begin(); - mvcc::VersionList version_list{*t1}; + mvcc::VersionList version_list{*t1, version_list_size}; TestClass *v1 = nullptr; tx::Transaction *t2 = nullptr; + int id0, id1, id2; }; // helper macros. important: @@ -65,11 +82,13 @@ class Mvcc : public ::testing::Test { #define T3_COMMIT t3->commit(); #define T2_ABORT t2->abort(); #define T3_ABORT t3->abort(); -#define T3_BEGIN auto t3 = engine.begin(); +#define T3_BEGIN auto t3 = engine.begin(); __attribute__((unused)) int id3 = t3->id #define T2_REMOVE version_list.remove(*t2) #define T3_REMOVE version_list.remove(*t3) -#define EXPECT_CRE(record, expected) EXPECT_EQ(record->tx.cre(), expected) -#define EXPECT_EXP(record, expected) EXPECT_EQ(record->tx.exp(), expected) +#define EXPECT_CRE(record, expected) EXPECT_EQ(record->tx.cre(), id##expected) +#define EXPECT_EXP(record, expected) EXPECT_EQ(record->tx.exp(), id##expected) +#define EXPECT_NXT(v1, v2) EXPECT_EQ(v1->next(), v2) +#define EXPECT_SIZE(n) EXPECT_EQ(version_list_size, n) // test the fixture TEST_F(Mvcc, Fixture) { diff --git a/tests/unit/mvcc_gc.cpp b/tests/unit/mvcc_gc.cpp index bb7104a83..5835fdf45 100644 --- a/tests/unit/mvcc_gc.cpp +++ b/tests/unit/mvcc_gc.cpp @@ -91,7 +91,7 @@ TEST(GarbageCollector, GcClean) { t1->commit(); auto t2 = engine.begin(); - EXPECT_EQ(vl->remove(*t2), true); + vl->remove(*t2); t2->commit(); gc.Run(Id(3), engine); diff --git a/tests/unit/mvcc_parallel_update.cpp b/tests/unit/mvcc_parallel_update.cpp index 600d84e49..5a8c8d2c8 100644 --- a/tests/unit/mvcc_parallel_update.cpp +++ b/tests/unit/mvcc_parallel_update.cpp @@ -1,88 +1,489 @@ #include "mvcc_find_update_common.hpp" -// TODO Gradicek: rename all existing cases -// TODO Gradicek: check validity of all existing cases -// TODO Gradicek: add all other cases (48 in total, discuss with Flor) -// TODO Gradicek: what about command advance testing, -// as opposed to transaction commit/abort? -TEST_F(Mvcc, Case1_InsertWithUpdates) { +// **************************************************************** +// * CASE 1: T3 starts after T2 ends. +// * +// * T2: START---OP---END +// * +// * T3: START---OP---END +// * +// **************************************************************** + +TEST_F(Mvcc, UpdCmtUpdCmt1) { + T2_UPDATE; + T2_COMMIT; + T3_BEGIN; + T3_UPDATE; + T3_COMMIT; + EXPECT_EXP(v1, 2); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 3); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v3, 0); + EXPECT_NXT(v3, v2); + EXPECT_NXT(v2, v1); + EXPECT_SIZE(3); +} + +TEST_F(Mvcc, UpdCmtRemCmt1) { T2_UPDATE; T2_COMMIT; T3_BEGIN; T3_REMOVE; T3_COMMIT; + EXPECT_EXP(v1, 2); + EXPECT_CRE(v2, 2); EXPECT_EXP(v2, 3); + EXPECT_NXT(v2, v1); + EXPECT_SIZE(2); } -TEST_F(Mvcc, RemoveUpdatedRecord) { +TEST_F(Mvcc, RemCmtUpdCmt1) { + T2_REMOVE; + T2_COMMIT; + T3_BEGIN; + EXPECT_DEATH(T3_UPDATE, ".*nullptr.*"); +} + +TEST_F(Mvcc, RemCmtRemCmt1) { + T2_REMOVE; + T2_COMMIT; + T3_BEGIN; + EXPECT_DEATH(T3_REMOVE, ".*nullptr.*"); +} + +TEST_F(Mvcc, UpdCmtUpdAbt1) { + T2_UPDATE; + T2_COMMIT; + T3_BEGIN; + T3_UPDATE; + T3_ABORT; + EXPECT_EXP(v1, 2); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 3); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v3, 0); + EXPECT_NXT(v2, v1); + EXPECT_NXT(v3, v2); + EXPECT_SIZE(3); +} + +TEST_F(Mvcc, UpdCmtRemAbt1) { + T2_UPDATE; + T2_COMMIT; + T3_BEGIN; + T3_REMOVE; + T3_ABORT; + EXPECT_EXP(v1, 2); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 3); + EXPECT_NXT(v2, v1); + EXPECT_SIZE(2); +} + +TEST_F(Mvcc, RemCmtUpdAbt1) { + T2_REMOVE; + T2_COMMIT; + T3_BEGIN; + EXPECT_DEATH(T3_UPDATE, ".*nullptr.*"); +} + +TEST_F(Mvcc, RemCmtRemAbt1) { + T2_REMOVE; + T2_COMMIT; + T3_BEGIN; + EXPECT_DEATH(T3_REMOVE, ".*nullptr.*"); +} + +TEST_F(Mvcc, UpdAbtUpdCmt1) { + T2_UPDATE; + T2_ABORT; + T3_BEGIN; + T3_UPDATE; + T3_COMMIT; + EXPECT_EXP(v1, 3); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 0); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v3, 0); + EXPECT_NXT(v3, v2); + EXPECT_NXT(v2, v1); + EXPECT_SIZE(3); +} + +TEST_F(Mvcc, UpdAbtRemCmt1) { + T2_UPDATE; + T2_ABORT; + T3_BEGIN; + T3_REMOVE; + T3_COMMIT; + EXPECT_NXT(v2, v1); + EXPECT_EXP(v1, 3); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 0); + EXPECT_SIZE(2); +} + +TEST_F(Mvcc, RemAbtUpdCmt1) { + T2_REMOVE; + T2_ABORT; + T3_BEGIN; + T3_UPDATE; + T3_COMMIT; + EXPECT_EXP(v1, 3); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v3, 0); + EXPECT_NXT(v3, v1); + EXPECT_SIZE(2); +} + +TEST_F(Mvcc, RemAbtRemCmt1) { + T2_REMOVE; + T2_ABORT; + T3_BEGIN; + T3_REMOVE; + T3_COMMIT; + EXPECT_EXP(v1, 3); + EXPECT_SIZE(1); +} + +TEST_F(Mvcc, UpdAbtUpdAbt1) { + T2_UPDATE; + T2_ABORT; + T3_BEGIN; + T3_UPDATE; + T3_ABORT; + EXPECT_EXP(v1, 3); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 0); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v3, 0); + EXPECT_NXT(v2, v1); + EXPECT_NXT(v3, v2); + EXPECT_SIZE(3); +} + +TEST_F(Mvcc, UpdAbtRemAbt1) { + T2_UPDATE; + T2_ABORT; + T3_BEGIN; + T3_REMOVE; + T3_ABORT; + EXPECT_NXT(v2, v1); + EXPECT_EXP(v1, 3); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 0); + EXPECT_SIZE(2); +} + +TEST_F(Mvcc, RemAbtUpdAbt1) { + T2_REMOVE; + T2_ABORT; + T3_BEGIN; + T3_UPDATE; + T3_ABORT; + EXPECT_EXP(v1, 3); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v3, 0); + EXPECT_NXT(v3, v1); + EXPECT_SIZE(2); +} + +TEST_F(Mvcc, RemAbtRemAbt1) { + T2_REMOVE; + T2_ABORT; + T3_BEGIN; + T3_REMOVE; + T3_ABORT; + EXPECT_EXP(v1, 3); + EXPECT_SIZE(1); +} + +// **************************************************************** +// * CASE 2: T3 starts before T2 ends. +// * +// * T2: START---OP---END +// * +// * T3: START---------OP---END +// * +// **************************************************************** + +// **************************** +// COVERS 8 cases! +TEST_F(Mvcc, UpdCmtUpd2) { + T2_UPDATE; + T3_BEGIN; + T2_COMMIT; + EXPECT_THROW(T3_UPDATE, mvcc::SerializationError); +} + +TEST_F(Mvcc, UpdCmtRem2) { + T2_UPDATE; + T3_BEGIN; + T2_COMMIT; + EXPECT_THROW(T3_REMOVE, mvcc::SerializationError); +} + +TEST_F(Mvcc, RemCmtUpd2) { + T2_REMOVE; + T3_BEGIN; + T2_COMMIT; + EXPECT_THROW(T3_UPDATE, mvcc::SerializationError); +} + +TEST_F(Mvcc, RemCmtRem2) { + T2_REMOVE; + T3_BEGIN; + T2_COMMIT; + EXPECT_THROW(T3_REMOVE, mvcc::SerializationError); +} +// ************************** + +TEST_F(Mvcc, UpdAbtUpdCmt2) { + T2_UPDATE; + T3_BEGIN; + T2_ABORT; + T3_UPDATE; + T3_COMMIT; + EXPECT_EXP(v1, 3); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 0); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v3, 0); + EXPECT_NXT(v3, v2); + EXPECT_NXT(v2, v1); + EXPECT_SIZE(3); +} + +TEST_F(Mvcc, UpdAbtRemCmt2) { + T2_UPDATE; + T3_BEGIN; + T2_ABORT; + T3_REMOVE; + T3_COMMIT; + EXPECT_NXT(v2, v1); + EXPECT_EXP(v1, 3); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 0); + EXPECT_SIZE(2); +} + +TEST_F(Mvcc, RemAbtUpdCmt2) { + T2_REMOVE; + T3_BEGIN; + T2_ABORT; + T3_UPDATE; + T3_COMMIT; + EXPECT_EXP(v1, 3); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v3, 0); + EXPECT_NXT(v3, v1); + EXPECT_SIZE(2); +} + +TEST_F(Mvcc, RemAbtRemCmt2) { + T2_REMOVE; + T3_BEGIN; + T2_ABORT; + T3_REMOVE; + T3_COMMIT; + EXPECT_EXP(v1, 3); + EXPECT_SIZE(1); +} + +TEST_F(Mvcc, UpdAbtUpdAbt2) { + T2_UPDATE; + T3_BEGIN; + T2_ABORT; + T3_UPDATE; + T3_ABORT; + EXPECT_EXP(v1, 3); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 0); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v2, 0); + EXPECT_NXT(v2, v1); + EXPECT_NXT(v3, v2); + EXPECT_SIZE(3); +} + +TEST_F(Mvcc, UpdAbtRemAbt2) { + T2_UPDATE; + T3_BEGIN; + T2_ABORT; + T3_REMOVE; + T3_ABORT; + EXPECT_NXT(v2, v1); + EXPECT_EXP(v1, 3); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 0); + EXPECT_SIZE(2); +} + +TEST_F(Mvcc, RemAbtUpdAbt2) { + T2_REMOVE; + T3_BEGIN; + T2_ABORT; + T3_UPDATE; + T3_ABORT; + EXPECT_EXP(v1, 3); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v3, 0); + EXPECT_NXT(v3, v1); + EXPECT_SIZE(2); +} + +TEST_F(Mvcc, RemAbtRemAbt2) { + T2_REMOVE; + T3_BEGIN; + T2_ABORT; + T3_REMOVE; + T3_ABORT; + EXPECT_EXP(v1, 3); + EXPECT_SIZE(1); +} + +// **************************************************************** +// * CASE 3: T3 ends before T2 starts executing operations. +// * +// * T2: START--------------------OP---END +// * +// * T3: START---OP---END +// * +// **************************************************************** + +// **************************** +// COVERS 8 cases! +TEST_F(Mvcc, UpdUpdCmt3) { + T3_BEGIN; + T3_UPDATE; + T3_COMMIT; + EXPECT_THROW(T2_UPDATE, mvcc::SerializationError); +} + +TEST_F(Mvcc, UpdRemCmt3) { + T3_BEGIN; + T3_REMOVE; + T3_COMMIT; + EXPECT_THROW(T2_UPDATE, mvcc::SerializationError); +} + +TEST_F(Mvcc, RemUpdCmt3) { T3_BEGIN; T3_UPDATE; T3_COMMIT; EXPECT_THROW(T2_REMOVE, mvcc::SerializationError); } -TEST_F(Mvcc, UpdateUpdatedRecord) { - T3_BEGIN; - T3_UPDATE; - T3_COMMIT; - EXPECT_THROW(version_list.update(*t2), mvcc::SerializationError); -} - -TEST_F(Mvcc, Case2_AbortUpdate_Remove_T10) { - T2_UPDATE; - T2_ABORT; +TEST_F(Mvcc, RemRemCmt3) { T3_BEGIN; T3_REMOVE; T3_COMMIT; + EXPECT_THROW(T2_REMOVE, mvcc::SerializationError); +} +// ************************** - EXPECT_CRE(v1, 1); - EXPECT_EXP(v1, 3); +TEST_F(Mvcc, UpdCmtUpdAbt3) { + T3_BEGIN; + T3_UPDATE; + T3_ABORT; + T2_UPDATE; + T2_COMMIT; + EXPECT_EXP(v1, 2); EXPECT_CRE(v2, 2); EXPECT_EXP(v2, 0); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v3, 0); + EXPECT_NXT(v3, v1); + EXPECT_NXT(v2, v3); + EXPECT_SIZE(3); } -TEST_F(Mvcc, Case2_AbortUpdate_Remove_T7) { +TEST_F(Mvcc, UpdCmtRemAbt3) { + T3_BEGIN; + T3_REMOVE; + T3_ABORT; + T2_UPDATE; + T2_COMMIT; + EXPECT_EXP(v1, 2); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 0); + EXPECT_NXT(v2, v1); + EXPECT_SIZE(2); +} + +TEST_F(Mvcc, RemCmtUpdAbt3) { T3_BEGIN; T3_UPDATE; T3_ABORT; T2_REMOVE; T2_COMMIT; - EXPECT_CRE(v1, 1); EXPECT_EXP(v1, 2); EXPECT_CRE(v3, 3); EXPECT_EXP(v3, 0); + EXPECT_NXT(v3, v1); + EXPECT_SIZE(2); } -TEST_F(Mvcc, Case2_AbortUpdate_Update_T10) { - T2_UPDATE; - T2_ABORT; +TEST_F(Mvcc, RemCmtRemAbt3) { T3_BEGIN; - T3_UPDATE; - T3_COMMIT; - - EXPECT_CRE(v1, 1); - EXPECT_EXP(v1, 3); - EXPECT_CRE(v3, 3); - EXPECT_EXP(v3, 0); + T3_REMOVE; + T3_ABORT; + T2_REMOVE; + T2_COMMIT; + EXPECT_EXP(v1, 2); + EXPECT_SIZE(1); } -TEST_F(Mvcc, Case2_AbortUpdate_Update_T7) { +TEST_F(Mvcc, UpdAbtUpdAbt3) { T3_BEGIN; T3_UPDATE; T3_ABORT; T2_UPDATE; - T2_COMMIT; - - EXPECT_CRE(v3, 3); - EXPECT_EXP(v3, 0); + T2_ABORT; + EXPECT_EXP(v1, 2); EXPECT_CRE(v2, 2); EXPECT_EXP(v2, 0); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v3, 0); + EXPECT_NXT(v3, v1); + EXPECT_NXT(v2, v3); + EXPECT_SIZE(3); } -TEST_F(Mvcc, Case1Test3) { +TEST_F(Mvcc, UpdAbtRemAbt3) { + T3_BEGIN; + T3_REMOVE; + T3_ABORT; + T2_UPDATE; + T2_ABORT; + EXPECT_NXT(v2, v1); + EXPECT_EXP(v1, 2); + EXPECT_CRE(v2, 2); + EXPECT_EXP(v2, 0); + EXPECT_SIZE(2); +} + +TEST_F(Mvcc, RemAbtUpdAbt3) { T3_BEGIN; T3_UPDATE; - T3_COMMIT; - EXPECT_THROW(T2_REMOVE, mvcc::SerializationError); + T3_ABORT; + T2_REMOVE; + T2_ABORT; + EXPECT_EXP(v1, 2); + EXPECT_CRE(v3, 3); + EXPECT_EXP(v3, 0); + EXPECT_NXT(v3, v1); + EXPECT_SIZE(2); +} + +TEST_F(Mvcc, RemAbtRemAbt3) { + T3_BEGIN; + T3_REMOVE; + T3_ABORT; + T2_REMOVE; + T2_ABORT; + EXPECT_EXP(v1, 2); + EXPECT_SIZE(1); }