Revert MDEV-18464 and MDEV-12009

This reverts commit 21b2fada7ab7f35c898c02d2f918461409cc9c8e
and commit 81d71ee6b21870772c336bff15b71904914f146a.

The MDEV-18464 change introduces a few data race issues. Contrary to
the documentation, the field trx_t::victim is not always being protected
by lock_sys_t::mutex and trx_t::mutex. Most importantly, it seems
that KILL QUERY could wrongly avoid acquiring both mutexes when
invoking lock_trx_handle_wait_low(), in case another thread had
already set trx->victim=true.

We also revert MDEV-12009, because it should depend on the MDEV-18464
fix being present.
This commit is contained in:
Marko Mäkelä 2019-03-28 12:27:06 +02:00
parent 81d71ee6b2
commit d0116e10a5
22 changed files with 178 additions and 194 deletions

View File

@ -112,7 +112,6 @@ extern struct wsrep_service_st {
int (*wsrep_trx_order_before_func)(MYSQL_THD, MYSQL_THD); int (*wsrep_trx_order_before_func)(MYSQL_THD, MYSQL_THD);
void (*wsrep_unlock_rollback_func)(); void (*wsrep_unlock_rollback_func)();
void (*wsrep_set_data_home_dir_func)(const char *data_dir); void (*wsrep_set_data_home_dir_func)(const char *data_dir);
my_bool (*wsrep_thd_is_applier_func)(THD *thd);
} *wsrep_service; } *wsrep_service;
#ifdef MYSQL_DYNAMIC_PLUGIN #ifdef MYSQL_DYNAMIC_PLUGIN
@ -156,7 +155,6 @@ extern struct wsrep_service_st {
#define wsrep_trx_order_before(T1,T2) wsrep_service->wsrep_trx_order_before_func(T1,T2) #define wsrep_trx_order_before(T1,T2) wsrep_service->wsrep_trx_order_before_func(T1,T2)
#define wsrep_unlock_rollback() wsrep_service->wsrep_unlock_rollback_func() #define wsrep_unlock_rollback() wsrep_service->wsrep_unlock_rollback_func()
#define wsrep_set_data_home_dir(A) wsrep_service->wsrep_set_data_home_dir_func(A) #define wsrep_set_data_home_dir(A) wsrep_service->wsrep_set_data_home_dir_func(A)
#define wsrep_thd_is_applier(T) wsrep_service->wsrep_thd_is_applier_func(T)
#define wsrep_debug get_wsrep_debug() #define wsrep_debug get_wsrep_debug()
#define wsrep_log_conflicts get_wsrep_log_conflicts() #define wsrep_log_conflicts get_wsrep_log_conflicts()
@ -216,7 +214,6 @@ void wsrep_thd_set_conflict_state(THD *thd, enum wsrep_conflict_state state);
bool wsrep_thd_ignore_table(THD *thd); bool wsrep_thd_ignore_table(THD *thd);
void wsrep_unlock_rollback(); void wsrep_unlock_rollback();
void wsrep_set_data_home_dir(const char *data_dir); void wsrep_set_data_home_dir(const char *data_dir);
my_bool wsrep_thd_is_applier(THD *thd);
#endif #endif

View File

@ -1,8 +1,4 @@
CREATE USER foo@localhost;
GRANT SELECT on test.* TO foo@localhost;
# Open connection to the 1st node using 'test_user1' user.
Got one of the listed errors Got one of the listed errors
Got one of the listed errors Got one of the listed errors
Got one of the listed errors Got one of the listed errors
Got one of the listed errors Got one of the listed errors
DROP USER foo@localhost;

View File

@ -1,10 +0,0 @@
!include ../galera_2nodes.cnf
[mysqld.1]
wsrep_provider_options='base_port=@mysqld.1.#galera_port;pc.ignore_sb=true'
auto_increment_offset=1
[mysqld.2]
wsrep_provider_options='base_port=@mysqld.2.#galera_port;pc.ignore_sb=true'
auto_increment_offset=2

View File

@ -1,25 +1,14 @@
# #
# This test checks that applier threads are immune to KILL QUERY and KILL STATEMENT # This test checks that applier threads are immune to KILL QUERY and KILL STATEMENT
# when USER is not SUPER
# #
--source include/galera_cluster.inc --source include/galera_cluster.inc
--source include/have_innodb.inc
--connection node_1 --connection node_1
CREATE USER foo@localhost;
GRANT SELECT on test.* TO foo@localhost;
--let $applier_thread = `SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE != 'wsrep aborter idle' OR STATE IS NULL LIMIT 1` --let $applier_thread = `SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE != 'wsrep aborter idle' OR STATE IS NULL LIMIT 1`
--let $aborter_thread = `SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE = 'wsrep aborter idle' LIMIT 1`
--echo # Open connection to the 1st node using 'test_user1' user.
--let $port_1= \$NODE_MYPORT_1
--connect(foo_node_1,localhost,foo,,test,$port_1,)
--connection foo_node_1
--disable_query_log --disable_query_log
--error ER_KILL_DENIED_ERROR,ER_KILL_DENIED_ERROR --error ER_KILL_DENIED_ERROR,ER_KILL_DENIED_ERROR
--eval KILL $applier_thread --eval KILL $applier_thread
@ -27,48 +16,11 @@ GRANT SELECT on test.* TO foo@localhost;
--error ER_KILL_DENIED_ERROR,ER_KILL_DENIED_ERROR --error ER_KILL_DENIED_ERROR,ER_KILL_DENIED_ERROR
--eval KILL QUERY $applier_thread --eval KILL QUERY $applier_thread
--let $aborter_thread = `SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE = 'wsrep aborter idle' LIMIT 1`
--error ER_KILL_DENIED_ERROR,ER_KILL_DENIED_ERROR --error ER_KILL_DENIED_ERROR,ER_KILL_DENIED_ERROR
--eval KILL $aborter_thread --eval KILL $aborter_thread
--error ER_KILL_DENIED_ERROR,ER_KILL_DENIED_ERROR --error ER_KILL_DENIED_ERROR,ER_KILL_DENIED_ERROR
--eval KILL QUERY $aborter_thread --eval KILL QUERY $aborter_thread
--enable_query_log --enable_query_log
#
# SUPER can kill applier threads
#
--connection node_2
--let $applier_thread = `SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE != 'wsrep aborter idle' OR STATE IS NULL LIMIT 1`
--disable_query_log
--eval KILL $applier_thread
--enable_query_log
--let $aborter_thread = `SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE = 'wsrep aborter idle' LIMIT 1`
--disable_query_log
--eval KILL $aborter_thread
--enable_query_log
--source include/restart_mysqld.inc
--connection node_2
--let $applier_thread = `SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE != 'wsrep aborter idle' OR STATE IS NULL LIMIT 1`
--disable_query_log
--eval KILL QUERY $applier_thread
--enable_query_log
--let $aborter_thread = `SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST WHERE USER = 'system user' AND STATE = 'wsrep aborter idle' LIMIT 1`
--disable_query_log
--eval KILL QUERY $aborter_thread
--enable_query_log
--source include/restart_mysqld.inc
--connection node_1
--disconnect foo_node_1
DROP USER foo@localhost;

View File

@ -8292,19 +8292,11 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ
It's ok to also kill DELAYED threads with KILL_CONNECTION instead of It's ok to also kill DELAYED threads with KILL_CONNECTION instead of
KILL_SYSTEM_THREAD; The difference is that KILL_CONNECTION may be KILL_SYSTEM_THREAD; The difference is that KILL_CONNECTION may be
faster and do a harder kill than KILL_SYSTEM_THREAD; faster and do a harder kill than KILL_SYSTEM_THREAD;
Note that if thread is wsrep Brute Force or applier thread we
allow killing it only when we're SUPER.
*/ */
if ((thd->security_ctx->master_access & SUPER_ACL) || if (((thd->security_ctx->master_access & SUPER_ACL) ||
(thd->security_ctx->user_matches(tmp->security_ctx) thd->security_ctx->user_matches(tmp->security_ctx)) &&
#ifdef WITH_WSREP !wsrep_thd_is_BF(tmp, false))
&&
!tmp->wsrep_applier &&
!wsrep_thd_is_BF(tmp, false)
#endif
))
{ {
tmp->awake(kill_signal); tmp->awake(kill_signal);
error=0; error=0;

View File

@ -181,8 +181,7 @@ static struct wsrep_service_st wsrep_handler = {
wsrep_trx_is_aborting, wsrep_trx_is_aborting,
wsrep_trx_order_before, wsrep_trx_order_before,
wsrep_unlock_rollback, wsrep_unlock_rollback,
wsrep_set_data_home_dir, wsrep_set_data_home_dir
wsrep_thd_is_applier,
}; };
static struct thd_specifics_service_st thd_specifics_handler= static struct thd_specifics_service_st thd_specifics_handler=

View File

@ -20,9 +20,6 @@
my_bool wsrep_thd_is_BF(THD *, my_bool) my_bool wsrep_thd_is_BF(THD *, my_bool)
{ return 0; } { return 0; }
my_bool wsrep_thd_is_applier(THD *)
{ return 0; }
int wsrep_trx_order_before(THD *, THD *) int wsrep_trx_order_before(THD *, THD *)
{ return 0; } { return 0; }

View File

@ -2470,6 +2470,7 @@ extern "C" void wsrep_thd_set_exec_mode(THD *thd, enum wsrep_exec_mode mode)
thd->wsrep_exec_mode= mode; thd->wsrep_exec_mode= mode;
} }
extern "C" void wsrep_thd_set_query_state( extern "C" void wsrep_thd_set_query_state(
THD *thd, enum wsrep_query_state state) THD *thd, enum wsrep_query_state state)
{ {

View File

@ -596,15 +596,6 @@ my_bool wsrep_thd_is_BF(THD *thd, my_bool sync)
return status; return status;
} }
my_bool wsrep_thd_is_applier(THD *thd)
{
my_bool ret = FALSE;
if (thd) {
ret = thd->wsrep_applier;
}
return ret;
}
extern "C" extern "C"
my_bool wsrep_thd_is_BF_or_commit(void *thd_ptr, my_bool sync) my_bool wsrep_thd_is_BF_or_commit(void *thd_ptr, my_bool sync)
{ {

View File

@ -37,7 +37,6 @@ int wsrep_abort_thd(void *bf_thd_ptr, void *victim_thd_ptr,
*/ */
extern void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe); extern void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe);
extern my_bool wsrep_thd_is_BF(THD *thd, my_bool sync); extern my_bool wsrep_thd_is_BF(THD *thd, my_bool sync);
extern my_bool wsrep_thd_is_applier(THD *thd);
extern my_bool wsrep_thd_is_wsrep(void *thd_ptr); extern my_bool wsrep_thd_is_wsrep(void *thd_ptr);
enum wsrep_conflict_state wsrep_thd_conflict_state(void *thd_ptr, my_bool sync); enum wsrep_conflict_state wsrep_thd_conflict_state(void *thd_ptr, my_bool sync);
@ -48,7 +47,6 @@ extern "C" int wsrep_thd_in_locking_session(void *thd_ptr);
#else /* WITH_WSREP */ #else /* WITH_WSREP */
#define wsrep_thd_is_BF(T, S) (0) #define wsrep_thd_is_BF(T, S) (0)
#define wsrep_thd_is_applier(T) (0)
#define wsrep_abort_thd(X,Y,Z) do { } while(0) #define wsrep_abort_thd(X,Y,Z) do { } while(0)
#define wsrep_create_appliers(T) do { } while(0) #define wsrep_create_appliers(T) do { } while(0)

View File

@ -4929,6 +4929,8 @@ static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels)
/* if victim has been signaled by BF thread and/or aborting /* if victim has been signaled by BF thread and/or aborting
is already progressing, following query aborting is not necessary is already progressing, following query aborting is not necessary
any more. any more.
Also, BF thread should own trx mutex for the victim, which would
conflict with trx_mutex_enter() below
*/ */
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
@ -4937,8 +4939,34 @@ static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels)
if (trx_t* trx = thd_to_trx(thd)) { if (trx_t* trx = thd_to_trx(thd)) {
ut_ad(trx->mysql_thd == thd); ut_ad(trx->mysql_thd == thd);
switch (trx->abort_type) {
#ifdef WITH_WSREP
case TRX_WSREP_ABORT:
break;
#endif
case TRX_SERVER_ABORT:
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
lock_mutex_enter();
}
/* fall through */
case TRX_REPLICATION_ABORT:
trx_mutex_enter(trx);
}
/* Cancel a pending lock request if there are any */ /* Cancel a pending lock request if there are any */
lock_trx_handle_wait(trx); lock_trx_handle_wait(trx);
switch (trx->abort_type) {
#ifdef WITH_WSREP
case TRX_WSREP_ABORT:
break;
#endif
case TRX_SERVER_ABORT:
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
lock_mutex_exit();
}
/* fall through */
case TRX_REPLICATION_ABORT:
trx_mutex_exit(trx);
}
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
@ -18655,12 +18683,6 @@ wsrep_innobase_kill_one_trx(
wsrep_thd_ws_handle(thd)->trx_id); wsrep_thd_ws_handle(thd)->trx_id);
wsrep_thd_LOCK(thd); wsrep_thd_LOCK(thd);
/* We mark this as victim transaction, which is already marked
as BF victim. Both trx mutex and lock_sys mutex is held until
this victim has aborted. */
victim_trx->victim = true;
DBUG_EXECUTE_IF("sync.wsrep_after_BF_victim_lock", DBUG_EXECUTE_IF("sync.wsrep_after_BF_victim_lock",
{ {
const char act[]= const char act[]=
@ -18856,10 +18878,12 @@ wsrep_abort_transaction(
if (victim_trx) { if (victim_trx) {
lock_mutex_enter(); lock_mutex_enter();
trx_mutex_enter(victim_trx); trx_mutex_enter(victim_trx);
victim_trx->abort_type = TRX_WSREP_ABORT;
int rcode = wsrep_innobase_kill_one_trx(bf_thd, bf_trx, int rcode = wsrep_innobase_kill_one_trx(bf_thd, bf_trx,
victim_trx, signal); victim_trx, signal);
trx_mutex_exit(victim_trx); trx_mutex_exit(victim_trx);
lock_mutex_exit(); lock_mutex_exit();
victim_trx->abort_type = TRX_SERVER_ABORT;
wsrep_srv_conc_cancel_wait(victim_trx); wsrep_srv_conc_cancel_wait(victim_trx);
DBUG_RETURN(rcode); DBUG_RETURN(rcode);
} else { } else {

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2015, 2019, MariaDB Corporation. Copyright (c) 2015, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program 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 the terms of the GNU General Public License as published by the Free Software
@ -623,6 +623,7 @@ struct trx_lock_t {
lock_sys->mutex. Otherwise, this may lock_sys->mutex. Otherwise, this may
only be modified by the thread that is only be modified by the thread that is
serving the running transaction. */ serving the running transaction. */
mem_heap_t* lock_heap; /*!< memory heap for trx_locks; mem_heap_t* lock_heap; /*!< memory heap for trx_locks;
protected by lock_sys->mutex */ protected by lock_sys->mutex */
@ -694,6 +695,14 @@ lock_rec_convert_impl_to_expl()) will access transactions associated
to other connections. The locks of transactions are protected by to other connections. The locks of transactions are protected by
lock_sys->mutex and sometimes by trx->mutex. */ lock_sys->mutex and sometimes by trx->mutex. */
enum trx_abort_t {
TRX_SERVER_ABORT = 0,
#ifdef WITH_WSREP
TRX_WSREP_ABORT,
#endif
TRX_REPLICATION_ABORT
};
struct trx_t{ struct trx_t{
ulint magic_n; ulint magic_n;
@ -871,12 +880,8 @@ struct trx_t{
/*------------------------------*/ /*------------------------------*/
THD* mysql_thd; /*!< MySQL thread handle corresponding THD* mysql_thd; /*!< MySQL thread handle corresponding
to this trx, or NULL */ to this trx, or NULL */
bool victim; /*!< This transaction is trx_abort_t abort_type; /*!< Transaction abort type*/
selected as victim for abort
either by replication or
high priority wsrep thread. This
field is protected by trx and
lock sys mutex. */
const char* mysql_log_file_name; const char* mysql_log_file_name;
/*!< if MySQL binlog is used, this field /*!< if MySQL binlog is used, this field
contains a pointer to the latest file contains a pointer to the latest file

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2014, 2019, MariaDB Corporation. Copyright (c) 2014, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program 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 the terms of the GNU General Public License as published by the Free Software
@ -1793,8 +1793,10 @@ wsrep_kill_victim(
} }
} }
lock->trx->abort_type = TRX_WSREP_ABORT;
wsrep_innobase_kill_one_trx(trx->mysql_thd, wsrep_innobase_kill_one_trx(trx->mysql_thd,
(const trx_t*) trx, lock->trx, TRUE); (const trx_t*) trx, lock->trx, TRUE);
lock->trx->abort_type = TRX_SERVER_ABORT;
} }
} }
} }
@ -4780,11 +4782,12 @@ lock_report_waiters_to_mysql(
if (w_trx->id != victim_trx_id) { if (w_trx->id != victim_trx_id) {
/* If thd_report_wait_for() decides to kill the /* If thd_report_wait_for() decides to kill the
transaction, then we will get a call back into transaction, then we will get a call back into
innobase_kill_query.*/ innobase_kill_query. We mark this by setting
trx_mutex_enter(w_trx); current_lock_mutex_owner, so we can avoid trying
w_trx->victim = true; to recursively take lock_sys->mutex. */
w_trx->abort_type = TRX_REPLICATION_ABORT;
thd_report_wait_for(mysql_thd, w_trx->mysql_thd); thd_report_wait_for(mysql_thd, w_trx->mysql_thd);
trx_mutex_exit(w_trx); w_trx->abort_type = TRX_SERVER_ABORT;
} }
++i; ++i;
} }
@ -7964,7 +7967,16 @@ lock_trx_release_locks(
lock_mutex_exit(); lock_mutex_exit();
} }
inline dberr_t lock_trx_handle_wait_low(trx_t* trx) /*********************************************************************//**
Check whether the transaction has already been rolled back because it
was selected as a deadlock victim, or if it has to wait then cancel
the wait lock.
@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
UNIV_INTERN
dberr_t
lock_trx_handle_wait(
/*=================*/
trx_t* trx) /*!< in/out: trx lock state */
{ {
ut_ad(lock_mutex_own()); ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(trx)); ut_ad(trx_mutex_own(trx));
@ -7981,32 +7993,6 @@ inline dberr_t lock_trx_handle_wait_low(trx_t* trx)
return DB_LOCK_WAIT; return DB_LOCK_WAIT;
} }
/*********************************************************************//**
Check whether the transaction has already been rolled back because it
was selected as a deadlock victim, or if it has to wait then cancel
the wait lock.
@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
UNIV_INTERN
dberr_t
lock_trx_handle_wait(
/*=================*/
trx_t* trx) /*!< in/out: trx lock state */
{
if (!trx->victim) {
lock_mutex_enter();
trx_mutex_enter(trx);
}
dberr_t err = lock_trx_handle_wait_low(trx);
if (!trx->victim) {
lock_mutex_exit();
trx_mutex_exit(trx);
}
return err;
}
/*********************************************************************//** /*********************************************************************//**
Get the number of locks on a table. Get the number of locks on a table.
@return number of locks */ @return number of locks */

View File

@ -4746,7 +4746,11 @@ no_gap_lock:
a deadlock and the transaction had to wait then a deadlock and the transaction had to wait then
release the lock it is waiting on. */ release the lock it is waiting on. */
lock_mutex_enter();
trx_mutex_enter(trx);
err = lock_trx_handle_wait(trx); err = lock_trx_handle_wait(trx);
lock_mutex_exit();
trx_mutex_exit(trx);
switch (err) { switch (err) {
case DB_SUCCESS: case DB_SUCCESS:

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2016, 2019, MariaDB Corporation. Copyright (c) 2016, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program 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 the terms of the GNU General Public License as published by the Free Software
@ -370,7 +370,6 @@ trx_rollback_to_savepoint_for_mysql_low(
trx_mark_sql_stat_end(trx); trx_mark_sql_stat_end(trx);
trx->op_info = ""; trx->op_info = "";
trx->victim = false;
return(err); return(err);
} }

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2015, 2019, MariaDB Corporation. Copyright (c) 2015, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program 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 the terms of the GNU General Public License as published by the Free Software
@ -1339,7 +1339,11 @@ trx_commit_in_memory(
ut_ad(!trx->in_ro_trx_list); ut_ad(!trx->in_ro_trx_list);
ut_ad(!trx->in_rw_trx_list); ut_ad(!trx->in_rw_trx_list);
trx->victim = false; #ifdef WITH_WSREP
if (trx->mysql_thd && wsrep_on(trx->mysql_thd)) {
trx->lock.was_chosen_as_deadlock_victim = FALSE;
}
#endif
trx->dict_operation = TRX_DICT_OP_NONE; trx->dict_operation = TRX_DICT_OP_NONE;
trx->error_state = DB_SUCCESS; trx->error_state = DB_SUCCESS;

View File

@ -5534,6 +5534,8 @@ static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels)
/* if victim has been signaled by BF thread and/or aborting /* if victim has been signaled by BF thread and/or aborting
is already progressing, following query aborting is not necessary is already progressing, following query aborting is not necessary
any more. any more.
Also, BF thread should own trx mutex for the victim, which would
conflict with trx_mutex_enter() below
*/ */
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
@ -5541,8 +5543,34 @@ static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels)
if (trx_t* trx = thd_to_trx(thd)) { if (trx_t* trx = thd_to_trx(thd)) {
ut_ad(trx->mysql_thd == thd); ut_ad(trx->mysql_thd == thd);
switch (trx->abort_type) {
#ifdef WITH_WSREP
case TRX_WSREP_ABORT:
break;
#endif
case TRX_SERVER_ABORT:
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
lock_mutex_enter();
}
/* fall through */
case TRX_REPLICATION_ABORT:
trx_mutex_enter(trx);
}
/* Cancel a pending lock request if there are any */ /* Cancel a pending lock request if there are any */
lock_trx_handle_wait(trx); lock_trx_handle_wait(trx);
switch (trx->abort_type) {
#ifdef WITH_WSREP
case TRX_WSREP_ABORT:
break;
#endif
case TRX_SERVER_ABORT:
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
lock_mutex_exit();
}
/* fall through */
case TRX_REPLICATION_ABORT:
trx_mutex_exit(trx);
}
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
@ -19695,12 +19723,6 @@ wsrep_innobase_kill_one_trx(
(thd && wsrep_thd_query(thd)) ? wsrep_thd_query(thd) : "void"); (thd && wsrep_thd_query(thd)) ? wsrep_thd_query(thd) : "void");
wsrep_thd_LOCK(thd); wsrep_thd_LOCK(thd);
/* We mark this as victim transaction, which is already marked
as BF victim. Both trx mutex and lock_sys mutex is held until
this victim has aborted. */
victim_trx->victim = true;
DBUG_EXECUTE_IF("sync.wsrep_after_BF_victim_lock", DBUG_EXECUTE_IF("sync.wsrep_after_BF_victim_lock",
{ {
const char act[]= const char act[]=
@ -19889,10 +19911,12 @@ wsrep_abort_transaction(handlerton* hton, THD *bf_thd, THD *victim_thd,
if (victim_trx) { if (victim_trx) {
lock_mutex_enter(); lock_mutex_enter();
trx_mutex_enter(victim_trx); trx_mutex_enter(victim_trx);
victim_trx->abort_type = TRX_WSREP_ABORT;
int rcode = wsrep_innobase_kill_one_trx(bf_thd, bf_trx, int rcode = wsrep_innobase_kill_one_trx(bf_thd, bf_trx,
victim_trx, signal); victim_trx, signal);
trx_mutex_exit(victim_trx); trx_mutex_exit(victim_trx);
lock_mutex_exit(); lock_mutex_exit();
victim_trx->abort_type = TRX_SERVER_ABORT;
wsrep_srv_conc_cancel_wait(victim_trx); wsrep_srv_conc_cancel_wait(victim_trx);
DBUG_RETURN(rcode); DBUG_RETURN(rcode);
} else { } else {

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2015, 2019, MariaDB Corporation. Copyright (c) 2015, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program 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 the terms of the GNU General Public License as published by the Free Software
@ -672,6 +672,7 @@ struct trx_lock_t {
lock_sys->mutex. Otherwise, this may lock_sys->mutex. Otherwise, this may
only be modified by the thread that is only be modified by the thread that is
serving the running transaction. */ serving the running transaction. */
mem_heap_t* lock_heap; /*!< memory heap for trx_locks; mem_heap_t* lock_heap; /*!< memory heap for trx_locks;
protected by lock_sys->mutex */ protected by lock_sys->mutex */
@ -743,6 +744,14 @@ lock_rec_convert_impl_to_expl()) will access transactions associated
to other connections. The locks of transactions are protected by to other connections. The locks of transactions are protected by
lock_sys->mutex and sometimes by trx->mutex. */ lock_sys->mutex and sometimes by trx->mutex. */
enum trx_abort_t {
TRX_SERVER_ABORT = 0,
#ifdef WITH_WSREP
TRX_WSREP_ABORT,
#endif
TRX_REPLICATION_ABORT
};
struct trx_t{ struct trx_t{
ulint magic_n; ulint magic_n;
@ -921,12 +930,8 @@ struct trx_t{
/*------------------------------*/ /*------------------------------*/
THD* mysql_thd; /*!< MySQL thread handle corresponding THD* mysql_thd; /*!< MySQL thread handle corresponding
to this trx, or NULL */ to this trx, or NULL */
bool victim; /*!< This transaction is trx_abort_t abort_type; /*!< Transaction abort type */
selected as victim for abort
either by replication or
high priority wsrep thread. This
field is protected by trx and
lock sys mutex. */
const char* mysql_log_file_name; const char* mysql_log_file_name;
/*!< if MySQL binlog is used, this field /*!< if MySQL binlog is used, this field
contains a pointer to the latest file contains a pointer to the latest file

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2014, 2019, MariaDB Corporation. Copyright (c) 2014, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program 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 the terms of the GNU General Public License as published by the Free Software
@ -1804,8 +1804,10 @@ wsrep_kill_victim(
} }
} }
lock->trx->abort_type = TRX_WSREP_ABORT;
wsrep_innobase_kill_one_trx(trx->mysql_thd, wsrep_innobase_kill_one_trx(trx->mysql_thd,
(const trx_t*) trx, lock->trx, TRUE); (const trx_t*) trx, lock->trx, TRUE);
lock->trx->abort_type = TRX_SERVER_ABORT;
} }
} }
} }
@ -4819,11 +4821,12 @@ lock_report_waiters_to_mysql(
if (w_trx->id != victim_trx_id) { if (w_trx->id != victim_trx_id) {
/* If thd_report_wait_for() decides to kill the /* If thd_report_wait_for() decides to kill the
transaction, then we will get a call back into transaction, then we will get a call back into
innobase_kill_query.*/ innobase_kill_query. We mark this by setting
trx_mutex_enter(w_trx); current_lock_mutex_owner, so we can avoid trying
w_trx->victim = true; to recursively take lock_sys->mutex. */
w_trx->abort_type = TRX_REPLICATION_ABORT;
thd_report_wait_for(mysql_thd, w_trx->mysql_thd); thd_report_wait_for(mysql_thd, w_trx->mysql_thd);
trx_mutex_exit(w_trx); w_trx->abort_type = TRX_SERVER_ABORT;
} }
++i; ++i;
} }
@ -8074,7 +8077,16 @@ lock_trx_release_locks(
lock_mutex_exit(); lock_mutex_exit();
} }
inline dberr_t lock_trx_handle_wait_low(trx_t* trx) /*********************************************************************//**
Check whether the transaction has already been rolled back because it
was selected as a deadlock victim, or if it has to wait then cancel
the wait lock.
@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
UNIV_INTERN
dberr_t
lock_trx_handle_wait(
/*=================*/
trx_t* trx) /*!< in/out: trx lock state */
{ {
ut_ad(lock_mutex_own()); ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(trx)); ut_ad(trx_mutex_own(trx));
@ -8091,32 +8103,6 @@ inline dberr_t lock_trx_handle_wait_low(trx_t* trx)
return DB_LOCK_WAIT; return DB_LOCK_WAIT;
} }
/*********************************************************************//**
Check whether the transaction has already been rolled back because it
was selected as a deadlock victim, or if it has to wait then cancel
the wait lock.
@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
UNIV_INTERN
dberr_t
lock_trx_handle_wait(
/*=================*/
trx_t* trx) /*!< in/out: trx lock state */
{
if (!trx->victim) {
lock_mutex_enter();
trx_mutex_enter(trx);
}
dberr_t err = lock_trx_handle_wait_low(trx);
if (!trx->victim) {
lock_mutex_exit();
trx_mutex_exit(trx);
}
return err;
}
/*********************************************************************//** /*********************************************************************//**
Get the number of locks on a table. Get the number of locks on a table.
@return number of locks */ @return number of locks */

View File

@ -4755,7 +4755,11 @@ no_gap_lock:
a deadlock and the transaction had to wait then a deadlock and the transaction had to wait then
release the lock it is waiting on. */ release the lock it is waiting on. */
lock_mutex_enter();
trx_mutex_enter(trx);
err = lock_trx_handle_wait(trx); err = lock_trx_handle_wait(trx);
lock_mutex_exit();
trx_mutex_exit(trx);
switch (err) { switch (err) {
case DB_SUCCESS: case DB_SUCCESS:

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2016, 2019, MariaDB Corporation. Copyright (c) 2016, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program 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 the terms of the GNU General Public License as published by the Free Software
@ -33,6 +33,8 @@ Created 3/26/1996 Heikki Tuuri
#include "trx0roll.ic" #include "trx0roll.ic"
#endif #endif
#include <mysql/service_wsrep.h>
#include "fsp0fsp.h" #include "fsp0fsp.h"
#include "mach0data.h" #include "mach0data.h"
#include "trx0rseg.h" #include "trx0rseg.h"
@ -49,6 +51,9 @@ Created 3/26/1996 Heikki Tuuri
#include "pars0pars.h" #include "pars0pars.h"
#include "srv0mon.h" #include "srv0mon.h"
#include "trx0sys.h" #include "trx0sys.h"
#ifdef WITH_WSREP
#include "ha_prototypes.h"
#endif /* WITH_WSREP */
/** This many pages must be undone before a truncate is tried within /** This many pages must be undone before a truncate is tried within
rollback */ rollback */
@ -370,7 +375,13 @@ trx_rollback_to_savepoint_for_mysql_low(
trx_mark_sql_stat_end(trx); trx_mark_sql_stat_end(trx);
trx->op_info = ""; trx->op_info = "";
trx->victim = false;
#ifdef WITH_WSREP
if (wsrep_on(trx->mysql_thd) &&
trx->lock.was_chosen_as_deadlock_victim) {
trx->lock.was_chosen_as_deadlock_victim = FALSE;
}
#endif
return(err); return(err);
} }
@ -1068,6 +1079,12 @@ trx_roll_try_truncate(
if (trx->update_undo) { if (trx->update_undo) {
trx_undo_truncate_end(trx, trx->update_undo, limit); trx_undo_truncate_end(trx, trx->update_undo, limit);
} }
#ifdef WITH_WSREP_OUT
if (wsrep_on(trx->mysql_thd)) {
trx->lock.was_chosen_as_deadlock_victim = FALSE;
}
#endif /* WITH_WSREP */
} }
/***********************************************************************//** /***********************************************************************//**

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2015, 2019, MariaDB Corporation. Copyright (c) 2015, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program 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 the terms of the GNU General Public License as published by the Free Software
@ -1563,7 +1563,11 @@ trx_commit_in_memory(
ut_ad(!trx->in_ro_trx_list); ut_ad(!trx->in_ro_trx_list);
ut_ad(!trx->in_rw_trx_list); ut_ad(!trx->in_rw_trx_list);
trx->victim = false; #ifdef WITH_WSREP
if (trx->mysql_thd && wsrep_on(trx->mysql_thd)) {
trx->lock.was_chosen_as_deadlock_victim = FALSE;
}
#endif
trx->dict_operation = TRX_DICT_OP_NONE; trx->dict_operation = TRX_DICT_OP_NONE;
trx->error_state = DB_SUCCESS; trx->error_state = DB_SUCCESS;
@ -2664,6 +2668,10 @@ trx_start_if_not_started_low(
{ {
switch (trx->state) { switch (trx->state) {
case TRX_STATE_NOT_STARTED: case TRX_STATE_NOT_STARTED:
#ifdef WITH_WSREP
ut_d(trx->start_file = __FILE__);
ut_d(trx->start_line = __LINE__);
#endif /* WITH_WSREP */
trx_start_low(trx); trx_start_low(trx);
/* fall through */ /* fall through */
case TRX_STATE_ACTIVE: case TRX_STATE_ACTIVE:
@ -2697,6 +2705,11 @@ trx_start_for_ddl_low(
trx->will_lock = 1; trx->will_lock = 1;
trx->ddl = true; trx->ddl = true;
#ifdef WITH_WSREP
ut_d(trx->start_file = __FILE__);
ut_d(trx->start_line = __LINE__);
#endif /* WITH_WSREP */
trx_start_low(trx); trx_start_low(trx);
return; return;