summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaohua Wang <shaohua.wang@oracle.com>2016-07-28 13:08:52 +0800
committerMarko Mäkelä <marko.makela@mariadb.com>2017-04-24 15:09:18 +0300
commitd3a2f60e1a80a70f38ac8e86f297169bad812415 (patch)
tree8c1e938e4dedaebb341c6b268d3f9e3ff30bbd51
parent1a5ca702b1301b12bf97636bd8d168120a188681 (diff)
downloadmariadb-git-d3a2f60e1a80a70f38ac8e86f297169bad812415.tar.gz
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.
-rw-r--r--mysql-test/suite/innodb/r/deadlock_detect.result30
-rw-r--r--mysql-test/suite/innodb/t/deadlock_detect.test55
-rw-r--r--mysql-test/suite/sys_vars/r/innodb_deadlock_detect_basic.result56
-rw-r--r--mysql-test/suite/sys_vars/r/sysvars_innodb.result14
-rw-r--r--mysql-test/suite/sys_vars/t/innodb_deadlock_detect_basic.test53
-rw-r--r--storage/innobase/handler/ha_innodb.cc8
-rw-r--r--storage/innobase/include/lock0lock.h5
-rw-r--r--storage/innobase/lock/lock0lock.cc77
8 files changed, 254 insertions, 44 deletions
diff --git a/mysql-test/suite/innodb/r/deadlock_detect.result b/mysql-test/suite/innodb/r/deadlock_detect.result
new file mode 100644
index 00000000000..c3e3794ed21
--- /dev/null
+++ b/mysql-test/suite/innodb/r/deadlock_detect.result
@@ -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;
diff --git a/mysql-test/suite/innodb/t/deadlock_detect.test b/mysql-test/suite/innodb/t/deadlock_detect.test
new file mode 100644
index 00000000000..85d8c1f67f2
--- /dev/null
+++ b/mysql-test/suite/innodb/t/deadlock_detect.test
@@ -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;
diff --git a/mysql-test/suite/sys_vars/r/innodb_deadlock_detect_basic.result b/mysql-test/suite/sys_vars/r/innodb_deadlock_detect_basic.result
new file mode 100644
index 00000000000..615fc782ad5
--- /dev/null
+++ b/mysql-test/suite/sys_vars/r/innodb_deadlock_detect_basic.result
@@ -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
diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result
index d7d35da8459..3b62fef94de 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result
+++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result
@@ -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
diff --git a/mysql-test/suite/sys_vars/t/innodb_deadlock_detect_basic.test b/mysql-test/suite/sys_vars/t/innodb_deadlock_detect_basic.test
new file mode 100644
index 00000000000..8cac13906c8
--- /dev/null
+++ b/mysql-test/suite/sys_vars/t/innodb_deadlock_detect_basic.test
@@ -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;
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 44c4beb2445..5d8ff4ff493 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -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),
diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h
index ca6a5286cde..e718189062d 100644
--- a/storage/innobase/include/lock0lock.h
+++ b/storage/innobase/include/lock0lock.h
@@ -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 */
diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc
index 1068adc6462..800868bef94 100644
--- a/storage/innobase/lock/lock0lock.cc
+++ b/storage/innobase/lock/lock0lock.cc
@@ -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);
}