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
ENUM_VALUE_LIST NULL
READ_ONLY YES
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
SESSION_VALUE NULL
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,
"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);
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,
PLUGIN_VAR_RQCMDARG,
"Percentage of B-tree page filled during bulk insert",
......@@ -21954,6 +21961,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(lock_schedule_algorithm),
MYSQL_SYSVAR(locks_unsafe_for_binlog),
MYSQL_SYSVAR(lock_wait_timeout),
MYSQL_SYSVAR(deadlock_detect),
MYSQL_SYSVAR(page_size),
MYSQL_SYSVAR(log_buffer_size),
MYSQL_SYSVAR(log_file_size),
......
/*****************************************************************************
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
the terms of the GNU General Public License as published by the Free Software
......@@ -55,6 +55,9 @@ extern ulong innodb_lock_schedule_algorithm;
// Forward declaration
class ReadView;
/** The value of innodb_deadlock_detect */
extern my_bool innobase_deadlock_detect;
/*********************************************************************//**
Gets the size of a lock struct.
@return size in bytes */
......
......@@ -56,6 +56,9 @@ Created 5/7/1996 Heikki Tuuri
/** Lock scheduling algorithm */
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 */
static const ulint REC_LOCK_CACHE = 8;
......@@ -124,7 +127,7 @@ class DeadlockChecker {
@return id of transaction chosen as victim or 0 */
static const trx_t* check_and_resolve(
const lock_t* lock,
const trx_t* trx);
trx_t* trx);
private:
/** Do a shallow copy. Default destructor OK.
......@@ -2136,25 +2139,8 @@ RecLock::deadlock_check(lock_t* lock)
ut_ad(lock->trx == m_trx);
ut_ad(trx_mutex_own(m_trx));
bool async_rollback = m_trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC;
/* 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);
const trx_t* victim_trx =
DeadlockChecker::check_and_resolve(lock, m_trx);
/* Check the outcome of the deadlock test. It is possible that
the transaction that blocked our lock was rolled back and we
......@@ -4653,25 +4639,8 @@ lock_table_enqueue_waiting(
/* Enqueue the lock request that will wait to be granted */
lock = lock_table_create(c_lock, table, mode | LOCK_WAIT, trx);
bool async_rollback = trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC;
/* 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 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);
const trx_t* victim_trx =
DeadlockChecker::check_and_resolve(lock, trx);
if (victim_trx != 0) {
ut_ad(victim_trx == trx);
......@@ -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 was chosen as a victim and rolled back or no deadlock found.
@param lock lock the transaction is requesting
@param trx transaction requesting the lock
@param[in] lock lock the transaction is requesting
@param[in,out] trx transaction requesting the lock
@return transaction instanace chosen as victim or 0 */
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(trx_mutex_own(trx));
check_trx_state(trx);
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;
THD* start_mysql_thd;
bool report_waits = false;
......@@ -8491,7 +8480,7 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, const trx_t* trx)
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);
......@@ -8512,6 +8501,8 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, const trx_t* trx)
lock_deadlock_found = true;
}
trx_mutex_enter(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