From 174ecfe5780668350bfc4506f7278cf102c52694 Mon Sep 17 00:00:00 2001 From: Teemu Ollakka Date: Tue, 12 Jun 2018 18:17:32 +0300 Subject: [PATCH] Moved tests under separate directory. --- CMakeLists.txt | 4 +- dbsim/CMakeLists.txt | 8 + {src => dbsim}/dbms_simulator.cpp | 0 src/CMakeLists.txt | 31 - src/provider.cpp | 2 +- test/CMakeLists.txt | 27 + {src => test}/id_test.cpp | 0 {src => test}/mock_client_context.cpp | 0 {src => test}/mock_client_context.hpp | 0 {src => test}/mock_provider.hpp | 0 {src => test}/mock_server_context.hpp | 0 test/server_context_test.cpp | 85 ++ {src => test}/test_utils.cpp | 0 {src => test}/test_utils.hpp | 0 test/transaction_context_test.cpp | 1224 +++++++++++++++++++++++++ test/wsrep-lib_test.cpp | 7 + 16 files changed, 1355 insertions(+), 33 deletions(-) create mode 100644 dbsim/CMakeLists.txt rename {src => dbsim}/dbms_simulator.cpp (100%) create mode 100644 test/CMakeLists.txt rename {src => test}/id_test.cpp (100%) rename {src => test}/mock_client_context.cpp (100%) rename {src => test}/mock_client_context.hpp (100%) rename {src => test}/mock_provider.hpp (100%) rename {src => test}/mock_server_context.hpp (100%) create mode 100644 test/server_context_test.cpp rename {src => test}/test_utils.cpp (100%) rename {src => test}/test_utils.hpp (100%) create mode 100644 test/transaction_context_test.cpp create mode 100644 test/wsrep-lib_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 847934b..3ed08a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,7 @@ if (WITH_TSAN) endif() add_custom_target(coverage_report - lcov --capture --directory . --output lcov.info --no-external + lcov --capture --directory src --output lcov.info --no-external COMMAND genhtml --output-directory coverage_report lcov.info) @@ -66,4 +66,6 @@ endif() add_subdirectory(src) add_subdirectory(wsrep-API) +add_subdirectory(test) +add_subdirectory(dbsim) diff --git a/dbsim/CMakeLists.txt b/dbsim/CMakeLists.txt new file mode 100644 index 0000000..0e20333 --- /dev/null +++ b/dbsim/CMakeLists.txt @@ -0,0 +1,8 @@ +# +# Copyright (C) 2018 Codership Oy +# + +add_executable(dbms_simulator + dbms_simulator.cpp) +target_link_libraries(dbms_simulator wsrep-lib ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY}) +set_property(TARGET dbms_simulator PROPERTY CXX_STANDARD 14) diff --git a/src/dbms_simulator.cpp b/dbsim/dbms_simulator.cpp similarity index 100% rename from src/dbms_simulator.cpp rename to dbsim/dbms_simulator.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 290b2e4..0d406e9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,8 +2,6 @@ # Copyright (C) 2018 Codership Oy # - - add_library(wsrep-lib client_context.cpp id.cpp @@ -13,32 +11,3 @@ add_library(wsrep-lib transaction_context.cpp wsrep_provider_v26.cpp) target_link_libraries(wsrep-lib wsrep_api_v26 pthread dl) - -add_executable(wsrep-lib_test - mock_client_context.cpp - test_utils.cpp - id_test.cpp - server_context_test.cpp - transaction_context_test.cpp - wsrep-lib_test.cpp - ) -target_link_libraries(wsrep-lib_test wsrep-lib) -add_test(NAME wsrep-lib_test - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/wsrep-lib_test - ) - -if (WITH_AUTO_TEST) - set(UNIT_TEST wsrep-lib_test) - add_custom_command( - TARGET ${UNIT_TEST} - COMMENT "Run tests" - POST_BUILD - COMMAND ${UNIT_TEST} - ) -endif() - -add_executable(dbms_simulator - dbms_simulator.cpp) -target_link_libraries(dbms_simulator wsrep-lib ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY}) -set_property(TARGET dbms_simulator PROPERTY CXX_STANDARD 14) diff --git a/src/provider.cpp b/src/provider.cpp index 72a0b70..f1af0b4 100644 --- a/src/provider.cpp +++ b/src/provider.cpp @@ -5,7 +5,7 @@ #include "wsrep/provider.hpp" #include "wsrep/logger.hpp" -#include "mock_provider.hpp" +#include "../test/mock_provider.hpp" #include "wsrep_provider_v26.hpp" wsrep::provider* wsrep::provider::make_provider( diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..ae24400 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Copyright (C) 2018 Codership Oy +# + +add_executable(wsrep-lib_test + mock_client_context.cpp + test_utils.cpp + id_test.cpp + server_context_test.cpp + transaction_context_test.cpp + wsrep-lib_test.cpp + ) +target_link_libraries(wsrep-lib_test wsrep-lib) +add_test(NAME wsrep-lib_test + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/wsrep-lib_test + ) + +if (WITH_AUTO_TEST) + set(UNIT_TEST wsrep-lib_test) + add_custom_command( + TARGET ${UNIT_TEST} + COMMENT "Run tests" + POST_BUILD + COMMAND ${UNIT_TEST} + ) +endif() diff --git a/src/id_test.cpp b/test/id_test.cpp similarity index 100% rename from src/id_test.cpp rename to test/id_test.cpp diff --git a/src/mock_client_context.cpp b/test/mock_client_context.cpp similarity index 100% rename from src/mock_client_context.cpp rename to test/mock_client_context.cpp diff --git a/src/mock_client_context.hpp b/test/mock_client_context.hpp similarity index 100% rename from src/mock_client_context.hpp rename to test/mock_client_context.hpp diff --git a/src/mock_provider.hpp b/test/mock_provider.hpp similarity index 100% rename from src/mock_provider.hpp rename to test/mock_provider.hpp diff --git a/src/mock_server_context.hpp b/test/mock_server_context.hpp similarity index 100% rename from src/mock_server_context.hpp rename to test/mock_server_context.hpp diff --git a/test/server_context_test.cpp b/test/server_context_test.cpp new file mode 100644 index 0000000..2d2cdd4 --- /dev/null +++ b/test/server_context_test.cpp @@ -0,0 +1,85 @@ +// +// Copyright (C) 2018 Codership Oy +// + +#include "mock_server_context.hpp" + +#include + +namespace +{ + struct applying_server_fixture + { + applying_server_fixture() + : sc("s1", "s1", + wsrep::server_context::rm_sync) + , cc(sc, + wsrep::client_id(1), + wsrep::client_context::m_applier, + false) + , ws_handle(1, (void*)1) + , ws_meta(wsrep::gtid(wsrep::id("1"), wsrep::seqno(1)), + wsrep::stid(wsrep::id("1"), 1, 1), + wsrep::seqno(0), + wsrep::provider::flag::start_transaction | + wsrep::provider::flag::commit) + { + cc.start_transaction(ws_handle, ws_meta); + } + wsrep::mock_server_context sc; + wsrep::mock_client_context cc; + wsrep::ws_handle ws_handle; + wsrep::ws_meta ws_meta; + }; +} + +// Test on_apply() method for 1pc +BOOST_FIXTURE_TEST_CASE(server_context_applying_1pc, + applying_server_fixture) +{ + char buf[1] = { 1 }; + BOOST_REQUIRE(sc.on_apply(cc, ws_handle, ws_meta, + wsrep::const_buffer(buf, 1)) == 0); + const wsrep::transaction_context& txc(cc.transaction()); + // ::abort(); + BOOST_REQUIRE_MESSAGE( + txc.state() == wsrep::transaction_context::s_committed, + "Transaction state " << txc.state() << " not committed"); +} + +// Test on_apply() method for 2pc +BOOST_FIXTURE_TEST_CASE(server_context_applying_2pc, + applying_server_fixture) +{ + char buf[1] = { 1 }; + BOOST_REQUIRE(sc.on_apply(cc, ws_handle, ws_meta, + wsrep::const_buffer(buf, 1)) == 0); + const wsrep::transaction_context& txc(cc.transaction()); + BOOST_REQUIRE(txc.state() == wsrep::transaction_context::s_committed); +} + +// Test on_apply() method for 1pc transaction which +// fails applying and rolls back +BOOST_FIXTURE_TEST_CASE(server_context_applying_1pc_rollback, + applying_server_fixture) +{ + cc.fail_next_applying_ = true; + char buf[1] = { 1 }; + BOOST_REQUIRE(sc.on_apply(cc, ws_handle, ws_meta, + wsrep::const_buffer(buf, 1)) == 1); + const wsrep::transaction_context& txc(cc.transaction()); + BOOST_REQUIRE(txc.state() == wsrep::transaction_context::s_aborted); +} + +// Test on_apply() method for 2pc transaction which +// fails applying and rolls back +BOOST_FIXTURE_TEST_CASE(server_context_applying_2pc_rollback, + applying_server_fixture) +{ + cc.fail_next_applying_ = true; + char buf[1] = { 1 }; + BOOST_REQUIRE(sc.on_apply(cc, ws_handle, ws_meta, + wsrep::const_buffer(buf, 1)) == 1); + const wsrep::transaction_context& txc(cc.transaction()); + BOOST_REQUIRE(txc.state() == wsrep::transaction_context::s_aborted); +} diff --git a/src/test_utils.cpp b/test/test_utils.cpp similarity index 100% rename from src/test_utils.cpp rename to test/test_utils.cpp diff --git a/src/test_utils.hpp b/test/test_utils.hpp similarity index 100% rename from src/test_utils.hpp rename to test/test_utils.hpp diff --git a/test/transaction_context_test.cpp b/test/transaction_context_test.cpp new file mode 100644 index 0000000..33b37e4 --- /dev/null +++ b/test/transaction_context_test.cpp @@ -0,0 +1,1224 @@ +// +// Copyright (C) 2018 Codership Oy +// + +#include "wsrep/transaction_context.hpp" +#include "wsrep/provider.hpp" + +#include "mock_client_context.hpp" +#include "mock_server_context.hpp" + +#include "test_utils.hpp" + +#include +#include + +namespace +{ + struct replicating_client_fixture_sync_rm + { + replicating_client_fixture_sync_rm() + : sc("s1", "s1", wsrep::server_context::rm_sync) + , cc(sc, wsrep::client_id(1), + wsrep::client_context::m_replicating) + , tc(cc.transaction()) + { + BOOST_REQUIRE(cc.before_command() == 0); + BOOST_REQUIRE(cc.before_statement() == 0); + // Verify initial state + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + } + wsrep::mock_server_context sc; + wsrep::mock_client_context cc; + const wsrep::transaction_context& tc; + }; + + struct replicating_client_fixture_async_rm + { + replicating_client_fixture_async_rm() + : sc("s1", "s1", wsrep::server_context::rm_async) + , cc(sc, wsrep::client_id(1), + wsrep::client_context::m_replicating) + , tc(cc.transaction()) + { + BOOST_REQUIRE(cc.before_command() == 0); + BOOST_REQUIRE(cc.before_statement() == 0); + // Verify initial state + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + } + wsrep::mock_server_context sc; + wsrep::mock_client_context cc; + const wsrep::transaction_context& tc; + }; + + struct replicating_client_fixture_autocommit + { + replicating_client_fixture_autocommit() + : sc("s1", "s1", wsrep::server_context::rm_sync) + , cc(sc, wsrep::client_id(1), + wsrep::client_context::m_replicating, true) + , tc(cc.transaction()) + { + BOOST_REQUIRE(cc.before_command() == 0); + BOOST_REQUIRE(cc.before_statement() == 0); + // Verify initial state + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + } + wsrep::mock_server_context sc; + wsrep::mock_client_context cc; + const wsrep::transaction_context& tc; + }; + + struct applying_client_fixture + { + applying_client_fixture() + : sc("s1", "s1", + wsrep::server_context::rm_async) + , cc(sc, + wsrep::client_id(1), + wsrep::client_context::m_applier) + , tc(cc.transaction()) + { + BOOST_REQUIRE(cc.before_command() == 0); + BOOST_REQUIRE(cc.before_statement() == 0); + wsrep::ws_handle ws_handle(1, (void*)1); + wsrep::ws_meta ws_meta(wsrep::gtid(wsrep::id("1"), wsrep::seqno(1)), + wsrep::stid(sc.id(), 1, cc.id()), + wsrep::seqno(0), + wsrep::provider::flag::start_transaction | + wsrep::provider::flag::commit); + BOOST_REQUIRE(cc.start_transaction(ws_handle, ws_meta) == 0); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(cc.start_transaction() == 0); + BOOST_REQUIRE(tc.active() == true); + BOOST_REQUIRE(tc.certified() == true); + BOOST_REQUIRE(tc.ordered() == true); + } + wsrep::mock_server_context sc; + wsrep::mock_client_context cc; + const wsrep::transaction_context& tc; + }; + + struct streaming_client_fixture_row + { + streaming_client_fixture_row() + : sc("s1", "s1", wsrep::server_context::rm_sync) + , cc(sc, wsrep::client_id(1), + wsrep::client_context::m_replicating) + , tc(cc.transaction()) + { + BOOST_REQUIRE(cc.before_command() == 0); + BOOST_REQUIRE(cc.before_statement() == 0); + // Verify initial state + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + cc.enable_streaming(wsrep::transaction_context::streaming_context::row, 1); + } + wsrep::mock_server_context sc; + wsrep::mock_client_context cc; + const wsrep::transaction_context& tc; + }; + + typedef + boost::mpl::vector + replicating_fixtures; +} + +BOOST_FIXTURE_TEST_CASE(transaction_context_append_key_data, + replicating_client_fixture_sync_rm) +{ + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + int vals[3] = {1, 2, 3}; + wsrep::key key(wsrep::key::exclusive); + for (int i(0); i < 3; ++i) + { + key.append_key_part(&vals[i], sizeof(vals[i])); + } + BOOST_REQUIRE(cc.append_key(key) == 0); + wsrep::const_buffer data(&vals[2], sizeof(vals[2])); + BOOST_REQUIRE(cc.append_data(data) == 0); + BOOST_REQUIRE(cc.before_commit() == 0); + BOOST_REQUIRE(cc.ordered_commit() == 0); + BOOST_REQUIRE(cc.after_commit() == 0); + cc.after_statement(); +} +// +// Test a succesful 1PC transaction lifecycle +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE(transaction_context_1pc, T, + replicating_fixtures, T) +{ + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + // Verify that the commit can be succesfully executed in separate command + BOOST_REQUIRE(cc.after_statement() == wsrep::client_context::asr_success); + cc.after_command_before_result(); + cc.after_command_after_result(); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); + BOOST_REQUIRE(cc.before_command() == 0); + BOOST_REQUIRE(cc.before_statement() == 0); + // Run before commit + BOOST_REQUIRE(cc.before_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_committing); + + // Run ordered commit + BOOST_REQUIRE(cc.ordered_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_ordered_commit); + + // Run after commit + BOOST_REQUIRE(cc.after_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_committed); + + // Cleanup after statement + cc.after_statement(); + 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 2PC transaction lifecycle +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE(transaction_context_2pc, T, + replicating_fixtures, T) +{ + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + // Run before prepare + BOOST_REQUIRE(cc.before_prepare() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_preparing); + + // Run after prepare + BOOST_REQUIRE(cc.after_prepare() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_committing); + + // Run before commit + BOOST_REQUIRE(cc.before_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_committing); + + // Run ordered commit + BOOST_REQUIRE(cc.ordered_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_ordered_commit); + + // Run after commit + BOOST_REQUIRE(cc.after_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_committed); + + // Cleanup after statement + cc.after_statement(); + 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 voluntary rollback +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE(transaction_context_rollback, T, + replicating_fixtures, T) +{ + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + // Run before commit + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + + // Run after commit + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + 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 1PC transaction which gets BF aborted before before_commit +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_bf_before_before_commit, T, + replicating_fixtures, T) +{ + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + wsrep_test::bf_abort_unordered(cc); + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error()); +} + +// +// Test a 2PC transaction which gets BF aborted before before_prepare +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_2pc_bf_before_before_prepare, T, + replicating_fixtures, T) +{ + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + wsrep_test::bf_abort_unordered(cc); + + // Run before commit + BOOST_REQUIRE(cc.before_prepare()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error()); +} + +// +// Test a 2PC transaction which gets BF aborted before before_prepare +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_2pc_bf_before_after_prepare, T, + replicating_fixtures, T) +{ + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + // Run before prepare + BOOST_REQUIRE(cc.before_prepare() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_preparing); + + wsrep_test::bf_abort_unordered(cc); + + // Run before commit + BOOST_REQUIRE(cc.after_prepare()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error()); +} + +// +// Test a 1PC transaction which gets BF aborted during before_commit via +// provider before the write set was ordered and certified. +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_bf_during_before_commit_uncertified, T, + replicating_fixtures, T) +{ + wsrep::mock_server_context& sc(T::sc); + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + wsrep_test::bf_abort_provider(sc, tc, wsrep::seqno::undefined()); + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_cert_failed); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error()); +} + +// +// Test a 1PC transaction which gets BF aborted during before_commit +// when waiting for replayers +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_bf_during_commit_wait_for_replayers, T, + replicating_fixtures, T) +{ + wsrep::mock_client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + cc.bf_abort_during_wait_ = true; + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error()); +} + +// +// Test a 1PC transaction for which prepare data fails +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_error_during_prepare_data, T, + replicating_fixtures, T) +{ + wsrep::mock_client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + cc.error_during_prepare_data_ = true; + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(cc.current_error() == wsrep::e_error_during_commit); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error()); +} + +// +// Test a 1PC transaction which gets killed by DBMS before certification +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_killed_before_certify, T, + replicating_fixtures, T) +{ + wsrep::mock_client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + cc.killed_before_certify_ = true; + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(cc.current_error() == wsrep::e_interrupted_error); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error()); +} + + +// +// Test a 1PC transaction which gets BF aborted during before_commit via +// provider after the write set was ordered and certified. This must +// result replaying of transaction. +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_bf_during_before_commit_certified, T, + replicating_fixtures, T) +{ + wsrep::mock_server_context& sc(T::sc); + wsrep::mock_client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + wsrep_test::bf_abort_provider(sc, tc, wsrep::seqno(1)); + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_replay); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == true); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_replay); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_replay); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); + BOOST_REQUIRE(cc.replays() == 1); +} + +// +// Test a 1PC transaction which gets "warning error" from certify call +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_warning_error_from_certify, T, + replicating_fixtures, T) +{ + wsrep::mock_server_context& sc(T::sc); + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + sc.provider().inject_error(wsrep::provider::error_warning); + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + sc.provider().inject_error(wsrep::provider::success); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_error_during_commit); +} + +// +// Test a 1PC transaction which gets transaction missing from certify call +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_transaction_missing_from_certify, T, + replicating_fixtures, T) +{ + wsrep::mock_server_context& sc(T::sc); + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + sc.provider().inject_error(wsrep::provider::error_transaction_missing); + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + sc.provider().inject_error(wsrep::provider::success); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_error_during_commit); +} + +// +// Test a 1PC transaction which gets size exceeded error from certify call +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_size_exceeded_from_certify, T, + replicating_fixtures, T) +{ + wsrep::mock_server_context& sc(T::sc); + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + sc.provider().inject_error(wsrep::provider::error_size_exceeded); + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + sc.provider().inject_error(wsrep::provider::success); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_error_during_commit); +} + +// +// Test a 1PC transaction which gets connection failed error from certify call +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_connection_failed_from_certify, T, + replicating_fixtures, T) +{ + wsrep::mock_server_context& sc(T::sc); + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + sc.provider().inject_error(wsrep::provider::error_connection_failed); + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + sc.provider().inject_error(wsrep::provider::success); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_error_during_commit); +} + +// +// Test a 1PC transaction which gets not allowed error from certify call +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_no_allowed_from_certify, T, + replicating_fixtures, T) +{ + wsrep::mock_server_context& sc(T::sc); + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + sc.provider().inject_error(wsrep::provider::error_not_allowed); + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + sc.provider().inject_error(wsrep::provider::success); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_error_during_commit); +} + +// +// Test a 1PC transaction which gets fatal error from certify call +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_fatal_from_certify, T, + replicating_fixtures, T) +{ + wsrep::mock_server_context& sc(T::sc); + wsrep::mock_client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + sc.provider().inject_error(wsrep::provider::error_fatal); + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + sc.provider().inject_error(wsrep::provider::success); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_error_during_commit); + BOOST_REQUIRE(cc.aborts() == 1); +} + +// +// Test a 1PC transaction which gets unknown from certify call +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_unknown_from_certify, T, + replicating_fixtures, T) +{ + wsrep::mock_server_context& sc(T::sc); + wsrep::mock_client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + sc.provider().inject_error(wsrep::provider::error_unknown); + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(tc.ordered() == false); + + sc.provider().inject_error(wsrep::provider::success); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_error_during_commit); +} + +// +// Test a 1PC transaction which gets BF aborted before grabbing lock +// after certify call +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_bf_abort_after_certify_regain_lock, T, + replicating_fixtures, T) +{ + // wsrep::mock_server_context& sc(T::sc); + wsrep::mock_client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + cc.sync_point_action_ = "wsrep_after_certification"; + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_replay); + BOOST_REQUIRE(tc.certified() == true); + BOOST_REQUIRE(tc.ordered() == true); + + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_replay); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_replay); + + // Cleanup after statement + cc.after_statement(); + BOOST_REQUIRE(cc.replays() == 1); + 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 2PC transaction which gets BF aborted when trying to grab +// commit order. +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_2pc_bf_during_commit_order_enter, T, + replicating_fixtures, T) +{ + wsrep::mock_server_context& sc(T::sc); + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + BOOST_REQUIRE(cc.before_prepare() == 0); + BOOST_REQUIRE(cc.after_prepare() == 0); + + sc.provider().inject_error(wsrep::provider::error_bf_abort); + + // Run before commit + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_replay); + BOOST_REQUIRE(tc.certified() == true); + BOOST_REQUIRE(tc.ordered() == true); + + sc.provider().inject_error(wsrep::provider::success); + // Rollback sequence + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_replay); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_replay); + + // Cleanup after statement + cc.after_statement(); + 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 transaction which gets BF aborted before before_statement. +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_bf_before_before_statement, T, + replicating_fixtures, T) +{ + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + cc.after_statement(); + cc.after_command_before_result(); + cc.after_command_after_result(); + BOOST_REQUIRE(cc.before_command() == 0); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + wsrep_test::bf_abort_unordered(cc); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(cc.before_statement() == 1); + BOOST_REQUIRE(tc.active()); + cc.after_command_before_result(); + BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error); + cc.after_command_after_result(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); +} + +// +// Test a transaction which gets BF aborted before after_statement. +// +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_bf_before_after_statement, T, + replicating_fixtures, T) +{ + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + // Start a new transaction with ID 1 + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_executing); + + wsrep_test::bf_abort_unordered(cc); + + cc.after_statement(); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.ordered() == false); + BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error()); +} + +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_bf_abort_after_after_statement, T, + replicating_fixtures, T) +{ + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + cc.after_statement(); + BOOST_REQUIRE(cc.state() == wsrep::client_context::s_exec); + wsrep_test::bf_abort_unordered(cc); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + cc.after_command_before_result(); + BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + cc.after_command_after_result(); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); +} + +BOOST_FIXTURE_TEST_CASE( + transaction_context_1pc_autocommit_retry_bf_aborted, + replicating_client_fixture_autocommit) +{ + + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + wsrep_test::bf_abort_unordered(cc); + BOOST_REQUIRE(cc.before_commit()); + BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error); + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(cc.after_statement() == wsrep::client_context::asr_may_retry); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + BOOST_REQUIRE(cc.state() == wsrep::client_context::s_exec); +} + +BOOST_FIXTURE_TEST_CASE_TEMPLATE( + transaction_context_1pc_bf_abort_after_after_command_before_result, T, + replicating_fixtures, T) +{ + wsrep::client_context& cc(T::cc); + const wsrep::transaction_context& tc(T::tc); + + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + cc.after_statement(); + BOOST_REQUIRE(cc.state() == wsrep::client_context::s_exec); + cc.after_command_before_result(); + BOOST_REQUIRE(cc.state() == wsrep::client_context::s_result); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); + wsrep_test::bf_abort_unordered(cc); + // The result is being sent to client. We need to mark transaction + // as must_abort but not override error yet as this might cause + // a race condition resulting incorrect result returned to the DBMS client. + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); + // After the result has been sent to the DBMS client, the after result + // processing should roll back the transaction and set the error. + cc.after_command_after_result(); + BOOST_REQUIRE(cc.state() == wsrep::client_context::s_idle); + BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error); + BOOST_REQUIRE(tc.active() == true); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + BOOST_REQUIRE(cc.before_command() == 1); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error); + cc.after_command_before_result(); + BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error); + cc.after_command_after_result(); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); +} + +BOOST_FIXTURE_TEST_CASE( + transaction_context_1pc_bf_abort_after_after_command_after_result_sync_rm, + replicating_client_fixture_sync_rm) +{ + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + cc.after_statement(); + BOOST_REQUIRE(cc.state() == wsrep::client_context::s_exec); + cc.after_command_before_result(); + BOOST_REQUIRE(cc.state() == wsrep::client_context::s_result); + cc.after_command_after_result(); + BOOST_REQUIRE(cc.state() == wsrep::client_context::s_idle); + wsrep_test::bf_abort_unordered(cc); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(cc.before_command() == 1); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error); + cc.after_command_before_result(); + BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error); + cc.after_command_after_result(); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); + BOOST_REQUIRE(tc.active() == false); +} + +BOOST_FIXTURE_TEST_CASE( + transaction_context_1pc_bf_abort_after_after_command_after_result_async_rm, + replicating_client_fixture_async_rm) +{ + cc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + cc.after_statement(); + BOOST_REQUIRE(cc.state() == wsrep::client_context::s_exec); + cc.after_command_before_result(); + BOOST_REQUIRE(cc.state() == wsrep::client_context::s_result); + cc.after_command_after_result(); + BOOST_REQUIRE(cc.state() == wsrep::client_context::s_idle); + wsrep_test::bf_abort_unordered(cc); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_must_abort); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(cc.before_command() == 1); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error); + cc.after_command_before_result(); + BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error); + cc.after_command_after_result(); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); + BOOST_REQUIRE(tc.active() == false); +} + +BOOST_FIXTURE_TEST_CASE(transaction_context_1pc_applying, + applying_client_fixture) +{ + BOOST_REQUIRE(cc.before_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_committing); + BOOST_REQUIRE(cc.ordered_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_ordered_commit); + BOOST_REQUIRE(cc.after_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_committed); + cc.after_statement(); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_committed); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); +} + +BOOST_FIXTURE_TEST_CASE(transaction_context_2pc_applying, + applying_client_fixture) +{ + BOOST_REQUIRE(cc.before_prepare() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_preparing); + BOOST_REQUIRE(cc.after_prepare() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_committing); + BOOST_REQUIRE(cc.before_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_committing); + BOOST_REQUIRE(cc.ordered_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_ordered_commit); + BOOST_REQUIRE(cc.after_commit() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_committed); + cc.after_statement(); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_committed); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); +} + +BOOST_FIXTURE_TEST_CASE(transaction_context_applying_rollback, + applying_client_fixture) +{ + BOOST_REQUIRE(cc.before_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborting); + BOOST_REQUIRE(cc.after_rollback() == 0); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + cc.after_statement(); + BOOST_REQUIRE(tc.state() == wsrep::transaction_context::s_aborted); + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(cc.current_error() == wsrep::e_success); +} + + +/////////////////////////////////////////////////////////////////////////////// +// STREAMING REPLICATION // +/////////////////////////////////////////////////////////////////////////////// + +BOOST_FIXTURE_TEST_CASE(transaction_context_streaming_1pc_commit, + streaming_client_fixture_row) +{ + BOOST_REQUIRE(cc.start_transaction(1) == 0); + BOOST_REQUIRE(cc.after_row() == 0); + BOOST_REQUIRE(tc.streaming_context_.fragments_certified() == 1); + BOOST_REQUIRE(cc.before_commit() == 0); + BOOST_REQUIRE(cc.ordered_commit() == 0); + BOOST_REQUIRE(cc.after_commit() == 0); + BOOST_REQUIRE(cc.after_statement() == wsrep::client_context::asr_success); + BOOST_REQUIRE(sc.provider().fragments() == 2); + BOOST_REQUIRE(sc.provider().start_fragments() == 1); + BOOST_REQUIRE(sc.provider().commit_fragments() == 1); + +} + +BOOST_FIXTURE_TEST_CASE(transaction_context_streaming_2pc_commit, + streaming_client_fixture_row) +{ + BOOST_REQUIRE(cc.start_transaction(1) == 0); + BOOST_REQUIRE(cc.after_row() == 0); + BOOST_REQUIRE(tc.streaming_context_.fragments_certified() == 1); + BOOST_REQUIRE(cc.before_prepare() == 0); + BOOST_REQUIRE(cc.after_prepare() == 0); + BOOST_REQUIRE(cc.before_commit() == 0); + BOOST_REQUIRE(cc.ordered_commit() == 0); + BOOST_REQUIRE(cc.after_commit() == 0); + BOOST_REQUIRE(cc.after_statement() == wsrep::client_context::asr_success); + BOOST_REQUIRE(sc.provider().fragments() == 2); + BOOST_REQUIRE(sc.provider().start_fragments() == 1); + BOOST_REQUIRE(sc.provider().commit_fragments() == 1); + +} diff --git a/test/wsrep-lib_test.cpp b/test/wsrep-lib_test.cpp new file mode 100644 index 0000000..4037e74 --- /dev/null +++ b/test/wsrep-lib_test.cpp @@ -0,0 +1,7 @@ +// +// Copyright (C) 2018 Codership Oy +// + +#define BOOST_TEST_MODULE wsrep_test +#include +