mirror of
https://github.com/codership/wsrep-lib.git
synced 2026-10-23 09:48:57 +03:00
MGL-52 Handle BF abort during XA commit/rollback by XID
Although XA commit/rollback by XID does not have active transaction with changes and does not become BF aborted by lock conflict, there are other reasons why it can become BF aborted (e.g. session becoming killed). Add handling of `s_must_abort` in `commit_or_rollback_by_xid()` and unit tests for testing correct behavior.
This commit is contained in:
@@ -1206,9 +1206,20 @@ int wsrep::transaction::commit_or_rollback_by_xid(const wsrep::xid& xid,
|
||||
ws_handle_,
|
||||
flags(),
|
||||
meta, nullptr));
|
||||
client_service_.debug_sync("wsrep_commit_or_rollback_by_xid_after_certify");
|
||||
lock.lock();
|
||||
int ret;
|
||||
if (cert_ret == wsrep::provider::success)
|
||||
if (state_ == s_must_abort) {
|
||||
/* This is hyphotetical situation: Commit or rollback by XID
|
||||
* typically happens from a client context which does not have an active
|
||||
* transaction, so there should not a possibility of BF abort. There
|
||||
* may however be other corner cases (e.g. client session becomes killed
|
||||
* during commit/rollback by XID), so this case must be handled
|
||||
* neverteless. */
|
||||
client_state_.override_error(wsrep::e_error_during_commit);
|
||||
ret = 1;
|
||||
}
|
||||
else if (cert_ret == wsrep::provider::success)
|
||||
{
|
||||
if (commit)
|
||||
{
|
||||
@@ -1225,6 +1236,7 @@ int wsrep::transaction::commit_or_rollback_by_xid(const wsrep::xid& xid,
|
||||
}
|
||||
else
|
||||
{
|
||||
state(lock, s_must_abort);
|
||||
client_state_.override_error(wsrep::e_error_during_commit,
|
||||
cert_ret);
|
||||
wsrep::log_error() << "Failed to commit_or_rollback_by_xid,"
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace wsrep_test
|
||||
// Simple BF abort method to BF abort unordered transasctions
|
||||
void bf_abort_unordered(wsrep::client_state& cc);
|
||||
|
||||
// Simple BF abort method to BF abort unordered transasctions
|
||||
// Simple BF abort method to BF abort ordered transasctions
|
||||
void bf_abort_ordered(wsrep::client_state& cc);
|
||||
|
||||
// BF abort method to abort transactions via provider
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* along with wsrep-lib. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "client_state_fixture.hpp"
|
||||
#include "test_utils.hpp"
|
||||
#include <iostream>
|
||||
|
||||
//
|
||||
@@ -135,6 +136,104 @@ BOOST_FIXTURE_TEST_CASE(transaction_xa_detach_rollback_by_xid,
|
||||
server_service.release_high_priority_service(hps);
|
||||
}
|
||||
|
||||
//
|
||||
// Test BF abort during commit_by_xid certify
|
||||
//
|
||||
BOOST_FIXTURE_TEST_CASE(transaction_xa_detach_commit_by_xid_bf_abort,
|
||||
replicating_two_clients_fixture_sync_rm)
|
||||
{
|
||||
wsrep::xid xid(1, 1, 1, "id");
|
||||
|
||||
cc1.start_transaction(wsrep::transaction_id(1));
|
||||
cc1.assign_xid(xid);
|
||||
cc1.before_prepare();
|
||||
cc1.after_prepare();
|
||||
BOOST_REQUIRE(sc.provider().fragments() == 1);
|
||||
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 1);
|
||||
|
||||
cc1.xa_detach();
|
||||
|
||||
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
|
||||
BOOST_REQUIRE(cc1.after_statement() == 0);
|
||||
|
||||
cc2.start_transaction(wsrep::transaction_id(2));
|
||||
cc2.assign_xid(xid);
|
||||
|
||||
// Inject BF abort for cc2's transaction during certify
|
||||
wsrep_test::bf_abort_provider(sc, cc2, wsrep::seqno(100));
|
||||
|
||||
BOOST_REQUIRE(cc2.client_state::commit_by_xid(xid) == 1);
|
||||
BOOST_REQUIRE(cc2.current_error() == wsrep::e_error_during_commit);
|
||||
|
||||
// Rollback cc2's transaction after failed commit_by_xid
|
||||
BOOST_REQUIRE(cc2.before_rollback() == 0);
|
||||
BOOST_REQUIRE(cc2.after_rollback() == 0);
|
||||
BOOST_REQUIRE(cc2.transaction().state() == wsrep::transaction::s_aborted);
|
||||
// Note that by convention `after_statement()` returns an error only
|
||||
// when the statement can be retried. Currently that is only for
|
||||
// deadlock errors. In this case the error is e_error_during_commit.
|
||||
BOOST_REQUIRE(cc2.after_statement() == 0);
|
||||
|
||||
// xa_detach() creates a streaming applier, clean it up
|
||||
wsrep::mock_high_priority_service* hps(
|
||||
static_cast<wsrep::mock_high_priority_service*>(
|
||||
sc.find_streaming_applier(xid)));
|
||||
BOOST_REQUIRE(hps);
|
||||
hps->rollback(wsrep::ws_handle(), wsrep::ws_meta());
|
||||
hps->after_apply();
|
||||
sc.stop_streaming_applier(sc.id(), wsrep::transaction_id(1));
|
||||
server_service.release_high_priority_service(hps);
|
||||
}
|
||||
|
||||
//
|
||||
// Test BF abort after commit_by_xid certify
|
||||
//
|
||||
BOOST_FIXTURE_TEST_CASE(transaction_xa_detach_commit_by_xid_bf_abort_after_cert,
|
||||
replicating_two_clients_fixture_sync_rm)
|
||||
{
|
||||
wsrep::xid xid(1, 1, 1, "id");
|
||||
|
||||
cc1.start_transaction(wsrep::transaction_id(1));
|
||||
cc1.assign_xid(xid);
|
||||
cc1.before_prepare();
|
||||
cc1.after_prepare();
|
||||
BOOST_REQUIRE(sc.provider().fragments() == 1);
|
||||
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 1);
|
||||
|
||||
cc1.xa_detach();
|
||||
|
||||
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
|
||||
BOOST_REQUIRE(cc1.after_statement() == 0);
|
||||
|
||||
cc2.start_transaction(wsrep::transaction_id(2));
|
||||
cc2.assign_xid(xid);
|
||||
|
||||
cc2.sync_point_enabled_ = "wsrep_commit_or_rollback_by_xid_after_certify";
|
||||
cc2.sync_point_action_ = wsrep::mock_client_service::spa_bf_abort_unordered;
|
||||
|
||||
BOOST_REQUIRE(cc2.client_state::commit_by_xid(xid) == 1);
|
||||
BOOST_REQUIRE(cc2.current_error() == wsrep::e_error_during_commit);
|
||||
|
||||
// Rollback cc2's transaction after failed commit_by_xid
|
||||
BOOST_REQUIRE(cc2.before_rollback() == 0);
|
||||
BOOST_REQUIRE(cc2.after_rollback() == 0);
|
||||
BOOST_REQUIRE(cc2.transaction().state() == wsrep::transaction::s_aborted);
|
||||
// Note that by convention `after_statement()` returns an error only
|
||||
// when the statement can be retried. Currently that is only for
|
||||
// deadlock errors. In this case the error is e_error_during_commit.
|
||||
BOOST_REQUIRE(cc2.after_statement() == 0);
|
||||
|
||||
// xa_detach() creates a streaming applier, clean it up
|
||||
wsrep::mock_high_priority_service* hps(
|
||||
static_cast<wsrep::mock_high_priority_service*>(
|
||||
sc.find_streaming_applier(xid)));
|
||||
BOOST_REQUIRE(hps);
|
||||
hps->rollback(wsrep::ws_handle(), wsrep::ws_meta());
|
||||
hps->after_apply();
|
||||
sc.stop_streaming_applier(sc.id(), wsrep::transaction_id(1));
|
||||
server_service.release_high_priority_service(hps);
|
||||
}
|
||||
|
||||
//
|
||||
// Test XA replay
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user