diff --git a/include/mysql/service_wsrep.h b/include/mysql/service_wsrep.h index cf94364c1cf82..4168b133a4853 100644 --- a/include/mysql/service_wsrep.h +++ b/include/mysql/service_wsrep.h @@ -8,6 +8,11 @@ enum Wsrep_service_key_type WSREP_SERVICE_KEY_UPDATE, WSREP_SERVICE_KEY_EXCLUSIVE }; + + +/* the bits in the bitmask for disabling temporarily some asserts */ +#define WSREP_ASSERT_INNODB_TRX 1 + #if (defined (MYSQL_DYNAMIC_PLUGIN) && defined(MYSQL_SERVICE_WSREP_DYNAMIC_INCLUDED)) || (!defined(MYSQL_DYNAMIC_PLUGIN) && defined(MYSQL_SERVICE_WSREP_STATIC_INCLUDED)) @@ -64,6 +69,7 @@ extern struct wsrep_service_st { bool (*wsrep_thd_ignore_table_func)(MYSQL_THD thd); long long (*wsrep_thd_trx_seqno_func)(const MYSQL_THD thd); my_bool (*wsrep_thd_is_aborting_func)(const MYSQL_THD thd); + my_bool (*wsrep_thd_in_rollback_func)(const THD *thd); void (*wsrep_set_data_home_dir_func)(const char *data_dir); my_bool (*wsrep_thd_is_BF_func)(const MYSQL_THD thd, my_bool sync); my_bool (*wsrep_thd_is_local_func)(const MYSQL_THD thd); @@ -123,6 +129,7 @@ extern struct wsrep_service_st { #define wsrep_set_data_home_dir(A) wsrep_service->wsrep_set_data_home_dir_func(A) #define wsrep_thd_is_BF(T,S) wsrep_service->wsrep_thd_is_BF_func(T,S) #define wsrep_thd_is_aborting(T) wsrep_service->wsrep_thd_is_aborting_func(T) +#define wsrep_thd_in_rollback_func(T) wsrep_service->wsrep_thd_in_rollback_func(T) #define wsrep_thd_is_local(T) wsrep_service->wsrep_thd_is_local_func(T) #define wsrep_thd_self_abort(T) wsrep_service->wsrep_thd_self_abort_func(T) #define wsrep_thd_append_key(T,W,N,K) wsrep_service->wsrep_thd_append_key_func(T,W,N,K) @@ -225,6 +232,8 @@ extern "C" my_bool wsrep_thd_order_before(const MYSQL_THD left, const MYSQL_THD extern "C" my_bool wsrep_thd_skip_locking(const MYSQL_THD thd); /* Return true if thd is aborting */ extern "C" my_bool wsrep_thd_is_aborting(const MYSQL_THD thd); +/* Return true if thd is a WSREP applier rolling back a transaction locally */ +extern "C" my_bool wsrep_thd_in_rollback(const MYSQL_THD thd); struct wsrep_key; struct wsrep_key_array; diff --git a/mysql-test/suite/galera/r/mdev-36554.result b/mysql-test/suite/galera/r/mdev-36554.result new file mode 100644 index 0000000000000..c4271093c1f89 --- /dev/null +++ b/mysql-test/suite/galera/r/mdev-36554.result @@ -0,0 +1,25 @@ +connection node_2; +connection node_1; +CALL mtr.add_suppression("Event .* Update_rows.* apply failed"); +CALL mtr.add_suppression("mariadbd: Can't find record in 't1'"); +CALL mtr.add_suppression("Failed to apply write set.*"); +CALL mtr.add_suppression("Inconsistency detected"); +connection node_1; +CREATE TABLE t1 (f1 INTEGER); +INSERT INTO t1 VALUES (1); +connection node_2; +SET SESSION wsrep_on = OFF; +DELETE FROM t1; +SET SESSION wsrep_on = ON; +SET GLOBAL wsrep_applier_retry_count=1; +connection node_1; +UPDATE t1 SET f1 = f1 + 1; +connection node_2; +Shutting down server ... +SET wsrep_on=OFF; +Restarting server ... +connection node_1; +SET wsrep_sync_wait=0; +connection node_1; +SET GLOBAL wsrep_applier_retry_count = 0; +DROP TABLE t1; diff --git a/mysql-test/suite/galera/t/mdev-36554.test b/mysql-test/suite/galera/t/mdev-36554.test new file mode 100644 index 0000000000000..f2a57c6e242c8 --- /dev/null +++ b/mysql-test/suite/galera/t/mdev-36554.test @@ -0,0 +1,45 @@ +# +# Test for MDEV-36554 +# + +--source include/galera_cluster.inc + +CALL mtr.add_suppression("Event .* Update_rows.* apply failed"); +CALL mtr.add_suppression("mariadbd: Can't find record in 't1'"); +CALL mtr.add_suppression("Failed to apply write set.*"); +CALL mtr.add_suppression("Inconsistency detected"); + +--connection node_1 +CREATE TABLE t1 (f1 INTEGER); +INSERT INTO t1 VALUES (1); + +--connection node_2 +SET SESSION wsrep_on = OFF; +DELETE FROM t1; +SET SESSION wsrep_on = ON; +SET GLOBAL wsrep_applier_retry_count=1; + +--connection node_1 +UPDATE t1 SET f1 = f1 + 1; + +# restart node 2 +--connection node_2 +--echo Shutting down server ... +SET wsrep_on=OFF; +--source include/shutdown_mysqld.inc +--source include/wait_until_disconnected.inc +--remove_file $MYSQLTEST_VARDIR/mysqld.2/data/grastate.dat +--echo Restarting server ... +--source include/start_mysqld.inc + +# wait till node 2 is back in the cluster +--connection node_1 +SET wsrep_sync_wait=0; +--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM performance_schema.global_status WHERE VARIABLE_NAME = 'wsrep_cluster_size' +--source include/wait_condition.inc + +# cleanup +--connection node_1 +SET GLOBAL wsrep_applier_retry_count = 0; +DROP TABLE t1; + diff --git a/sql/service_wsrep.cc b/sql/service_wsrep.cc index d58a05b3eb54e..d5646fa3cb06f 100644 --- a/sql/service_wsrep.cc +++ b/sql/service_wsrep.cc @@ -295,6 +295,22 @@ extern "C" my_bool wsrep_thd_is_aborting(const MYSQL_THD thd) return false; } +/** Check if a wsrep applier is rolling back a transaction locally. + +This function is used for notifying InnoDB routines that this thread +is rolling back a wsrep transaction locally. + +@param thd thread handle + +@return true if wsrep applier is rolling back a transaction locally +@return false otherwise + +*/ +extern "C" my_bool wsrep_thd_in_rollback(const THD *thd) +{ + return thd->wsrep_applier_is_in_rollback(); +} + static inline enum wsrep::key::type map_key_type(enum Wsrep_service_key_type type) { diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 08354e0fa2db3..685dbd4e9c7ad 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -742,6 +742,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) , wsrep_applier(is_wsrep_applier), wsrep_applier_closing(false), + wsrep_applier_in_rollback(false), wsrep_client_thread(false), wsrep_retry_counter(0), wsrep_PA_safe(true), diff --git a/sql/sql_class.h b/sql/sql_class.h index c9e6d8d5f2d0e..17fc9fe90cbd3 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -5641,6 +5641,8 @@ class THD: public THD_count, /* this must be first */ #ifdef WITH_WSREP bool wsrep_applier; /* dedicated slave applier thread */ bool wsrep_applier_closing; /* applier marked to close */ + bool wsrep_applier_in_rollback; /* applier is rolling + back a transaction */ bool wsrep_client_thread; /* to identify client threads*/ query_id_t wsrep_last_query_id; XID wsrep_xid; @@ -5720,7 +5722,6 @@ class THD: public THD_count, /* this must be first */ return m_wsrep_client_state.transaction().id().get(); } - /* Set next trx id */ @@ -5760,6 +5761,8 @@ class THD: public THD_count, /* this must be first */ Wsrep_applier_service* wsrep_applier_service; /* wait_for_commit struct for binlog group commit */ wait_for_commit wsrep_wfc; + bool wsrep_applier_is_in_rollback() const + { return wsrep_applier_in_rollback; } #endif /* WITH_WSREP */ /* Handling of timeouts for commands */ diff --git a/sql/sql_plugin_services.inl b/sql/sql_plugin_services.inl index 2114f3560a7ec..6cf8514216edb 100644 --- a/sql/sql_plugin_services.inl +++ b/sql/sql_plugin_services.inl @@ -159,6 +159,7 @@ static struct wsrep_service_st wsrep_handler = { wsrep_thd_ignore_table, wsrep_thd_trx_seqno, wsrep_thd_is_aborting, + wsrep_thd_in_rollback, wsrep_set_data_home_dir, wsrep_thd_is_BF, wsrep_thd_is_local, diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc index 31a28ac4d4164..580630a1147dd 100644 --- a/sql/wsrep_applier.cc +++ b/sql/wsrep_applier.cc @@ -292,11 +292,14 @@ int wsrep_apply_events(THD* thd, /* rollback to savepoint without telling Wsrep-lib */ thd->variables.wsrep_on = false; + thd->wsrep_applier_in_rollback= true; if (FALSE != trans_rollback_to_savepoint(thd, savepoint)) { thd->variables.wsrep_on = true; + thd->wsrep_applier_in_rollback= false; break; } thd->variables.wsrep_on = true; + thd->wsrep_applier_in_rollback= false; /* reset THD object for retry */ thd->clear_error(); diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc index 8762dd9907e19..95949b9adffdf 100644 --- a/sql/wsrep_dummy.cc +++ b/sql/wsrep_dummy.cc @@ -94,6 +94,9 @@ long long wsrep_thd_trx_seqno(const THD *) my_bool wsrep_thd_is_aborting(const THD *) { return 0; } +my_bool wsrep_thd_in_rollback(const THD *) +{ return 0; } + void wsrep_set_data_home_dir(const char *) { } diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 55c283859cbfc..f4b9446c633f6 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -1501,7 +1501,11 @@ TRANSACTIONAL_INLINE inline void trx_t::commit_in_memory(const mtr_t *mtr) trx_finalize_for_fts(this, undo_no != 0); #ifdef WITH_WSREP - ut_ad(is_wsrep() == wsrep_on(mysql_thd)); + /* Assert that any transaction which is started as a wsrep + transaction, also commits or rolls back as a wsrep + transaction. Wsrep applier may turn wsrep off temporarily for + rollback when the applying of wsrep transactions is retried. */ + ut_ad(is_wsrep() == wsrep_on(mysql_thd) || wsrep_thd_in_rollback(mysql_thd)); /* Serialization history has been written and the transaction is committed in memory, which makes this commit ordered. Release commit