From cd21425e07a6d27c440ee93dafd47a5b4852e6d2 Mon Sep 17 00:00:00 2001 From: Teemu Ollakka Date: Wed, 18 Apr 2018 15:03:12 +0300 Subject: [PATCH] Added unit tests for server_context applying codepath. --- src/CMakeLists.txt | 3 ++ src/client_context.hpp | 13 ++--- src/mock_client_context.cpp | 56 +++++++++++++++++++++ src/mock_client_context.hpp | 15 +++++- src/mock_server_context.hpp | 2 +- src/mock_utils.cpp | 42 ++++++++++++++++ src/mock_utils.hpp | 36 ++++++++++++++ src/server_context.cpp | 3 ++ src/server_context.hpp | 1 - src/server_context_test.cpp | 84 ++++++++++++++++++++++++++++++++ src/transaction_context_test.cpp | 60 ++++------------------- 11 files changed, 253 insertions(+), 62 deletions(-) create mode 100644 src/mock_client_context.cpp create mode 100644 src/mock_utils.cpp create mode 100644 src/mock_utils.hpp create mode 100644 src/server_context_test.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e5ae421..9795a4f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,9 @@ add_library(trrep transaction_context.cpp) add_executable(trrep_test + mock_client_context.cpp + mock_utils.cpp + server_context_test.cpp transaction_context_test.cpp trrep_test.cpp ) diff --git a/src/client_context.hpp b/src/client_context.hpp index 311aec8..d3b9855 100644 --- a/src/client_context.hpp +++ b/src/client_context.hpp @@ -62,15 +62,10 @@ namespace trrep trrep::provider& provider() const; client_id id() const { return id_; } - client_id id(const client_id& id) - { - assert(mode() == m_applier); - id_ = id; - } - enum mode mode() const { return mode_; } enum state state() const { return state_; } + virtual bool do_2pc() const = 0; // // // @@ -85,15 +80,15 @@ namespace trrep virtual int append_fragment(trrep::transaction_context&, uint32_t, const trrep::data&) { return 0; } - virtual int commit(trrep::transaction_context&) { return 0; } - virtual int rollback(trrep::transaction_context&) { return 0; } + virtual int commit(trrep::transaction_context&) = 0; + virtual int rollback(trrep::transaction_context&) = 0; virtual void will_replay(trrep::transaction_context&) { } virtual int replay(trrep::transaction_context& tc); virtual int apply(trrep::transaction_context&, - const trrep::data&) { return 0; } + const trrep::data&) = 0; virtual void wait_for_replayers(trrep::unique_lock&) { } diff --git a/src/mock_client_context.cpp b/src/mock_client_context.cpp new file mode 100644 index 0000000..bfea2c9 --- /dev/null +++ b/src/mock_client_context.cpp @@ -0,0 +1,56 @@ +// +// Copyright (C) 2018 Codership Oy +// + +#include "transaction_context.hpp" +#include "mock_client_context.hpp" + + +int trrep::mock_client_context::apply( + trrep::transaction_context& transaction_context, + const trrep::data& data __attribute__((unused))) + +{ + assert(transaction_context.state() == trrep::transaction_context::s_executing); + return (fail_next_applying_ ? 1 : 0); +} + +int trrep::mock_client_context::commit( + trrep::transaction_context& transaction_context) +{ + int ret(0); + if (do_2pc()) + { + if (transaction_context.before_prepare()) + { + ret = 1; + } + else if (transaction_context.after_prepare()) + { + ret = 1; + } + } + if (ret == 0 && + (transaction_context.before_commit() || + transaction_context.ordered_commit() || + transaction_context.after_commit())) + { + ret = 1; + } + return ret; +} + +int trrep::mock_client_context::rollback( + trrep::transaction_context& transaction_context) +{ + int ret(0); + if (transaction_context.before_rollback()) + { + ret = 1; + } + else if (transaction_context.after_rollback()) + { + ret = 1; + } + return ret; +} diff --git a/src/mock_client_context.hpp b/src/mock_client_context.hpp index f20bfe4..8136716 100644 --- a/src/mock_client_context.hpp +++ b/src/mock_client_context.hpp @@ -15,14 +15,27 @@ namespace trrep public: mock_client_context(trrep::server_context& server_context, const trrep::client_id& id, - enum trrep::client_context::mode mode) + enum trrep::client_context::mode mode, + bool do_2pc = false) : trrep::client_context(mutex_, server_context, id, mode) // Note: Mutex is initialized only after passed // to client_context constructor. , mutex_() + , do_2pc_(do_2pc) + , fail_next_applying_() { } + int apply(trrep::transaction_context&, const trrep::data&); + int commit(trrep::transaction_context&); + int rollback(trrep::transaction_context&); + bool do_2pc() const { return do_2pc_; } + + // Mock state modifiers + void fail_next_applying(bool fail_next_applying) + { fail_next_applying_ = fail_next_applying; } private: trrep::default_mutex mutex_; + bool do_2pc_; + bool fail_next_applying_; }; } diff --git a/src/mock_server_context.hpp b/src/mock_server_context.hpp index 787cc21..d53d337 100644 --- a/src/mock_server_context.hpp +++ b/src/mock_server_context.hpp @@ -22,7 +22,7 @@ namespace trrep , provider_() , last_client_id_(0) { } - trrep::provider& provider() const + trrep::mock_provider& provider() const { return provider_; } trrep::client_context* local_client_context() { diff --git a/src/mock_utils.cpp b/src/mock_utils.cpp new file mode 100644 index 0000000..3f26a08 --- /dev/null +++ b/src/mock_utils.cpp @@ -0,0 +1,42 @@ +// +// Copyright (C) 2018 Codership Oy +// + +#include "mock_utils.hpp" +#include "client_context.hpp" +#include "mock_server_context.hpp" + + +// Simple BF abort method to BF abort unordered transasctions +void trrep_mock::bf_abort_unordered(trrep::client_context& cc, + trrep::transaction_context& tc) +{ + trrep::unique_lock lock(cc.mutex()); + tc.state(lock, trrep::transaction_context::s_must_abort); +} + + // BF abort method to abort transactions via provider +void trrep_mock::bf_abort_provider(trrep::mock_server_context& sc, + const trrep::transaction_context& tc, + wsrep_seqno_t bf_seqno) +{ + wsrep_seqno_t victim_seqno; + sc.provider().bf_abort(bf_seqno, tc.id().get(), &victim_seqno); + (void)victim_seqno; +} + +trrep::transaction_context trrep_mock::applying_transaction( + trrep::client_context& cc, + const trrep::transaction_id& id, + wsrep_seqno_t seqno, + uint32_t flags) +{ + wsrep_ws_handle_t ws_handle = { id.get(), 0 }; + wsrep_trx_meta_t meta = { + { {1 }, seqno }, /* gtid */ + { { static_cast(cc.id().get()) }, id.get(), cc.id().get() }, /* stid */ + seqno - 1 + }; + trrep::transaction_context ret(cc, ws_handle, meta, flags); + return ret; +} diff --git a/src/mock_utils.hpp b/src/mock_utils.hpp new file mode 100644 index 0000000..57e738b --- /dev/null +++ b/src/mock_utils.hpp @@ -0,0 +1,36 @@ +// +// Copyright (C) 2018 Codership Oy +// + +#include // Wsrep typedefs + +// Forward declarations +namespace trrep +{ + class client_context; + class mock_server_context; +} + +#include "transaction_context.hpp" + +// +// Utility functions +// +namespace trrep_mock +{ + + // Simple BF abort method to BF abort unordered transasctions + void bf_abort_unordered(trrep::client_context& cc, + trrep::transaction_context& tc); + + // BF abort method to abort transactions via provider + void bf_abort_provider(trrep::mock_server_context& sc, + const trrep::transaction_context& tc, + wsrep_seqno_t bf_seqno); + + trrep::transaction_context applying_transaction( + trrep::client_context& cc, + const trrep::transaction_id& id, + wsrep_seqno_t seqno, + uint32_t flags); +} diff --git a/src/server_context.cpp b/src/server_context.cpp index 0e48ebb..9c200e8 100644 --- a/src/server_context.cpp +++ b/src/server_context.cpp @@ -94,6 +94,9 @@ int trrep::server_context::on_apply( { ret = 1; } + assert(ret || + transaction_context.state() == + trrep::transaction_context::s_committed); } else { diff --git a/src/server_context.hpp b/src/server_context.hpp index 1c3ce3f..bef1301 100644 --- a/src/server_context.hpp +++ b/src/server_context.hpp @@ -36,7 +36,6 @@ namespace trrep , name_(name) , id_(id) , rollback_mode_(rollback_mode) - { } // diff --git a/src/server_context_test.cpp b/src/server_context_test.cpp new file mode 100644 index 0000000..a80ea5d --- /dev/null +++ b/src/server_context_test.cpp @@ -0,0 +1,84 @@ +// +// Copyright (C) 2018 Codership Oy +// + +#include "mock_server_context.hpp" +#include "mock_utils.hpp" + +#include + +// Test on_apply() method for 1pc +BOOST_AUTO_TEST_CASE(server_context_applying_1pc) +{ + trrep::mock_server_context sc("s1", "s1", + trrep::server_context::rm_sync); + trrep::mock_client_context cc(sc, + trrep::client_id(1), + trrep::client_context::m_applier, + false); + trrep::transaction_context tc( + trrep_mock::applying_transaction( + cc, 1, 1, + WSREP_FLAG_TRX_START | WSREP_FLAG_TRX_END)); + char buf[1] = { 1 }; + BOOST_REQUIRE(sc.on_apply(cc, tc, trrep::data(buf, 1)) == 0); + BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_committed); +} + +// Test on_apply() method for 2pc +BOOST_AUTO_TEST_CASE(server_context_applying_2pc) +{ + trrep::mock_server_context sc("s1", "s1", + trrep::server_context::rm_sync); + trrep::mock_client_context cc(sc, + trrep::client_id(1), + trrep::client_context::m_applier, + true); + trrep::transaction_context tc( + trrep_mock::applying_transaction( + cc, 1, 1, + WSREP_FLAG_TRX_START | WSREP_FLAG_TRX_END)); + char buf[1] = { 1 }; + BOOST_REQUIRE(sc.on_apply(cc, tc, trrep::data(buf, 1)) == 0); + BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_committed); +} + +// Test on_apply() method for 1pc transaction which +// fails applying and rolls back +BOOST_AUTO_TEST_CASE(server_context_applying_1pc_rollback) +{ + trrep::mock_server_context sc("s1", "s1", + trrep::server_context::rm_sync); + trrep::mock_client_context cc(sc, + trrep::client_id(1), + trrep::client_context::m_applier, + false); + cc.fail_next_applying(true); + trrep::transaction_context tc( + trrep_mock::applying_transaction( + cc, 1, 1, + WSREP_FLAG_TRX_START | WSREP_FLAG_TRX_END)); + char buf[1] = { 1 }; + BOOST_REQUIRE(sc.on_apply(cc, tc, trrep::data(buf, 1)) == 1); + BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_aborted); +} + +// Test on_apply() method for 2pc transaction which +// fails applying and rolls back +BOOST_AUTO_TEST_CASE(server_context_applying_2pc_rollback) +{ + trrep::mock_server_context sc("s1", "s1", + trrep::server_context::rm_sync); + trrep::mock_client_context cc(sc, + trrep::client_id(1), + trrep::client_context::m_applier, + true); + cc.fail_next_applying(true); + trrep::transaction_context tc( + trrep_mock::applying_transaction( + cc, 1, 1, + WSREP_FLAG_TRX_START | WSREP_FLAG_TRX_END)); + char buf[1] = { 1 }; + BOOST_REQUIRE(sc.on_apply(cc, tc, trrep::data(buf, 1)) == 1); + BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_aborted); +} diff --git a/src/transaction_context_test.cpp b/src/transaction_context_test.cpp index ab473a3..ac6afb8 100644 --- a/src/transaction_context_test.cpp +++ b/src/transaction_context_test.cpp @@ -7,50 +7,10 @@ #include "mock_server_context.hpp" #include "provider.hpp" +#include "mock_utils.hpp" + #include -// -// Utility functions -// -namespace -{ - // Simple BF abort method to BF abort unordered transasctions - void bf_abort_unordered(trrep::client_context& cc, - trrep::transaction_context& tc) - { - trrep::unique_lock lock(cc.mutex()); - tc.state(lock, trrep::transaction_context::s_must_abort); - } - - // BF abort method to abort transactions via provider - void bf_abort_provider(trrep::mock_server_context& sc, - const trrep::transaction_context& tc, - wsrep_seqno_t bf_seqno) - { - wsrep_seqno_t victim_seqno; - sc.provider().bf_abort(bf_seqno, tc.id().get(), &victim_seqno); - (void)victim_seqno; - } - - trrep::transaction_context applying_transaction( - trrep::client_context& cc, - trrep::transaction_id id, - wsrep_seqno_t seqno, - uint32_t flags) - { - wsrep_ws_handle_t ws_handle = { id.get(), 0 }; - wsrep_trx_meta_t meta = { - { {1 }, seqno }, /* gtid */ - { { static_cast(cc.id().get()) }, id.get(), cc.id().get() }, /* stid */ - seqno - 1 - }; - trrep::transaction_context ret(cc, ws_handle, meta, flags); - return ret; - } - - -} - // // Test a succesful 1PC transaction lifecycle // @@ -196,7 +156,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_1pc_bf_before_before_commit) BOOST_REQUIRE(tc.id() == trrep::transaction_id(1)); BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_executing); - bf_abort_unordered(cc, tc); + trrep_mock::bf_abort_unordered(cc, tc); // Run before commit BOOST_REQUIRE(tc.before_commit()); @@ -237,7 +197,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_2pc_bf_before_before_prepare) BOOST_REQUIRE(tc.id() == trrep::transaction_id(1)); BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_executing); - bf_abort_unordered(cc, tc); + trrep_mock::bf_abort_unordered(cc, tc); // Run before commit BOOST_REQUIRE(tc.before_prepare()); @@ -282,7 +242,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_2pc_bf_before_after_prepare) BOOST_REQUIRE(tc.before_prepare() == 0); BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_preparing); - bf_abort_unordered(cc, tc); + trrep_mock::bf_abort_unordered(cc, tc); // Run before commit BOOST_REQUIRE(tc.after_prepare()); @@ -324,7 +284,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_1pc_bf_during_before_commit_uncertified BOOST_REQUIRE(tc.id() == trrep::transaction_id(1)); BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_executing); - bf_abort_provider(sc, tc, WSREP_SEQNO_UNDEFINED); + trrep_mock::bf_abort_provider(sc, tc, WSREP_SEQNO_UNDEFINED); // Run before commit BOOST_REQUIRE(tc.before_commit()); @@ -367,7 +327,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_1pc_bf_during_before_commit_certified) BOOST_REQUIRE(tc.id() == trrep::transaction_id(1)); BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_executing); - bf_abort_provider(sc, tc, 1); + trrep_mock::bf_abort_provider(sc, tc, 1); // Run before commit BOOST_REQUIRE(tc.before_commit()); @@ -395,7 +355,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_1pc_applying) trrep::mock_client_context cc(sc, trrep::client_id(1), trrep::client_context::m_applier); - trrep::transaction_context tc(applying_transaction( + trrep::transaction_context tc(trrep_mock::applying_transaction( cc, 1, 1, WSREP_FLAG_TRX_START | WSREP_FLAG_TRX_END)); @@ -420,7 +380,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_2pc_applying) trrep::mock_client_context cc(sc, trrep::client_id(1), trrep::client_context::m_applier); - trrep::transaction_context tc(applying_transaction( + trrep::transaction_context tc(trrep_mock::applying_transaction( cc, 1, 1, WSREP_FLAG_TRX_START | WSREP_FLAG_TRX_END)); @@ -449,7 +409,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_applying_rollback) trrep::mock_client_context cc(sc, trrep::client_id(1), trrep::client_context::m_applier); - trrep::transaction_context tc(applying_transaction( + trrep::transaction_context tc(trrep_mock::applying_transaction( cc, 1, 1, WSREP_FLAG_TRX_START | WSREP_FLAG_TRX_END));