From 06b07fe94095f168150ac0b07445e597aa7bf14a Mon Sep 17 00:00:00 2001 From: Teemu Ollakka Date: Wed, 20 Mar 2024 19:05:36 +0200 Subject: [PATCH] Add a method to disable BF aborts for transaction --- include/wsrep/client_state.hpp | 19 ++++++++++++++- include/wsrep/transaction.hpp | 2 ++ src/client_state.cpp | 7 ++++++ src/transaction.cpp | 28 ++++++++++++++++++++++ test/transaction_test.cpp | 43 ++++++++++++++++++++++++++++++++++ 5 files changed, 98 insertions(+), 1 deletion(-) diff --git a/include/wsrep/client_state.hpp b/include/wsrep/client_state.hpp index d8449d7..25f973a 100644 --- a/include/wsrep/client_state.hpp +++ b/include/wsrep/client_state.hpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2019 Codership Oy + * Copyright (C) 2018-2024 Codership Oy * * This file is part of wsrep-lib. * @@ -422,6 +422,7 @@ namespace wsrep int ordered_commit(); int after_commit(); + /** @} */ int before_rollback(); @@ -562,6 +563,22 @@ namespace wsrep */ int total_order_bf_abort(wsrep::seqno bf_seqno); + /* + * Disable BF aborts for an active transaction. This method + * may be called several times during the transaction lifetime. + * Calling before_prepare() or before_commit() after calling + * this method will result error. The BF aborts are enabled again + * in the next call to after_statement(). + * + * BF aborts cannot be disabled in the following cases: 1) The + * transactions has already been BF aborted. 2) The transaction + * has generated a write set. + * + * @return Zero on success, non-zero if the transaction was found + * BF aborted. + */ + int disable_bf_abort(); + /** * Adopt a streaming transaction state. This is must be * called from high_priority_service::adopt_transaction() diff --git a/include/wsrep/transaction.hpp b/include/wsrep/transaction.hpp index 3328c09..931253b 100644 --- a/include/wsrep/transaction.hpp +++ b/include/wsrep/transaction.hpp @@ -202,6 +202,8 @@ namespace wsrep wsrep::seqno bf_seqno); bool total_order_bf_abort(wsrep::unique_lock&, wsrep::seqno bf_seqno); + int disable_bf_abort(); + void clone_for_replay(const wsrep::transaction& other); diff --git a/src/client_state.cpp b/src/client_state.cpp index 48501fd..4a1d891 100644 --- a/src/client_state.cpp +++ b/src/client_state.cpp @@ -538,6 +538,13 @@ int wsrep::client_state::total_order_bf_abort(wsrep::seqno bf_seqno) return total_order_bf_abort(lock, bf_seqno); } +int wsrep::client_state::disable_bf_abort() +{ + assert(owning_thread_id_ == wsrep::this_thread::get_id()); + assert(state_ == s_exec || mode_ == m_local); + return transaction_.disable_bf_abort(); +} + void wsrep::client_state::adopt_transaction( const wsrep::transaction& transaction) { diff --git a/src/transaction.cpp b/src/transaction.cpp index 451e94d..e648108 100644 --- a/src/transaction.cpp +++ b/src/transaction.cpp @@ -282,6 +282,11 @@ int wsrep::transaction::before_prepare( assert(state() == s_executing || state() == s_must_abort || state() == s_replaying); + if (is_bf_immutable_) /* Was marked read-only */ + { + return 1; + } + if (state() == s_must_abort) { assert(client_state_.mode() == wsrep::client_state::m_local); @@ -454,6 +459,11 @@ int wsrep::transaction::before_commit() assert((state() != s_committing && state() != s_replaying) || certified()); + if (is_bf_immutable_) /* Was marked read-only */ + { + return 1; + } + switch (client_state_.mode()) { case wsrep::client_state::m_local: @@ -1117,6 +1127,24 @@ bool wsrep::transaction::total_order_bf_abort( return ret; } +int wsrep::transaction::disable_bf_abort() +{ + wsrep::unique_lock lock{client_state_.mutex()}; + assert(active()); + assert(state() == s_executing || state() == s_must_abort); + if (not is_empty()) + { + return 1; + } + if (state() == s_must_abort) + { + return 1; + } + + is_bf_immutable_ = true; + return 0; +} + void wsrep::transaction::clone_for_replay(const wsrep::transaction& other) { assert(other.state() == s_replaying); diff --git a/test/transaction_test.cpp b/test/transaction_test.cpp index 3efd038..264cdc5 100644 --- a/test/transaction_test.cpp +++ b/test/transaction_test.cpp @@ -1267,6 +1267,49 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(transaction_keep_error_bf_after_after_command_b BOOST_REQUIRE(cc.current_error() == wsrep::e_success); } +BOOST_FIXTURE_TEST_CASE(transaction_disable_bf_abort, + replicating_client_fixture_sync_rm) +{ + cc.start_transaction(wsrep::transaction_id(1)); + BOOST_REQUIRE_EQUAL(cc.disable_bf_abort(), 0); + + BOOST_REQUIRE(tc.state() == wsrep::transaction::s_executing); + BOOST_REQUIRE_EQUAL(cc.before_prepare(), 1); + BOOST_REQUIRE_EQUAL(cc.before_commit(), 1); + + cc.before_rollback(); + cc.after_rollback(); + cc.after_statement(); +} + +BOOST_FIXTURE_TEST_CASE(transaction_disable_bf_abort_bf_aborted, + replicating_client_fixture_sync_rm) +{ + cc.start_transaction(wsrep::transaction_id(1)); + wsrep_test::bf_abort_unordered(cc); + BOOST_REQUIRE_EQUAL(cc.disable_bf_abort(), 1); + cc.before_rollback(); + cc.after_rollback(); + cc.after_statement(); +} + +BOOST_FIXTURE_TEST_CASE(transaction_disable_bf_abort_key_appended, + replicating_client_fixture_sync_rm) +{ + cc.start_transaction(wsrep::transaction_id(1)); + 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_EQUAL(cc.append_key(key), 0); + BOOST_REQUIRE_EQUAL(cc.disable_bf_abort(), 1); + cc.before_rollback(); + cc.after_rollback(); + cc.after_statement(); +} + BOOST_FIXTURE_TEST_CASE(transaction_1pc_applying, applying_client_fixture) {