diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1385c1f..ed667f6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable(wsrep-lib_test server_context_test.cpp transaction_test.cpp transaction_test_2pc.cpp + transaction_test_xa.cpp view_test.cpp wsrep-lib_test.cpp ) diff --git a/test/mock_client_state.hpp b/test/mock_client_state.hpp index df2c858..db16094 100644 --- a/test/mock_client_state.hpp +++ b/test/mock_client_state.hpp @@ -62,6 +62,8 @@ namespace wsrep : wsrep::client_service() , is_autocommit_() , do_2pc_() + , is_xa_() + , is_xa_prepare_() // , fail_next_applying_() // , fail_next_toi_() , bf_abort_during_wait_() @@ -131,12 +133,12 @@ namespace wsrep bool is_xa() const WSREP_OVERRIDE { - return false; + return is_xa_; } bool is_xa_prepare() const WSREP_OVERRIDE { - return false; + return is_xa_prepare_; } size_t bytes_generated() const WSREP_OVERRIDE @@ -190,6 +192,8 @@ namespace wsrep // bool is_autocommit_; bool do_2pc_; + bool is_xa_; + bool is_xa_prepare_; // bool fail_next_applying_; // bool fail_next_toi_; bool bf_abort_during_wait_; diff --git a/test/mock_storage_service.cpp b/test/mock_storage_service.cpp index ca65794..a455b9b 100644 --- a/test/mock_storage_service.cpp +++ b/test/mock_storage_service.cpp @@ -57,11 +57,27 @@ void wsrep::mock_storage_service::adopt_transaction( int wsrep::mock_storage_service::commit(const wsrep::ws_handle& ws_handle, const wsrep::ws_meta& ws_meta) { - int ret(client_state_.prepare_for_ordering( - ws_handle, ws_meta, true) || - client_state_.before_commit() || - client_state_.ordered_commit() || - client_state_.after_commit()); + // the logic here matches mariadb's wsrep_storage_service::commit + bool ret = 0; + const bool is_ordered = !ws_meta.seqno().is_undefined(); + if (is_ordered) + { + ret = ret || client_state_.prepare_for_ordering(ws_handle, ws_meta, true); + ret = ret || client_state_.before_commit(); + ret = ret || client_state_.ordered_commit(); + ret = ret || client_state_.after_commit(); + } + + if (!is_ordered) + { + client_state_.before_rollback(); + client_state_.after_rollback(); + } + else if (ret) + { + client_state_.prepare_for_ordering(wsrep::ws_handle(), wsrep::ws_meta(), false); + } + client_state_.after_applying(); return ret; } diff --git a/test/transaction_test_xa.cpp b/test/transaction_test_xa.cpp new file mode 100644 index 0000000..a5f961e --- /dev/null +++ b/test/transaction_test_xa.cpp @@ -0,0 +1,136 @@ +#include "client_state_fixture.hpp" +#include + +// +// Test a succesful XA transaction lifecycle +// +BOOST_FIXTURE_TEST_CASE(transaction_xa, + replicating_client_fixture_sync_rm) +{ + BOOST_REQUIRE(cc.start_transaction(wsrep::transaction_id(1)) == 0); + cc.is_xa_ = true; + + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing); + + cc.is_xa_prepare_ = true; + + BOOST_REQUIRE(cc.before_prepare() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing); + BOOST_REQUIRE(tc.ordered() == false); + // certified() only after the last fragment + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.after_prepare() == 0); + BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 1); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing); + // XA START + PREPARE fragment + BOOST_REQUIRE(sc.provider().start_fragments() == 1); + BOOST_REQUIRE(sc.provider().fragments() == 1); + + cc.is_xa_prepare_ = false; + + BOOST_REQUIRE(cc.before_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_committing); + BOOST_REQUIRE(cc.ordered_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_ordered_commit); + BOOST_REQUIRE(tc.ordered()); + BOOST_REQUIRE(tc.certified()); + BOOST_REQUIRE(cc.after_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_committed); + // XA PRERAPRE and XA COMMIT fragments + BOOST_REQUIRE(sc.provider().fragments() == 2); + BOOST_REQUIRE(sc.provider().commit_fragments() == 1); + + BOOST_REQUIRE(cc.after_statement() == 0); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); +} + + +// +// Test a succesful XA transaction lifecycle (applying side) +// +BOOST_FIXTURE_TEST_CASE(transaction_xa_applying, + applying_client_fixture) +{ + cc.is_xa_ = true; + cc.is_xa_prepare_ = true; + + BOOST_REQUIRE(cc.before_prepare() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing); + BOOST_REQUIRE(tc.ordered()); + BOOST_REQUIRE(tc.certified()); + BOOST_REQUIRE(tc.ws_meta().gtid().is_undefined() == false); + BOOST_REQUIRE(cc.after_prepare() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing); + + cc.is_xa_prepare_ = false; + + BOOST_REQUIRE(cc.before_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_committing); + BOOST_REQUIRE(cc.ordered_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_ordered_commit); + BOOST_REQUIRE(cc.after_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_committed); + cc.after_applying(); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_committed); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); +} + +/////////////////////////////////////////////////////////////////////////////// +// STREAMING REPLICATION // +/////////////////////////////////////////////////////////////////////////////// + +// +// Test a succesful XA transaction lifecycle +// +BOOST_FIXTURE_TEST_CASE(transaction_xa_sr, + streaming_client_fixture_byte) +{ + BOOST_REQUIRE(cc.start_transaction(wsrep::transaction_id(1)) == 0); + cc.is_xa_ = true; + cc.bytes_generated_ = 1; + BOOST_REQUIRE(cc.after_row() == 0); + BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 1); + // XA START fragment with data + BOOST_REQUIRE(sc.provider().fragments() == 1); + BOOST_REQUIRE(sc.provider().start_fragments() == 1); + + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing); + + cc.is_xa_prepare_ = true; + + BOOST_REQUIRE(cc.before_prepare() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.after_prepare() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing); + // XA PREPARE fragment + BOOST_REQUIRE(sc.provider().fragments() == 2); + + cc.is_xa_prepare_ = false; + + BOOST_REQUIRE(cc.before_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_committing); + BOOST_REQUIRE(cc.ordered_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_ordered_commit); + BOOST_REQUIRE(tc.ordered()); + BOOST_REQUIRE(tc.certified()); + BOOST_REQUIRE(cc.after_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_committed); + BOOST_REQUIRE(cc.after_statement() == 0); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); + // XA START fragment (with data), XA PREPARE fragment and XA COMMIT fragment + BOOST_REQUIRE(sc.provider().fragments() == 3); + BOOST_REQUIRE(sc.provider().start_fragments() == 1); + BOOST_REQUIRE(sc.provider().commit_fragments() == 1); +}