1
0
mirror of https://github.com/codership/wsrep-lib.git synced 2025-04-19 21:02:17 +03:00
wsrep-lib/test/transaction_test.cpp
Teemu Ollakka b76e94f84a Transaction not immune after becoming ordered for commit
The transaction state is set to s_ordered_commit in
ordered_commit(). However, this is too late for making the
transaction immune for BF aborts after commit order has
been established, which happens in before_commit().

Moving the state change into before_commit() would be the
right thing to do, but that would require too many fixes
to existing applications which are using the lib.

In order to make the transaction immune for BF abort
after it has been ordered to commit, introduce additional
boolean flag which is set to true at the end of before_commit()
and is taken into account in bf_abort().
2023-04-26 11:46:34 +03:00

1715 lines
63 KiB
C++

/*
* Copyright (C) 2018-2019 Codership Oy <info@codership.com>
*
* This file is part of wsrep-lib.
*
* Wsrep-lib is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Wsrep-lib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with wsrep-lib. If not, see <https://www.gnu.org/licenses/>.
*/
#include "wsrep/transaction.hpp"
#include "wsrep/provider.hpp"
#include "test_utils.hpp"
#include "client_state_fixture.hpp"
#include <boost/mpl/vector.hpp>
namespace
{
typedef
boost::mpl::vector<replicating_client_fixture_sync_rm,
replicating_client_fixture_async_rm>
replicating_fixtures;
}
BOOST_FIXTURE_TEST_CASE(transaction_append_key_data,
replicating_client_fixture_sync_rm)
{
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.is_empty());
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);
BOOST_REQUIRE(tc.is_empty() == false);
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_1pc, T,
replicating_fixtures, T)
{
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
// Establish default read view
BOOST_REQUIRE(0 == cc.assign_read_view(NULL));
// Verify that the commit can be successfully executed in separate command
BOOST_REQUIRE(cc.after_statement() == 0);
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::s_committing);
// Run ordered commit
BOOST_REQUIRE(cc.ordered_commit() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_ordered_commit);
// Run after commit
BOOST_REQUIRE(cc.after_commit() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_rollback, T,
replicating_fixtures, T)
{
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
// Run before commit
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborting);
// Run after commit
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_bf_before_before_commit, T,
replicating_fixtures, T)
{
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
wsrep_test::bf_abort_unordered(cc);
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::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::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_bf_during_before_commit_uncertified, T,
replicating_fixtures, T)
{
wsrep::mock_server_state& sc(T::sc);
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::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::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::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_bf_during_commit_wait_for_replayers, T,
replicating_fixtures, T)
{
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
cc.bf_abort_during_wait_ = true;
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::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::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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());
}
BOOST_FIXTURE_TEST_CASE_TEMPLATE(
transaction_1pc_bf_during_commit_order_enter, T,
replicating_fixtures, T)
{
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
auto& sc(T::sc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
sc.provider().commit_order_enter_result_ = wsrep::provider::error_bf_abort;
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_replay);
BOOST_REQUIRE(cc.will_replay_called() == true);
BOOST_REQUIRE(tc.certified() == true);
BOOST_REQUIRE(tc.ordered() == true);
sc.provider().commit_order_enter_result_ = wsrep::provider::success;
// Rollback sequence
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_replay);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_replay);
// Replay from after_statement()
cc.after_statement();
BOOST_REQUIRE(cc.replays() > 0);
BOOST_REQUIRE(tc.active() == false);
BOOST_REQUIRE(tc.ordered() == false);
BOOST_REQUIRE(tc.certified() == false);
BOOST_REQUIRE(not cc.current_error());
}
BOOST_FIXTURE_TEST_CASE(
transaction_1pc_bf_after_before_commit,
replicating_client_fixture_async_rm)
{
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
// Run before commit
BOOST_REQUIRE(cc.before_commit() == 0);
BOOST_REQUIRE_EQUAL(tc.state(), wsrep::transaction::s_committing);
BOOST_REQUIRE(tc.certified() == true);
BOOST_REQUIRE(tc.ordered() == true);
wsrep_test::bf_abort_ordered(cc);
BOOST_REQUIRE_EQUAL(tc.state(), wsrep::transaction::s_committing);
// Clean up
cc.ordered_commit();
cc.after_commit();
cc.after_statement();
}
//
// Test a 1PC transaction for which prepare data fails
//
BOOST_FIXTURE_TEST_CASE_TEMPLATE(
transaction_1pc_error_during_prepare_data, T,
replicating_fixtures, T)
{
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
cc.error_during_prepare_data_ = true;
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_abort);
BOOST_REQUIRE(cc.current_error() == wsrep::e_size_exceeded_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::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_killed_before_certify, T,
replicating_fixtures, T)
{
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
cc.killed_before_certify_ = true;
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::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::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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 transaction which gets BF aborted inside provider before
// certification result is known. Replaying will be successful
//
BOOST_FIXTURE_TEST_CASE(
transaction_bf_before_cert_result_replay_success,
replicating_client_fixture_sync_rm)
{
BOOST_REQUIRE(cc.start_transaction(wsrep::transaction_id(1)) == 0);
sc.provider().certify_result_ = wsrep::provider::error_bf_abort;
sc.provider().replay_result_ = wsrep::provider::success;
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_replay);
BOOST_REQUIRE(cc.will_replay_called() == true);
BOOST_REQUIRE(cc.after_statement() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_committed);
BOOST_REQUIRE(cc.current_error() == wsrep::e_success);
}
//
// Test a transaction which gets BF aborted inside provider before
// certification result is known. Replaying will fail because of
// certification failure.
//
BOOST_FIXTURE_TEST_CASE(
transaction_bf_before_cert_result_replay_cert_fail,
replicating_client_fixture_sync_rm)
{
BOOST_REQUIRE(cc.start_transaction(wsrep::transaction_id(1)) == 0);
sc.provider().certify_result_ = wsrep::provider::error_bf_abort;
sc.provider().replay_result_ = wsrep::provider::error_certification_failed;
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_replay);
BOOST_REQUIRE(cc.will_replay_called() == true);
BOOST_REQUIRE(cc.after_statement() );
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
BOOST_REQUIRE(tc.active() == false);
}
//
// 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_1pc_bf_during_before_commit_certified, T,
replicating_fixtures, T)
{
wsrep::mock_server_state& sc(T::sc);
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::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::s_must_replay);
BOOST_REQUIRE(cc.will_replay_called() == true);
BOOST_REQUIRE(tc.certified() == false);
BOOST_REQUIRE(tc.ordered() == true);
// Rollback sequence
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_replay);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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 BF aborted simultaneously with
// certification failure. BF abort should not succeed as the
// transaction is going to roll back anyway. Certification failure
// should not generate seqno for write set meta.
//
BOOST_FIXTURE_TEST_CASE_TEMPLATE(
transaction_1pc_bf_before_unordered_cert_failure, T,
replicating_fixtures, T)
{
wsrep::mock_server_state& sc(T::sc);
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
cc.sync_point_enabled_ = "wsrep_before_certification";
cc.sync_point_action_ = wsrep::mock_client_service::spa_bf_abort_unordered;
sc.provider().certify_result_ = wsrep::provider::error_certification_failed;
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_cert_failed);
BOOST_REQUIRE(tc.certified() == false);
BOOST_REQUIRE(tc.ordered() == false);
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
BOOST_REQUIRE(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_deadlock_error);
}
//
// Test a 1PC transaction which gets "warning error" from certify call
//
BOOST_FIXTURE_TEST_CASE_TEMPLATE(
transaction_1pc_warning_error_from_certify, T,
replicating_fixtures, T)
{
wsrep::mock_server_state& sc(T::sc);
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
sc.provider().certify_result_ = wsrep::provider::error_warning;
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_abort);
BOOST_REQUIRE(tc.certified() == false);
BOOST_REQUIRE(tc.ordered() == false);
sc.provider().certify_result_ = wsrep::provider::success;
// Rollback sequence
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_transaction_missing_from_certify, T,
replicating_fixtures, T)
{
wsrep::mock_server_state& sc(T::sc);
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
sc.provider().certify_result_ = wsrep::provider::error_transaction_missing;
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_abort);
BOOST_REQUIRE(tc.certified() == false);
BOOST_REQUIRE(tc.ordered() == false);
sc.provider().certify_result_ = wsrep::provider::success;
// Rollback sequence
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_size_exceeded_from_certify, T,
replicating_fixtures, T)
{
wsrep::mock_server_state& sc(T::sc);
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
sc.provider().certify_result_ = wsrep::provider::error_size_exceeded;
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_abort);
BOOST_REQUIRE(tc.certified() == false);
BOOST_REQUIRE(tc.ordered() == false);
sc.provider().certify_result_ = wsrep::provider::success;
// Rollback sequence
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_connection_failed_from_certify, T,
replicating_fixtures, T)
{
wsrep::mock_server_state& sc(T::sc);
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
sc.provider().certify_result_ = wsrep::provider::error_connection_failed;
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_abort);
BOOST_REQUIRE(tc.certified() == false);
BOOST_REQUIRE(tc.ordered() == false);
sc.provider().certify_result_ = wsrep::provider::success;
// Rollback sequence
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_no_allowed_from_certify, T,
replicating_fixtures, T)
{
wsrep::mock_server_state& sc(T::sc);
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
sc.provider().certify_result_ = wsrep::provider::error_not_allowed;
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_abort);
BOOST_REQUIRE(tc.certified() == false);
BOOST_REQUIRE(tc.ordered() == false);
sc.provider().certify_result_ = wsrep::provider::success;
// Rollback sequence
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_fatal_from_certify, T,
replicating_fixtures, T)
{
wsrep::mock_server_state& sc(T::sc);
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
sc.provider().certify_result_ = wsrep::provider::error_fatal;
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_abort);
BOOST_REQUIRE(tc.certified() == false);
BOOST_REQUIRE(tc.ordered() == false);
sc.provider().certify_result_ = wsrep::provider::success;
// Rollback sequence
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_unknown_from_certify, T,
replicating_fixtures, T)
{
wsrep::mock_server_state& sc(T::sc);
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
sc.provider().certify_result_ = wsrep::provider::error_unknown;
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_abort);
BOOST_REQUIRE(tc.certified() == false);
BOOST_REQUIRE(tc.ordered() == false);
sc.provider().certify_result_ = wsrep::provider::success;
// Rollback sequence
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_bf_abort_before_certify_regain_lock, T,
replicating_fixtures, T)
{
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing);
cc.sync_point_enabled_ = "wsrep_after_certification";
cc.sync_point_action_ = wsrep::mock_client_service::spa_bf_abort_ordered;
// Run before commit
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_replay);
BOOST_REQUIRE(cc.will_replay_called() == true);
BOOST_REQUIRE(tc.certified() == true);
BOOST_REQUIRE(tc.ordered() == true);
// Rollback sequence
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_replay);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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 transaction which gets BF aborted before before_statement.
//
BOOST_FIXTURE_TEST_CASE_TEMPLATE(
transaction_1pc_bf_before_before_statement, T,
replicating_fixtures, T)
{
wsrep::client_state& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::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::s_executing);
wsrep_test::bf_abort_unordered(cc);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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::s_aborted);
}
//
// Test a transaction which gets BF aborted before after_statement.
//
BOOST_FIXTURE_TEST_CASE_TEMPLATE(
transaction_1pc_bf_before_after_statement, T,
replicating_fixtures, T)
{
wsrep::client_state& cc(T::cc);
const wsrep::transaction& tc(T::tc);
// Start a new transaction with ID 1
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(tc.id() == wsrep::transaction_id(1));
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_bf_abort_after_after_statement, T,
replicating_fixtures, T)
{
wsrep::client_state& cc(T::cc);
const wsrep::transaction& tc(T::tc);
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
cc.after_statement();
BOOST_REQUIRE(cc.state() == wsrep::client_state::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::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::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::s_aborted);
}
BOOST_FIXTURE_TEST_CASE(
transaction_1pc_autocommit_retry_bf_aborted,
replicating_client_fixture_autocommit)
{
cc.start_transaction(wsrep::transaction_id(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());
BOOST_REQUIRE(tc.active() == false);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_exec);
}
BOOST_FIXTURE_TEST_CASE_TEMPLATE(
transaction_1pc_bf_abort_after_after_command_before_result, T,
replicating_fixtures, T)
{
wsrep::client_state& cc(T::cc);
const wsrep::transaction& tc(T::tc);
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
cc.after_statement();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_exec);
cc.after_command_before_result();
BOOST_REQUIRE(cc.state() == wsrep::client_state::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::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_state::s_idle);
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
BOOST_REQUIRE(tc.active() == true);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
cc.sync_rollback_complete();
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_1pc_bf_abort_after_after_command_after_result_sync_rm,
replicating_client_fixture_sync_rm)
{
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
cc.after_statement();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_exec);
cc.after_command_before_result();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_result);
cc.after_command_after_result();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_idle);
wsrep_test::bf_abort_unordered(cc);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
BOOST_REQUIRE(tc.active());
cc.sync_rollback_complete();
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);
}
// Check the case where client program calls wait_rollback_complete() to
// gain control before before_command().
BOOST_FIXTURE_TEST_CASE(
transaction_1pc_bf_abort_after_after_command_after_result_sync_rm_wait_rollback,
replicating_client_fixture_sync_rm)
{
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
cc.after_statement();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_exec);
cc.after_command_before_result();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_result);
cc.after_command_after_result();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_idle);
wsrep_test::bf_abort_unordered(cc);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
BOOST_REQUIRE(tc.active());
cc.sync_rollback_complete();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_idle);
cc.wait_rollback_complete_and_acquire_ownership();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_exec);
// Idempotent
cc.wait_rollback_complete_and_acquire_ownership();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_exec);
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);
}
// Check the case where BF abort happens between client calls to
// wait_rollback_complete_and_acquire_ownership()
// and before before_command().
BOOST_FIXTURE_TEST_CASE(
transaction_1pc_bf_abort_after_acquire_before_before_command_sync_rm,
replicating_client_fixture_sync_rm)
{
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
cc.after_statement();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_exec);
cc.after_command_before_result();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_result);
cc.after_command_after_result();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_idle);
cc.wait_rollback_complete_and_acquire_ownership();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_exec);
// As the control is now on client, the BF abort must just change
// the state to s_must_abort.
wsrep_test::bf_abort_unordered(cc);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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_1pc_bf_abort_after_after_command_after_result_async_rm,
replicating_client_fixture_async_rm)
{
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
cc.after_statement();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_exec);
cc.after_command_before_result();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_result);
cc.after_command_after_result();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_idle);
wsrep_test::bf_abort_unordered(cc);
BOOST_REQUIRE(tc.state() == wsrep::transaction::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);
}
//
// Test before_command() with keep_command_error param
// Failure free case is not affected by keep_command_error
//
BOOST_FIXTURE_TEST_CASE_TEMPLATE(transaction_keep_error, T,
replicating_fixtures, T)
{
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
cc.start_transaction(wsrep::transaction_id(1));
BOOST_REQUIRE(tc.active());
cc.after_statement();
cc.after_command_before_result();
cc.after_command_after_result();
bool keep_command_error(true);
BOOST_REQUIRE(cc.before_command(keep_command_error) == 0);
BOOST_REQUIRE(tc.active());
cc.after_command_before_result();
cc.after_command_after_result();
keep_command_error = false;
BOOST_REQUIRE(cc.before_command(keep_command_error) == 0);
BOOST_REQUIRE(cc.before_statement() == 0);
BOOST_REQUIRE(cc.before_commit() == 0);
BOOST_REQUIRE(cc.ordered_commit() == 0);
BOOST_REQUIRE(cc.after_commit() == 0);
cc.after_statement();
BOOST_REQUIRE(cc.current_error() == wsrep::e_success);
}
//
// Test before_command() with keep_command_error param
// BF abort while idle
//
BOOST_FIXTURE_TEST_CASE(transaction_keep_error_bf_idle_sync_rm,
replicating_client_fixture_sync_rm)
{
cc.start_transaction(wsrep::transaction_id(1));
cc.after_statement();
cc.after_command_before_result();
cc.after_command_after_result();
BOOST_REQUIRE(cc.state() == wsrep::client_state::s_idle);
wsrep_test::bf_abort_unordered(cc);
cc.sync_rollback_complete();
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
bool keep_command_error(true);
BOOST_REQUIRE(cc.before_command(keep_command_error) == 0);
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
cc.after_command_before_result();
cc.after_command_after_result();
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
keep_command_error = false;
BOOST_REQUIRE(cc.before_command(keep_command_error) == 1);
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
cc.after_command_before_result();
cc.after_command_after_result();
BOOST_REQUIRE(cc.current_error() == wsrep::e_success);
}
//
// Test before_command() with keep_command_error param
// BF abort after ownership is acquired and before before_command()
//
BOOST_FIXTURE_TEST_CASE_TEMPLATE(transaction_keep_error_bf_after_ownership, T,
replicating_fixtures, T)
{
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
cc.start_transaction(wsrep::transaction_id(1));
cc.after_statement();
cc.after_command_before_result();
cc.after_command_after_result();
cc.wait_rollback_complete_and_acquire_ownership();
wsrep_test::bf_abort_unordered(cc);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_abort);
bool keep_command_error(true);
BOOST_REQUIRE(cc.before_command(keep_command_error) == 0);
BOOST_REQUIRE(tc.active());
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
cc.after_command_before_result();
cc.after_command_after_result();
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
keep_command_error = false;
BOOST_REQUIRE(cc.before_command(keep_command_error) == 1);
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
cc.after_command_before_result();
cc.after_command_after_result();
BOOST_REQUIRE(cc.current_error() == wsrep::e_success);
}
//
// Test before_command() with keep_command_error param
// BF abort right after before_command()
//
BOOST_FIXTURE_TEST_CASE_TEMPLATE(transaction_keep_error_bf_after_before_command, T,
replicating_fixtures, T)
{
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
cc.start_transaction(wsrep::transaction_id(1));
cc.after_statement();
cc.after_command_before_result();
cc.after_command_after_result();
bool keep_command_error(true);
BOOST_REQUIRE(cc.before_command(keep_command_error) == 0);
BOOST_REQUIRE(tc.active());
wsrep_test::bf_abort_unordered(cc);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_abort);
cc.after_command_before_result();
cc.after_command_after_result();
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
keep_command_error = false;
BOOST_REQUIRE(cc.before_command(keep_command_error) == 1);
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
cc.after_command_before_result();
cc.after_command_after_result();
BOOST_REQUIRE(cc.current_error() == wsrep::e_success);
}
//
// Test before_command() with keep_command_error param
// BF abort right after after_command_before_result()
//
BOOST_FIXTURE_TEST_CASE_TEMPLATE(transaction_keep_error_bf_after_after_command_before_result, T,
replicating_fixtures, T)
{
wsrep::mock_client& cc(T::cc);
const wsrep::transaction& tc(T::tc);
cc.start_transaction(wsrep::transaction_id(1));
cc.after_statement();
cc.after_command_before_result();
cc.after_command_after_result();
bool keep_command_error(true);
BOOST_REQUIRE(cc.before_command(keep_command_error) == 0);
BOOST_REQUIRE(tc.active());
cc.after_command_before_result();
wsrep_test::bf_abort_unordered(cc);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_abort);
cc.after_command_after_result();
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
keep_command_error = false;
BOOST_REQUIRE(cc.before_command(keep_command_error) == 1);
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
cc.after_command_before_result();
cc.after_command_after_result();
BOOST_REQUIRE(cc.current_error() == wsrep::e_success);
}
BOOST_FIXTURE_TEST_CASE(transaction_1pc_applying,
applying_client_fixture)
{
start_transaction(wsrep::transaction_id(1),
wsrep::seqno(1));
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);
}
BOOST_FIXTURE_TEST_CASE(transaction_applying_rollback,
applying_client_fixture)
{
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborting);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
cc.after_applying();
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
BOOST_REQUIRE(tc.active() == false);
BOOST_REQUIRE(cc.current_error() == wsrep::e_success);
}
///////////////////////////////////////////////////////////////////////////////
// STREAMING REPLICATION //
///////////////////////////////////////////////////////////////////////////////
//
// Test 1PC with row streaming with one row
//
BOOST_FIXTURE_TEST_CASE(transaction_row_streaming_1pc_commit,
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);
BOOST_REQUIRE(cc.before_commit() == 0);
BOOST_REQUIRE(cc.ordered_commit() == 0);
BOOST_REQUIRE(cc.after_commit() == 0);
BOOST_REQUIRE(cc.after_statement() == 0);
BOOST_REQUIRE(sc.provider().fragments() == 2);
BOOST_REQUIRE(sc.provider().start_fragments() == 1);
BOOST_REQUIRE(sc.provider().commit_fragments() == 1);
}
//
// Test 1PC with row streaming with one row
//
BOOST_FIXTURE_TEST_CASE(transaction_row_batch_streaming_1pc_commit,
streaming_client_fixture_row)
{
BOOST_REQUIRE(cc.enable_streaming(
wsrep::streaming_context::row, 2) == 0);
BOOST_REQUIRE(cc.start_transaction(wsrep::transaction_id(1)) == 0);
BOOST_REQUIRE(cc.after_row() == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 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() == 0);
BOOST_REQUIRE(sc.provider().fragments() == 2);
BOOST_REQUIRE(sc.provider().start_fragments() == 1);
BOOST_REQUIRE(sc.provider().commit_fragments() == 1);
}
//
// Test 1PC row streaming with two separate statements
//
BOOST_FIXTURE_TEST_CASE(
transaction_row_streaming_1pc_commit_two_statements,
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);
BOOST_REQUIRE(cc.after_statement() == 0);
BOOST_REQUIRE(cc.before_statement() == 0);
BOOST_REQUIRE(cc.after_row() == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 2);
BOOST_REQUIRE(cc.before_commit() == 0);
BOOST_REQUIRE(cc.ordered_commit() == 0);
BOOST_REQUIRE(cc.after_commit() == 0);
BOOST_REQUIRE(cc.after_statement() == 0);
BOOST_REQUIRE(sc.provider().fragments() == 3);
BOOST_REQUIRE(sc.provider().start_fragments() == 1);
BOOST_REQUIRE(sc.provider().commit_fragments() == 1);
}
//
// Fragments are removed in before_prepare in running transaction context.
// In 1pc the before_prepare() is called from before_commit().
// 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_1pc_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_commit());
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
BOOST_REQUIRE(cc.after_statement());
BOOST_REQUIRE(tc.active() == false);
wsrep_test::terminate_streaming_applier(sc, sc.id(),
wsrep::transaction_id(1));
}
//
// Test streaming rollback
//
BOOST_FIXTURE_TEST_CASE(transaction_row_streaming_rollback,
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);
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(cc.after_statement() == 0);
BOOST_REQUIRE(sc.provider().fragments() == 2);
BOOST_REQUIRE(sc.provider().start_fragments() == 1);
BOOST_REQUIRE(sc.provider().rollback_fragments() == 1);
wsrep::high_priority_service* hps(
sc.find_streaming_applier(
sc.id(), wsrep::transaction_id(1)));
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 streaming BF abort in executing state.
//
BOOST_FIXTURE_TEST_CASE(transaction_row_streaming_bf_abort_executing,
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);
wsrep_test::bf_abort_unordered(cc);
BOOST_REQUIRE(tc.streaming_context().rolled_back());
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(cc.after_statement());
wsrep_test::terminate_streaming_applier(sc, sc.id(),
wsrep::transaction_id(1));
}
BOOST_FIXTURE_TEST_CASE(
transaction_row_streaming_total_order_bf_abort_executing,
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);
wsrep_test::bf_abort_in_total_order(cc);
BOOST_REQUIRE(tc.bf_aborted_in_total_order());
// TO BF abort must not replicate rollback fragment,
// rollback must complete before TO is allowed to
// continue.
BOOST_REQUIRE(sc.provider().rollback_fragments() == 0);
BOOST_REQUIRE(tc.streaming_context().rolled_back());
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(cc.after_statement());
wsrep_test::terminate_streaming_applier(sc, sc.id(),
wsrep::transaction_id(1));
}
//
// Test streaming certification failure during fragment replication
//
BOOST_FIXTURE_TEST_CASE(transaction_row_streaming_cert_fail_non_commit,
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);
sc.provider().certify_result_ = wsrep::provider::error_certification_failed;
BOOST_REQUIRE(cc.after_row() == 1);
sc.provider().certify_result_ = wsrep::provider::success;
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(cc.after_statement() == 1);
BOOST_REQUIRE(sc.provider().fragments() == 2);
BOOST_REQUIRE(sc.provider().start_fragments() == 1);
BOOST_REQUIRE(sc.provider().rollback_fragments() == 1);
wsrep::high_priority_service* hps(
sc.find_streaming_applier(
sc.id(), wsrep::transaction_id(1)));
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 streaming certification failure during commit
//
BOOST_FIXTURE_TEST_CASE(transaction_row_streaming_cert_fail_commit,
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);
sc.provider().certify_result_ = wsrep::provider::error_certification_failed;
BOOST_REQUIRE(cc.before_commit() == 1);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_cert_failed);
sc.provider().certify_result_ = wsrep::provider::success;
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(cc.after_statement() );
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_aborted);
BOOST_REQUIRE(sc.provider().fragments() == 2);
BOOST_REQUIRE(sc.provider().start_fragments() == 1);
BOOST_REQUIRE(sc.provider().rollback_fragments() == 1);
wsrep::high_priority_service* hps(
sc.find_streaming_applier(
sc.id(), wsrep::transaction_id(1)));
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 streaming BF abort after succesful certification
//
BOOST_FIXTURE_TEST_CASE(
transaction_row_streaming_bf_abort_during_commit_order_enter,
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);
sc.provider().commit_order_enter_result_ = wsrep::provider::error_bf_abort;
BOOST_REQUIRE(cc.before_commit());
BOOST_REQUIRE_EQUAL(tc.state(), wsrep::transaction::s_must_replay);
sc.provider().commit_order_enter_result_ = wsrep::provider::success;
BOOST_REQUIRE(cc.before_rollback() == 0);
BOOST_REQUIRE(cc.after_rollback() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_must_replay);
BOOST_REQUIRE(cc.will_replay_called() == true);
BOOST_REQUIRE(cc.after_statement() == 0);
BOOST_REQUIRE(tc.state() == wsrep::transaction::s_committed);
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_byte_streaming_1pc_commit,
streaming_client_fixture_byte)
{
BOOST_REQUIRE(cc.start_transaction(wsrep::transaction_id(1)) == 0);
cc.bytes_generated_ = 1;
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() == 0);
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_byte_batch_streaming_1pc_commit,
streaming_client_fixture_byte)
{
BOOST_REQUIRE(
cc.enable_streaming(
wsrep::streaming_context::bytes, 2) == 0);
BOOST_REQUIRE(cc.start_transaction(wsrep::transaction_id(1)) == 0);
BOOST_REQUIRE(cc.after_row() == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 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() == 0);
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_statement_streaming_statement_with_no_effect,
streaming_client_fixture_statement)
{
BOOST_REQUIRE(cc.start_transaction(wsrep::transaction_id(1)) == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 0);
BOOST_REQUIRE(cc.before_statement() == 0);
BOOST_REQUIRE(cc.after_statement() == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 0);
BOOST_REQUIRE(cc.after_row() == 0);
BOOST_REQUIRE(cc.before_statement() == 0);
BOOST_REQUIRE(cc.after_statement() == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 1);
BOOST_REQUIRE(cc.before_statement() == 0);
BOOST_REQUIRE(cc.before_commit() == 0);
BOOST_REQUIRE(cc.ordered_commit() == 0);
BOOST_REQUIRE(cc.after_commit() == 0);
BOOST_REQUIRE(cc.after_statement() == 0);
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_statement_streaming_1pc_commit,
streaming_client_fixture_statement)
{
BOOST_REQUIRE(cc.start_transaction(wsrep::transaction_id(1)) == 0);
BOOST_REQUIRE(cc.after_row() == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 0);
BOOST_REQUIRE(cc.after_statement() == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 1);
BOOST_REQUIRE(cc.before_statement() == 0);
BOOST_REQUIRE(cc.before_commit() == 0);
BOOST_REQUIRE(cc.ordered_commit() == 0);
BOOST_REQUIRE(cc.after_commit() == 0);
BOOST_REQUIRE(cc.after_statement() == 0);
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_statement_batch_streaming_1pc_commit,
streaming_client_fixture_statement)
{
BOOST_REQUIRE(
cc.enable_streaming(
wsrep::streaming_context::statement, 2) == 0);
BOOST_REQUIRE(cc.start_transaction(wsrep::transaction_id(1)) == 0);
BOOST_REQUIRE(cc.after_row() == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 0);
BOOST_REQUIRE(cc.after_statement() == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 0);
BOOST_REQUIRE(cc.before_statement() == 0);
BOOST_REQUIRE(cc.after_row() == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 0);
BOOST_REQUIRE(cc.after_statement() == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 1);
BOOST_REQUIRE(cc.before_statement() == 0);
BOOST_REQUIRE(cc.before_commit() == 0);
BOOST_REQUIRE(cc.ordered_commit() == 0);
BOOST_REQUIRE(cc.after_commit() == 0);
BOOST_REQUIRE(cc.after_statement() == 0);
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_statement_streaming_cert_fail,
streaming_client_fixture_statement)
{
BOOST_REQUIRE(cc.start_transaction(wsrep::transaction_id(1)) == 0);
BOOST_REQUIRE(cc.after_row() == 0);
BOOST_REQUIRE(tc.streaming_context().fragments_certified() == 0);
sc.provider().certify_result_ = wsrep::provider::error_certification_failed;
BOOST_REQUIRE(cc.after_statement());
BOOST_REQUIRE(cc.current_error() == wsrep::e_deadlock_error);
// Note: Due to possible limitation in wsrep-API error codes
// or a bug in current Galera provider, rollback fragment may be
// replicated even in case of certification failure.
// If the limitation is lifted later on or the provider is fixed,
// the above check should be change for fragments == 0,
// rollback_fragments == 0.
BOOST_REQUIRE(sc.provider().fragments() == 1);
BOOST_REQUIRE(sc.provider().start_fragments() == 0);
BOOST_REQUIRE(sc.provider().rollback_fragments() == 1);
wsrep::high_priority_service* hps(
sc.find_streaming_applier(
sc.id(), wsrep::transaction_id(1)));
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);
}
///////////////////////////////////////////////////////////////////////////////
// misc //
///////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE(transaction_state_strings)
{
BOOST_REQUIRE(wsrep::to_string(
wsrep::transaction::s_executing) == "executing");
BOOST_REQUIRE(wsrep::to_string(
wsrep::transaction::s_preparing) == "preparing");
BOOST_REQUIRE(
wsrep::to_string(
wsrep::transaction::s_certifying) == "certifying");
BOOST_REQUIRE(
wsrep::to_string(
wsrep::transaction::s_committing) == "committing");
BOOST_REQUIRE(
wsrep::to_string(
wsrep::transaction::s_ordered_commit) == "ordered_commit");
BOOST_REQUIRE(
wsrep::to_string(
wsrep::transaction::s_committed) == "committed");
BOOST_REQUIRE(
wsrep::to_string(
wsrep::transaction::s_cert_failed) == "cert_failed");
BOOST_REQUIRE(
wsrep::to_string(
wsrep::transaction::s_must_abort) == "must_abort");
BOOST_REQUIRE(
wsrep::to_string(
wsrep::transaction::s_aborting) == "aborting");
BOOST_REQUIRE(
wsrep::to_string(
wsrep::transaction::s_aborted) == "aborted");
BOOST_REQUIRE(
wsrep::to_string(
wsrep::transaction::s_must_replay) == "must_replay");
BOOST_REQUIRE(
wsrep::to_string(
wsrep::transaction::s_replaying) == "replaying");
}