diff --git a/src/client_context.cpp b/src/client_context.cpp index de7f540..25f957b 100644 --- a/src/client_context.cpp +++ b/src/client_context.cpp @@ -3,7 +3,6 @@ // #include "client_context.hpp" -#include "transaction_context.hpp" #include "compiler.hpp" #include @@ -26,15 +25,15 @@ int trrep::client_context::before_command() * \todo Wait until the possible synchronous rollback * has been finished. */ - trrep::unique_lock lock(mutex_); while (transaction_.state() == trrep::transaction_context::s_aborting) { // cond_.wait(lock); } } state(lock, s_exec); - if (transaction_.state() == trrep::transaction_context::s_must_abort || - transaction_.state() == trrep::transaction_context::s_aborted) + if (transaction_.active() && + (transaction_.state() == trrep::transaction_context::s_must_abort || + transaction_.state() == trrep::transaction_context::s_aborted)) { return 1; } @@ -44,11 +43,15 @@ int trrep::client_context::before_command() void trrep::client_context::after_command() { trrep::unique_lock lock(mutex_); - if (transaction_.state() == trrep::transaction_context::s_must_abort) + if (transaction_.active() && + transaction_.state() == trrep::transaction_context::s_must_abort) { + override_error(trrep::e_deadlock_error); lock.unlock(); rollback(transaction_); + transaction_.after_statement(); lock.lock(); + assert(transaction_.state() == trrep::transaction_context::s_aborted); } state(lock, s_idle); } @@ -68,8 +71,10 @@ int trrep::client_context::before_statement() } #endif // 0 - if (transaction_.state() == trrep::transaction_context::s_must_abort) + if (transaction_.active() && + transaction_.state() == trrep::transaction_context::s_must_abort) { + override_error(trrep::e_deadlock_error); lock.unlock(); rollback(transaction_); lock.lock(); diff --git a/src/client_context.hpp b/src/client_context.hpp index 5e0c61a..0c6d880 100644 --- a/src/client_context.hpp +++ b/src/client_context.hpp @@ -57,6 +57,17 @@ namespace trrep e_append_fragment_error }; + static inline std::string to_string(enum client_error error) + { + switch (error) + { + case e_success: return "success"; + case e_error_during_commit: return "error_during_commit"; + case e_deadlock_error: return "deadlock_error"; + case e_append_fragment_error: return "append_fragment_error"; + } + return "unknown"; + } class client_id { public: @@ -285,6 +296,23 @@ namespace trrep { return transaction_; } + + void debug_log_level(int level) { debug_log_level_ = level; } + int debug_log_level() const + { + return std::max(debug_log_level_, + server_context_.debug_log_level()); + } + + void reset_error() + { + current_error_ = trrep::e_success; + } + + enum trrep::client_error current_error() const + { + return current_error_; + } protected: /*! * Client context constuctor. This is protected so that it @@ -301,6 +329,8 @@ namespace trrep , state_(s_idle) , transaction_(*this) , allow_dirty_reads_() + , debug_log_level_(0) + , current_error_(trrep::e_success) { } private: @@ -400,11 +430,6 @@ namespace trrep return 0; } - /*! - * - */ - virtual void override_error(const trrep::client_error&) = 0; - /*! * Return true if the current client operation was killed. */ @@ -432,6 +457,22 @@ namespace trrep */ virtual void debug_suicide(const std::string&) = 0; + /*! + * Notify the implementation about an error. + */ + virtual void on_error(enum trrep::client_error error) = 0; + /*! + * + */ + void override_error(enum trrep::client_error error) + { + if (current_error_ != trrep::e_success && + error == trrep::e_success) + { + throw trrep::runtime_error("Overriding error with success"); + } + current_error_ = error; + } trrep::mutex& mutex_; trrep::server_context& server_context_; @@ -444,6 +485,8 @@ namespace trrep * semantics. */ bool allow_dirty_reads_; + int debug_log_level_; + trrep::client_error current_error_; }; diff --git a/src/dbms_simulator.cpp b/src/dbms_simulator.cpp index 10297d2..7a6cfb6 100644 --- a/src/dbms_simulator.cpp +++ b/src/dbms_simulator.cpp @@ -8,6 +8,7 @@ // through trrep interface. // + #include "server_context.hpp" #include "client_context.hpp" #include "transaction_context.hpp" @@ -39,12 +40,14 @@ struct dbms_simulator_params size_t n_transactions; std::string wsrep_provider; std::string wsrep_provider_options; + int debug_log_level; dbms_simulator_params() : n_servers(0) , n_clients(0) , n_transactions(0) , wsrep_provider() , wsrep_provider_options() + , debug_log_level(0) { } }; @@ -54,7 +57,7 @@ public: dbms_storage_engine() : mutex_() , transactions_() - , alg_freq_(1) + , alg_freq_(100) , bf_aborts_() { } @@ -66,6 +69,9 @@ public: , txc_() { } + + bool active() const { return txc_ != nullptr; } + void start(trrep::transaction_context* txc) { trrep::unique_lock lock(se_.mutex_); @@ -74,16 +80,32 @@ public: ::abort(); } txc_ = txc; - } - ~transaction() + void commit() { if (txc_) { trrep::unique_lock lock(se_.mutex_); se_.transactions_.erase(txc_); } + txc_ = nullptr; + } + + + void abort() + { + if (txc_) + { + trrep::unique_lock lock(se_.mutex_); + se_.transactions_.erase(txc_); + } + txc_ = nullptr; + } + + ~transaction() + { + abort(); } transaction(const transaction&) = delete; @@ -97,7 +119,7 @@ public: void bf_abort_some(const trrep::transaction_context& txc) { trrep::unique_lock lock(mutex_); - if ((std::rand() % alg_freq_) == 0) + if (alg_freq_ && (std::rand() % alg_freq_) == 0) { if (transactions_.empty() == false) { @@ -265,6 +287,7 @@ public: : trrep::client_context(mutex_, server, id, mode) , mutex_() , server_(server) + , se_trx_(server_.storage_engine()) , n_transactions_(n_transactions) { } @@ -302,6 +325,7 @@ private: << "state: " << trrep::to_string(transaction_context.state()); transaction_context.before_rollback(); + se_trx_.abort(); transaction_context.after_rollback(); return 0; } @@ -315,75 +339,82 @@ private: } void wait_for_replayers(trrep::unique_lock&) const override { } - void override_error(const trrep::client_error&) override { } bool killed() const override { return false; } void abort() const override { ::abort(); } void store_globals() override { } void debug_sync(const std::string&) override { } void debug_suicide(const std::string&) override { } + void on_error(enum trrep::client_error) override { } - void run_one_transaction() + template + int client_command(Func f) { int err(before_command()); - dbms_storage_engine::transaction se_trx(server_.storage_engine()); if (err == 0) { err = before_statement(); if (err == 0) { - err = start_transaction(server_.next_transaction_id()); - se_trx.start(&transaction()); + err = f(); } after_statement(); } after_command(); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - for (int i(0); i < 1 && err == 0; ++i) - { - std::ostringstream os; - err = before_command(); - if (err == 0) - { - err = before_statement(); - if (err == 0) - { - int data(std::rand() % 10000000); - os << data; - trrep::key key; - key.append_key_part("dbms", 4); - wsrep_conn_id_t client_id(id().get()); - key.append_key_part(&client_id, sizeof(client_id)); - key.append_key_part(&data, sizeof(data)); - err = append_key(key); - err = append_data(trrep::data(os.str().c_str(), os.str().size())); - } - after_statement(); - } - after_command(); - } - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - if (err == 0) - { - err = before_command(); - if (err == 0) - { - err = before_statement(); + return err; + } - if (err == 0 && do_2pc()) + void run_one_transaction() + { + reset_error(); + int err = client_command( + [&]() + { + err = start_transaction(server_.next_transaction_id()); + assert(err == 0); + se_trx_.start(&transaction()); + return err; + }); + err = err || current_error(); + err = err || client_command( + [&]() + { + assert(transaction().active()); + assert(err == 0); + int data(std::rand() % 10000000); + std::ostringstream os; + os << data; + trrep::key key; + key.append_key_part("dbms", 4); + wsrep_conn_id_t client_id(id().get()); + key.append_key_part(&client_id, sizeof(client_id)); + key.append_key_part(&data, sizeof(data)); + err = append_key(key); + err = err || append_data(trrep::data(os.str().c_str(), + os.str().size())); + return err; + }); + err = err || current_error(); + err = err || client_command( + [&]() + { + assert(err == 0); + if (do_2pc()) { err = err || before_prepare(); err = err || after_prepare(); } err = err || before_commit(); + se_trx_.commit(); err = err || ordered_commit(); err = err || after_commit(); - after_statement(); - } - after_command(); - } - assert(transaction().state() == trrep::transaction_context::s_committed - || - transaction().state() == trrep::transaction_context::s_aborted); + return err; + }); + + assert((current_error() && + transaction().state() == trrep::transaction_context::s_aborted) || + transaction().state() == trrep::transaction_context::s_committed); + assert(se_trx_.active() == false); + assert(transaction().active() == false); } void report_progress(size_t i) const @@ -397,6 +428,7 @@ private: } trrep::default_mutex mutex_; dbms_server& server_; + dbms_storage_engine::transaction se_trx_; const size_t n_transactions_; }; @@ -482,6 +514,7 @@ void dbms_simulator::start() boost::filesystem::create_directory(dir); dbms_server& server(*it.first->second); + server.debug_log_level(params_.debug_log_level); std::string server_options(params_.wsrep_provider_options); if (server.load_provider(params_.wsrep_provider, server_options)) @@ -622,7 +655,9 @@ int main(int argc, char** argv) ("clients", po::value(¶ms.n_clients)->required(), "number of clients to start per server") ("transactions", po::value(¶ms.n_transactions), - "number of transactions run by a client"); + "number of transactions run by a client") + ("debug-log-level", po::value(¶ms.debug_log_level), + "debug logging level: 0 - none, 1 - verbose"); po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); diff --git a/src/exception.hpp b/src/exception.hpp index 76cd7d9..3c8d8bc 100644 --- a/src/exception.hpp +++ b/src/exception.hpp @@ -6,6 +6,7 @@ #define TRREP_EXCEPTION_HPP #include +#include namespace trrep { @@ -14,7 +15,9 @@ namespace trrep public: runtime_error(const std::string& msg) : std::runtime_error(msg) - { } + { + ::abort(); + } }; class not_implemented_error : public std::exception @@ -22,7 +25,9 @@ namespace trrep public: not_implemented_error() : std::exception() - { } + { + ::abort(); + } }; } diff --git a/src/logger.hpp b/src/logger.hpp index 11882ae..ccab66d 100644 --- a/src/logger.hpp +++ b/src/logger.hpp @@ -16,13 +16,14 @@ namespace trrep class log { public: - log() - : oss_() + log(const std::string& prefix = "INFO") + : prefix_(prefix) + , oss_() { } ~log() { trrep::unique_lock lock(mutex_); - os_ << oss_.str() << "\n"; + os_ << prefix_ << ": " << oss_.str() << "\n"; } template std::ostream& operator<<(const T& val) @@ -30,10 +31,19 @@ namespace trrep return (oss_ << val); } private: + const std::string prefix_; std::ostringstream oss_; static trrep::mutex& mutex_; static std::ostream& os_; }; + + + class log_debug : public log + { + public: + log_debug() + : log("DEBUG") { } + }; } #endif // TRREP_LOGGER_HPP diff --git a/src/mock_client_context.hpp b/src/mock_client_context.hpp index 0805df4..1a2e24a 100644 --- a/src/mock_client_context.hpp +++ b/src/mock_client_context.hpp @@ -47,7 +47,6 @@ namespace trrep } void wait_for_replayers(trrep::unique_lock&) const TRREP_OVERRIDE { } - void override_error(const trrep::client_error&) TRREP_OVERRIDE { } bool killed() const TRREP_OVERRIDE { return false; } void abort() const TRREP_OVERRIDE { } void store_globals() TRREP_OVERRIDE { } @@ -56,7 +55,7 @@ namespace trrep { ::abort(); } - + void on_error(enum trrep::client_error) { } // Mock state modifiers void fail_next_applying(bool fail_next_applying) { fail_next_applying_ = fail_next_applying; } diff --git a/src/server_context.hpp b/src/server_context.hpp index 85b700e..e16906b 100644 --- a/src/server_context.hpp +++ b/src/server_context.hpp @@ -335,6 +335,9 @@ namespace trrep const trrep::client_context& client_context, const trrep::transaction_context& transaction_context) const; + void debug_log_level(int level) { debug_log_level_ = level; } + int debug_log_level() const { return debug_log_level_; } + protected: /*! Server Context constructor * @@ -365,6 +368,7 @@ namespace trrep , address_(address) , working_dir_(working_dir) , rollback_mode_(rollback_mode) + , debug_log_level_(0) { } private: @@ -384,6 +388,7 @@ namespace trrep std::string address_; std::string working_dir_; enum rollback_mode rollback_mode_; + int debug_log_level_; }; static inline std::string to_string(enum trrep::server_context::state state) diff --git a/src/transaction_context.cpp b/src/transaction_context.cpp index dbba2d0..76f234f 100644 --- a/src/transaction_context.cpp +++ b/src/transaction_context.cpp @@ -22,6 +22,8 @@ trrep::transaction_context::transaction_context( , id_(transaction_id::invalid()) , state_(s_executing) , state_hist_() + , bf_abort_state_(s_executing) + , bf_abort_client_state_() , ws_handle_() , trx_meta_() , flags_() @@ -42,6 +44,7 @@ int trrep::transaction_context::start_transaction( assert(active() == false); id_ = id; state_ = s_executing; + state_hist_.clear(); ws_handle_.trx_id = id_.get(); flags_ |= WSREP_FLAG_TRX_START; switch (client_context_.mode()) @@ -96,6 +99,7 @@ int trrep::transaction_context::before_prepare() if (state() == s_must_abort) { assert(client_context_.mode() == trrep::client_context::m_replicating); + client_context_.override_error(trrep::e_deadlock_error); return 1; } @@ -143,6 +147,7 @@ int trrep::transaction_context::after_prepare() if (state() == s_must_abort) { assert(client_context_.mode() == trrep::client_context::m_replicating); + client_context_.override_error(trrep::e_deadlock_error); return 1; } @@ -218,12 +223,17 @@ int trrep::transaction_context::before_commit() if (ret == 0) { lock.unlock(); - switch(provider_.commit_order_enter(&ws_handle_, &trx_meta_)) + wsrep_status_t status(provider_.commit_order_enter(&ws_handle_, &trx_meta_)); + lock.lock(); + switch (status) { case WSREP_OK: break; case WSREP_BF_ABORT: - state(lock, s_must_abort); + if (state() != s_must_abort) + { + state(lock, s_must_abort); + } ret = 1; break; default: @@ -231,7 +241,7 @@ int trrep::transaction_context::before_commit() assert(0); break; } - lock.lock(); + } break; case trrep::client_context::m_applier: @@ -405,6 +415,7 @@ int trrep::transaction_context::after_statement() break; case s_must_abort: case s_cert_failed: + client_context_.override_error(trrep::e_deadlock_error); lock.unlock(); ret = client_context_.rollback(*this); lock.lock(); @@ -453,44 +464,64 @@ bool trrep::transaction_context::bf_abort( { bool ret(false); assert(lock.owns_lock()); - switch (state()) + assert(&lock.mutex() == &mutex()); + + if (active() == false) { - case s_executing: - case s_preparing: - case s_certifying: - case s_committing: + trrep::log() << "Transaction not active, skipping bf abort"; + } + else if (ordered() && seqno() < txc.seqno()) { - wsrep_seqno_t victim_seqno(WSREP_SEQNO_UNDEFINED); - wsrep_status_t status(client_context_.provider().bf_abort( - txc.seqno(), id_.get(), &victim_seqno)); - switch (status) + trrep::log() << "Not allowed to BF abort transaction ordered before " + << "aborter: " << seqno() << " < " << txc.seqno(); + } + else + { + switch (state()) { - case WSREP_OK: - trrep::log() << "Seqno " << txc.seqno() - << " succesfully BF aborted " << id_.get() - << " victim_seqno " << victim_seqno; - state(lock, s_must_abort); - ret = true; - break; - default: - trrep::log() << "Seqno " << txc.seqno() - << " failed to BF abort " << id_.get() - << " with status " << status - << " victim_seqno " << victim_seqno; + case s_executing: + case s_preparing: + case s_certifying: + case s_committing: + { + wsrep_seqno_t victim_seqno(WSREP_SEQNO_UNDEFINED); + wsrep_status_t status(client_context_.provider().bf_abort( + txc.seqno(), id_.get(), &victim_seqno)); + switch (status) + { + case WSREP_OK: + trrep::log() << "Seqno " << txc.seqno() + << " succesfully BF aborted " << id_.get() + << " victim_seqno " << victim_seqno; + bf_abort_state_ = state(); + state(lock, s_must_abort); + ret = true; + break; + default: + trrep::log() << "Seqno " << txc.seqno() + << " failed to BF abort " << id_.get() + << " with status " << status + << " victim_seqno " << victim_seqno; + break; + } + break; + } + default: + trrep::log() << "BF abort not allowed in state " + << trrep::to_string(state()); break; } - break; - } - default: - trrep::log() << "BF abort not allowed in state " - << trrep::to_string(state()) << "\n"; - break; } - if (client_context_.server_context().rollback_mode() == trrep::server_context::rm_sync) + if (ret) { - //! \todo Launch background rollbacker. - assert(0); + bf_abort_client_state_ = client_context_.state(); + if (client_context_.server_context().rollback_mode() == + trrep::server_context::rm_sync) + { + //! \todo Launch background rollbacker. + assert(0); + } } return ret; } @@ -761,7 +792,8 @@ void trrep::transaction_context::cleanup() { state_ = s_executing; } - state_hist_.clear(); + // Keep the state history for troubleshooting. Reset at start_transaction(). + // state_hist_.clear(); trx_meta_.gtid = WSREP_GTID_UNDEFINED; trx_meta_.stid.node = WSREP_UUID_UNDEFINED; trx_meta_.stid.trx = trrep::transaction_id::invalid(); @@ -774,11 +806,14 @@ void trrep::transaction_context::cleanup() void trrep::transaction_context::debug_log_state( const std::string& context TRREP_UNUSED) const { -#if 0 - trrep::log() << context - << ": server: " << client_context_.server_context().name() - << ": client: " << client_context_.id().get() - << " trx: " << int64_t(id_.get()) - << " state: " << trrep::to_string(state_); -#endif /* 0 */ + if (client_context_.debug_log_level() >= 1) + { + trrep::log_debug() << context + << ": server: " << client_context_.server_context().name() + << ": client: " << client_context_.id().get() + << " trx: " << int64_t(id_.get()) + << " state: " << trrep::to_string(state_) + << " error: " + << trrep::to_string(client_context_.current_error()); + } } diff --git a/src/transaction_context.hpp b/src/transaction_context.hpp index fd4424e..1311178 100644 --- a/src/transaction_context.hpp +++ b/src/transaction_context.hpp @@ -157,6 +157,8 @@ namespace trrep trrep::transaction_id id_; enum state state_; std::vector state_hist_; + enum state bf_abort_state_; + int bf_abort_client_state_; wsrep_ws_handle_t ws_handle_; wsrep_trx_meta_t trx_meta_; uint32_t flags_; diff --git a/src/transaction_context_test.cpp b/src/transaction_context_test.cpp index 8e3f1a2..ab81f3f 100644 --- a/src/transaction_context_test.cpp +++ b/src/transaction_context_test.cpp @@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_1pc) BOOST_REQUIRE(tc.active() == false); BOOST_REQUIRE(tc.ordered() == false); BOOST_REQUIRE(tc.certified() == false); - + BOOST_REQUIRE(cc.current_error() == trrep::e_success); } // @@ -97,6 +97,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_2pc) BOOST_REQUIRE(tc.active() == false); BOOST_REQUIRE(tc.ordered() == false); BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error() == trrep::e_success); } // @@ -133,7 +134,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_rollback) BOOST_REQUIRE(tc.active() == false); BOOST_REQUIRE(tc.ordered() == false); BOOST_REQUIRE(tc.certified() == false); - + BOOST_REQUIRE(cc.current_error() == trrep::e_success); } // @@ -175,6 +176,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_1pc_bf_before_before_commit) BOOST_REQUIRE(tc.active() == false); BOOST_REQUIRE(tc.ordered() == false); BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error()); } // @@ -216,6 +218,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_2pc_bf_before_before_prepare) BOOST_REQUIRE(tc.active() == false); BOOST_REQUIRE(tc.ordered() == false); BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error()); } // @@ -261,6 +264,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_2pc_bf_before_after_prepare) BOOST_REQUIRE(tc.active() == false); BOOST_REQUIRE(tc.ordered() == false); BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error()); } // @@ -303,8 +307,37 @@ BOOST_AUTO_TEST_CASE(transaction_context_1pc_bf_during_before_commit_uncertified 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 before after_statement. +// +BOOST_AUTO_TEST_CASE(transaction_context_1pc_bf_during_before_after_statement) +{ + trrep::mock_server_context sc("s1", "s1", + trrep::server_context::rm_sync); + trrep::mock_client_context cc(sc, trrep::client_id(1), trrep::client_context::m_replicating); + trrep::transaction_context& tc(cc.transaction()); + + // Verify initial state + BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_executing); + + // Start a new transaction with ID 1 + tc.start_transaction(1); + BOOST_REQUIRE(tc.active()); + BOOST_REQUIRE(tc.id() == trrep::transaction_id(1)); + BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_executing); + + trrep_mock::bf_abort_unordered(cc, tc); + + BOOST_REQUIRE(tc.after_statement() == 0); + 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 @@ -346,6 +379,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_1pc_bf_during_before_commit_certified) BOOST_REQUIRE(tc.active() == false); BOOST_REQUIRE(tc.ordered() == false); BOOST_REQUIRE(tc.certified() == false); + BOOST_REQUIRE(cc.current_error() == trrep::e_success); } BOOST_AUTO_TEST_CASE(transaction_context_1pc_applying) @@ -374,6 +408,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_1pc_applying) BOOST_REQUIRE(tc.after_statement() == 0); BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_committed); BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(cc.current_error() == trrep::e_success); } BOOST_AUTO_TEST_CASE(transaction_context_2pc_applying) @@ -406,6 +441,7 @@ BOOST_AUTO_TEST_CASE(transaction_context_2pc_applying) BOOST_REQUIRE(tc.after_statement() == 0); BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_committed); BOOST_REQUIRE(tc.active() == false); + BOOST_REQUIRE(cc.current_error() == trrep::e_success); } BOOST_AUTO_TEST_CASE(transaction_context_applying_rollback) @@ -432,5 +468,5 @@ BOOST_AUTO_TEST_CASE(transaction_context_applying_rollback) BOOST_REQUIRE(tc.after_statement() == 0); BOOST_REQUIRE(tc.state() == trrep::transaction_context::s_aborted); BOOST_REQUIRE(tc.active() == false); - + BOOST_REQUIRE(cc.current_error() == trrep::e_success); }