Commit d3a2f60e authored by Shaohua Wang's avatar Shaohua Wang Committed by Marko Mäkelä

BUG#23477773 OPTION TO TURN OFF/ON DEADLOCK CHECKER

Backport WL#9383 INNODB: ADD AN OPTION TO TURN OFF/ON DEADLOCK CHECKER
(rb#12873) to 5.7.
parent 1a5ca702
SET GLOBAL innodb_deadlock_detect=OFF;
SET GLOBAL innodb_lock_wait_timeout=2;
connection default;
CREATE TABLE t1(
id INT,
PRIMARY KEY(id)
) ENGINE=InnoDB;
INSERT INTO t1 VALUES(1), (2), (3);
BEGIN;
SELECT * FROM t1 WHERE id = 1 FOR UPDATE;
id
1
connect con1,localhost,root,,;
BEGIN;
SELECT * FROM t1 WHERE id = 2 FOR UPDATE;
id
2
SELECT * FROM t1 WHERE id = 1 FOR UPDATE;
connection default;
SELECT * FROM t1 WHERE id = 2 FOR UPDATE;
connection con1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
ROLLBACK;
connection default;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
ROLLBACK;
DROP TABLE t1;
disconnect con1;
SET GLOBAL innodb_lock_wait_timeout=default;
SET GLOBAL innodb_deadlock_detect=default;
#
# wl#9383 INNODB: ADD AN OPTION TO TURN OFF/ON DEADLOCK CHECKER
#
--source include/have_innodb.inc
--source include/not_embedded.inc
--source include/count_sessions.inc
SET GLOBAL innodb_deadlock_detect=OFF;
SET GLOBAL innodb_lock_wait_timeout=2;
connection default;
CREATE TABLE t1(
id INT,
PRIMARY KEY(id)
) ENGINE=InnoDB;
INSERT INTO t1 VALUES(1), (2), (3);
BEGIN;
SELECT * FROM t1 WHERE id = 1 FOR UPDATE;
connect (con1,localhost,root,,);
BEGIN;
SELECT * FROM t1 WHERE id = 2 FOR UPDATE;
send SELECT * FROM t1 WHERE id = 1 FOR UPDATE;
connection default;
send SELECT * FROM t1 WHERE id = 2 FOR UPDATE;
connection con1;
--error ER_LOCK_WAIT_TIMEOUT
reap;
ROLLBACK;
connection default;
--error ER_LOCK_WAIT_TIMEOUT
reap;
ROLLBACK;
DROP TABLE t1;
disconnect con1;
--source include/wait_until_count_sessions.inc
SET GLOBAL innodb_lock_wait_timeout=default;
SET GLOBAL innodb_deadlock_detect=default;
SET @start_global_value = @@global.innodb_deadlock_detect;
SELECT @start_global_value;
@start_global_value
1
Valid values are 'ON' and 'OFF'
select @@global.innodb_deadlock_detect in (0, 1);
@@global.innodb_deadlock_detect in (0, 1)
1
select @@global.innodb_deadlock_detect;
@@global.innodb_deadlock_detect
1
select @@session.innodb_deadlock_detect in (0, 1);
ERROR HY000: Variable 'innodb_deadlock_detect' is a GLOBAL variable
select @@session.innodb_deadlock_detect;
ERROR HY000: Variable 'innodb_deadlock_detect' is a GLOBAL variable
show global variables like 'innodb_deadlock_detect';
Variable_name Value
innodb_deadlock_detect ON
show session variables like 'innodb_deadlock_detect';
Variable_name Value
innodb_deadlock_detect ON
set global innodb_deadlock_detect='OFF';
set session innodb_deadlock_detect='OFF';
ERROR HY000: Variable 'innodb_deadlock_detect' is a GLOBAL variable and should be set with SET GLOBAL
select @@global.innodb_deadlock_detect;
@@global.innodb_deadlock_detect
0
set @@global.innodb_deadlock_detect=1;
select @@global.innodb_deadlock_detect;
@@global.innodb_deadlock_detect
1
set global innodb_deadlock_detect=0;
select @@global.innodb_deadlock_detect;
@@global.innodb_deadlock_detect
0
set @@global.innodb_deadlock_detect='ON';
select @@global.innodb_deadlock_detect;
@@global.innodb_deadlock_detect
1
set global innodb_deadlock_detect=1.1;
ERROR 42000: Incorrect argument type to variable 'innodb_deadlock_detect'
set global innodb_deadlock_detect=1e1;
ERROR 42000: Incorrect argument type to variable 'innodb_deadlock_detect'
set global innodb_deadlock_detect=2;
ERROR 42000: Variable 'innodb_deadlock_detect' can't be set to the value of '2'
set global innodb_deadlock_detect='AUTO';
ERROR 42000: Variable 'innodb_deadlock_detect' can't be set to the value of 'AUTO'
set global innodb_deadlock_detect=-3;
ERROR 42000: Variable 'innodb_deadlock_detect' can't be set to the value of '-3'
select @@global.innodb_deadlock_detect;
@@global.innodb_deadlock_detect
1
SET @@global.innodb_deadlock_detect = @start_global_value;
SELECT @@global.innodb_deadlock_detect;
@@global.innodb_deadlock_detect
1
...@@ -580,6 +580,20 @@ NUMERIC_BLOCK_SIZE NULL ...@@ -580,6 +580,20 @@ NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST NULL ENUM_VALUE_LIST NULL
READ_ONLY YES READ_ONLY YES
COMMAND_LINE_ARGUMENT REQUIRED COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME INNODB_DEADLOCK_DETECT
SESSION_VALUE NULL
GLOBAL_VALUE ON
GLOBAL_VALUE_ORIGIN COMPILE-TIME
DEFAULT_VALUE ON
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BOOLEAN
VARIABLE_COMMENT Enable/disable InnoDB deadlock detector (default ON). if set to OFF, deadlock detection is skipped, and we rely on innodb_lock_wait_timeout in case of deadlock.
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST OFF,ON
READ_ONLY NO
COMMAND_LINE_ARGUMENT NONE
VARIABLE_NAME INNODB_DEBUG_FORCE_SCRUBBING VARIABLE_NAME INNODB_DEBUG_FORCE_SCRUBBING
SESSION_VALUE NULL SESSION_VALUE NULL
GLOBAL_VALUE OFF GLOBAL_VALUE OFF
......
--source include/have_innodb.inc
SET @start_global_value = @@global.innodb_deadlock_detect;
SELECT @start_global_value;
#
# exists as global
#
--echo Valid values are 'ON' and 'OFF'
select @@global.innodb_deadlock_detect in (0, 1);
select @@global.innodb_deadlock_detect;
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
select @@session.innodb_deadlock_detect in (0, 1);
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
select @@session.innodb_deadlock_detect;
show global variables like 'innodb_deadlock_detect';
show session variables like 'innodb_deadlock_detect';
#
# show that it's writable
#
set global innodb_deadlock_detect='OFF';
--error ER_GLOBAL_VARIABLE
set session innodb_deadlock_detect='OFF';
select @@global.innodb_deadlock_detect;
set @@global.innodb_deadlock_detect=1;
select @@global.innodb_deadlock_detect;
set global innodb_deadlock_detect=0;
select @@global.innodb_deadlock_detect;
set @@global.innodb_deadlock_detect='ON';
select @@global.innodb_deadlock_detect;
#
# incorrect types
#
--error ER_WRONG_TYPE_FOR_VAR
set global innodb_deadlock_detect=1.1;
--error ER_WRONG_TYPE_FOR_VAR
set global innodb_deadlock_detect=1e1;
--error ER_WRONG_VALUE_FOR_VAR
set global innodb_deadlock_detect=2;
--error ER_WRONG_VALUE_FOR_VAR
set global innodb_deadlock_detect='AUTO';
--error ER_WRONG_VALUE_FOR_VAR
set global innodb_deadlock_detect=-3;
select @@global.innodb_deadlock_detect;
#
# Cleanup
#
SET @@global.innodb_deadlock_detect = @start_global_value;
SELECT @@global.innodb_deadlock_detect;
...@@ -21246,6 +21246,13 @@ static MYSQL_SYSVAR_ULONG(concurrency_tickets, srv_n_free_tickets_to_enter, ...@@ -21246,6 +21246,13 @@ static MYSQL_SYSVAR_ULONG(concurrency_tickets, srv_n_free_tickets_to_enter,
"Number of times a thread is allowed to enter InnoDB within the same SQL query after it has once got the ticket", "Number of times a thread is allowed to enter InnoDB within the same SQL query after it has once got the ticket",
NULL, NULL, 5000L, 1L, ~0UL, 0); NULL, NULL, 5000L, 1L, ~0UL, 0);
static MYSQL_SYSVAR_BOOL(deadlock_detect, innobase_deadlock_detect,
PLUGIN_VAR_NOCMDARG,
"Enable/disable InnoDB deadlock detector (default ON)."
" if set to OFF, deadlock detection is skipped,"
" and we rely on innodb_lock_wait_timeout in case of deadlock.",
NULL, NULL, TRUE);
static MYSQL_SYSVAR_LONG(fill_factor, innobase_fill_factor, static MYSQL_SYSVAR_LONG(fill_factor, innobase_fill_factor,
PLUGIN_VAR_RQCMDARG, PLUGIN_VAR_RQCMDARG,
"Percentage of B-tree page filled during bulk insert", "Percentage of B-tree page filled during bulk insert",
...@@ -21954,6 +21961,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { ...@@ -21954,6 +21961,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(lock_schedule_algorithm), MYSQL_SYSVAR(lock_schedule_algorithm),
MYSQL_SYSVAR(locks_unsafe_for_binlog), MYSQL_SYSVAR(locks_unsafe_for_binlog),
MYSQL_SYSVAR(lock_wait_timeout), MYSQL_SYSVAR(lock_wait_timeout),
MYSQL_SYSVAR(deadlock_detect),
MYSQL_SYSVAR(page_size), MYSQL_SYSVAR(page_size),
MYSQL_SYSVAR(log_buffer_size), MYSQL_SYSVAR(log_buffer_size),
MYSQL_SYSVAR(log_file_size), MYSQL_SYSVAR(log_file_size),
......
/***************************************************************************** /*****************************************************************************
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) 2017, MariaDB Corporation. All Rights Reserved. Copyright (c) 2017, 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
...@@ -55,6 +55,9 @@ extern ulong innodb_lock_schedule_algorithm; ...@@ -55,6 +55,9 @@ extern ulong innodb_lock_schedule_algorithm;
// Forward declaration // Forward declaration
class ReadView; class ReadView;
/** The value of innodb_deadlock_detect */
extern my_bool innobase_deadlock_detect;
/*********************************************************************//** /*********************************************************************//**
Gets the size of a lock struct. Gets the size of a lock struct.
@return size in bytes */ @return size in bytes */
......
...@@ -56,6 +56,9 @@ Created 5/7/1996 Heikki Tuuri ...@@ -56,6 +56,9 @@ Created 5/7/1996 Heikki Tuuri
/** Lock scheduling algorithm */ /** Lock scheduling algorithm */
ulong innodb_lock_schedule_algorithm = INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS; ulong innodb_lock_schedule_algorithm = INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS;
/** The value of innodb_deadlock_detect */
my_bool innobase_deadlock_detect;
/** Total number of cached record locks */ /** Total number of cached record locks */
static const ulint REC_LOCK_CACHE = 8; static const ulint REC_LOCK_CACHE = 8;
...@@ -124,7 +127,7 @@ class DeadlockChecker { ...@@ -124,7 +127,7 @@ class DeadlockChecker {
@return id of transaction chosen as victim or 0 */ @return id of transaction chosen as victim or 0 */
static const trx_t* check_and_resolve( static const trx_t* check_and_resolve(
const lock_t* lock, const lock_t* lock,
const trx_t* trx); trx_t* trx);
private: private:
/** Do a shallow copy. Default destructor OK. /** Do a shallow copy. Default destructor OK.
...@@ -2136,25 +2139,8 @@ RecLock::deadlock_check(lock_t* lock) ...@@ -2136,25 +2139,8 @@ RecLock::deadlock_check(lock_t* lock)
ut_ad(lock->trx == m_trx); ut_ad(lock->trx == m_trx);
ut_ad(trx_mutex_own(m_trx)); ut_ad(trx_mutex_own(m_trx));
bool async_rollback = m_trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC; const trx_t* victim_trx =
DeadlockChecker::check_and_resolve(lock, m_trx);
/* This is safe, because DeadlockChecker::check_and_resolve()
is invoked when a lock wait is enqueued for the currently
running transaction. Because m_trx is a running transaction
(it is not currently suspended because of a lock wait),
its state can only be changed by this thread, which is
currently associated with the transaction. */
trx_mutex_exit(m_trx);
/* If transaction is marked for ASYNC rollback then we should
not allow it to wait for another lock causing possible deadlock.
We return current transaction as deadlock victim here. */
const trx_t* victim_trx = async_rollback ? m_trx
: DeadlockChecker::check_and_resolve(lock, m_trx);
trx_mutex_enter(m_trx);
/* Check the outcome of the deadlock test. It is possible that /* Check the outcome of the deadlock test. It is possible that
the transaction that blocked our lock was rolled back and we the transaction that blocked our lock was rolled back and we
...@@ -4653,25 +4639,8 @@ lock_table_enqueue_waiting( ...@@ -4653,25 +4639,8 @@ lock_table_enqueue_waiting(
/* Enqueue the lock request that will wait to be granted */ /* Enqueue the lock request that will wait to be granted */
lock = lock_table_create(c_lock, table, mode | LOCK_WAIT, trx); lock = lock_table_create(c_lock, table, mode | LOCK_WAIT, trx);
bool async_rollback = trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC; const trx_t* victim_trx =
/* Release the mutex to obey the latching order. DeadlockChecker::check_and_resolve(lock, trx);
This is safe, because DeadlockChecker::check_and_resolve()
is invoked when a lock wait is enqueued for the currently
running transaction. Because trx is a running transaction
(it is not currently suspended because of a lock wait),
its state can only be changed by this thread, which is
currently associated with the transaction. */
trx_mutex_exit(trx);
/* If transaction is marked for ASYNC rollback then we should
not allow it to wait for another lock causing possible deadlock.
We return current transaction as deadlock victim here. */
const trx_t* victim_trx = async_rollback ? trx
: DeadlockChecker::check_and_resolve(lock, trx);
trx_mutex_enter(trx);
if (victim_trx != 0) { if (victim_trx != 0) {
ut_ad(victim_trx == trx); ut_ad(victim_trx == trx);
...@@ -8441,17 +8410,37 @@ and rolling it back. It will attempt to resolve all deadlocks. The returned ...@@ -8441,17 +8410,37 @@ and rolling it back. It will attempt to resolve all deadlocks. The returned
transaction id will be the joining transaction instance or NULL if some other transaction id will be the joining transaction instance or NULL if some other
transaction was chosen as a victim and rolled back or no deadlock found. transaction was chosen as a victim and rolled back or no deadlock found.
@param lock lock the transaction is requesting @param[in] lock lock the transaction is requesting
@param trx transaction requesting the lock @param[in,out] trx transaction requesting the lock
@return transaction instanace chosen as victim or 0 */ @return transaction instanace chosen as victim or 0 */
const trx_t* const trx_t*
DeadlockChecker::check_and_resolve(const lock_t* lock, const trx_t* trx) DeadlockChecker::check_and_resolve(const lock_t* lock, trx_t* trx)
{ {
ut_ad(lock_mutex_own()); ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(trx));
check_trx_state(trx); check_trx_state(trx);
ut_ad(!srv_read_only_mode); ut_ad(!srv_read_only_mode);
/* If transaction is marked for ASYNC rollback then we should
not allow it to wait for another lock causing possible deadlock.
We return current transaction as deadlock victim here. */
if (trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC) {
return(trx);
} else if (!innobase_deadlock_detect) {
return(NULL);
}
/* Release the mutex to obey the latching order.
This is safe, because DeadlockChecker::check_and_resolve()
is invoked when a lock wait is enqueued for the currently
running transaction. Because m_trx is a running transaction
(it is not currently suspended because of a lock wait),
its state can only be changed by this thread, which is
currently associated with the transaction. */
trx_mutex_exit(trx);
const trx_t* victim_trx; const trx_t* victim_trx;
THD* start_mysql_thd; THD* start_mysql_thd;
bool report_waits = false; bool report_waits = false;
...@@ -8491,7 +8480,7 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, const trx_t* trx) ...@@ -8491,7 +8480,7 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, const trx_t* trx)
break; break;
} else if (victim_trx != 0 && victim_trx != trx) { } else if (victim_trx != NULL && victim_trx != trx) {
ut_ad(victim_trx == checker.m_wait_lock->trx); ut_ad(victim_trx == checker.m_wait_lock->trx);
...@@ -8512,6 +8501,8 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, const trx_t* trx) ...@@ -8512,6 +8501,8 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, const trx_t* trx)
lock_deadlock_found = true; lock_deadlock_found = true;
} }
trx_mutex_enter(trx);
return(victim_trx); return(victim_trx);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment