mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
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.
This commit is contained in:
committed by
Marko Mäkelä
parent
1a5ca702b1
commit
d3a2f60e1a
30
mysql-test/suite/innodb/r/deadlock_detect.result
Normal file
30
mysql-test/suite/innodb/r/deadlock_detect.result
Normal file
@ -0,0 +1,30 @@
|
||||
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;
|
55
mysql-test/suite/innodb/t/deadlock_detect.test
Normal file
55
mysql-test/suite/innodb/t/deadlock_detect.test
Normal file
@ -0,0 +1,55 @@
|
||||
#
|
||||
# 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;
|
@ -0,0 +1,56 @@
|
||||
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
|
||||
|
@ -0,0 +1,53 @@
|
||||
--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),
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*****************************************************************************
|
||||
|
||||
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 @@ public:
|
||||
@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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user