diff --git a/src/transaction.cpp b/src/transaction.cpp index cb34682..b5fa771 100644 --- a/src/transaction.cpp +++ b/src/transaction.cpp @@ -321,7 +321,8 @@ int wsrep::transaction::before_prepare( assert(state() == s_preparing || (ret && (state() == s_must_abort || state() == s_must_replay || - state() == s_cert_failed))); + state() == s_cert_failed || + state() == s_aborted))); debug_log_state("before_prepare_leave"); return ret; } diff --git a/test/mock_client_state.hpp b/test/mock_client_state.hpp index efb8094..af25fd5 100644 --- a/test/mock_client_state.hpp +++ b/test/mock_client_state.hpp @@ -65,6 +65,7 @@ namespace wsrep // , fail_next_applying_() // , fail_next_toi_() , bf_abort_during_wait_() + , bf_abort_during_fragment_removal_() , error_during_prepare_data_() , killed_before_certify_() , sync_point_enabled_() @@ -83,7 +84,19 @@ namespace wsrep void emergency_shutdown() WSREP_OVERRIDE { ++aborts_; } - int remove_fragments() WSREP_OVERRIDE { return 0; } + int remove_fragments() WSREP_OVERRIDE + { + if (bf_abort_during_fragment_removal_) + { + client_state_.before_rollback(); + client_state_.after_rollback(); + return 1; + } + else + { + return 0; + } + } void will_replay() WSREP_OVERRIDE { } enum wsrep::provider::status @@ -165,6 +178,7 @@ namespace wsrep // bool fail_next_applying_; // bool fail_next_toi_; bool bf_abort_during_wait_; + bool bf_abort_during_fragment_removal_; bool error_during_prepare_data_; bool killed_before_certify_; std::string sync_point_enabled_; diff --git a/test/transaction_test_2pc.cpp b/test/transaction_test_2pc.cpp index b6c30c1..63645d3 100644 --- a/test/transaction_test_2pc.cpp +++ b/test/transaction_test_2pc.cpp @@ -240,6 +240,26 @@ BOOST_FIXTURE_TEST_CASE(transaction_streaming_2pc_commit_two_statements, BOOST_REQUIRE(sc.provider().commit_fragments() == 1); } +// +// Fragments are removed in before_prepare in running transaction context. +// However, the BF abort may arrive during this removal and the +// client_service::remove_fragments() may roll back the transaction +// internally. This will cause the transaction to leave before_prepare() +// in aborted state. +// +BOOST_FIXTURE_TEST_CASE(transaction_streaming_2pc_bf_abort_during_fragment_removal, + streaming_client_fixture_row) +{ + BOOST_REQUIRE(cc.start_transaction(wsrep::transaction_id(1)) == 0); + BOOST_REQUIRE(cc.after_row() == 0); + BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 1); + cc.bf_abort_during_fragment_removal_ = true; + BOOST_REQUIRE(cc.before_prepare()); + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted); + BOOST_REQUIRE(cc.after_statement()); + BOOST_REQUIRE(tc.active() == false); +} + /////////////////////////////////////////////////////////////////////////////// // APPLYING // ///////////////////////////////////////////////////////////////////////////////