diff options
author | Shaohua Wang <shaohua.wang@oracle.com> | 2016-07-28 13:08:52 +0800 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2017-04-24 15:09:18 +0300 |
commit | d3a2f60e1a80a70f38ac8e86f297169bad812415 (patch) | |
tree | 8c1e938e4dedaebb341c6b268d3f9e3ff30bbd51 | |
parent | 1a5ca702b1301b12bf97636bd8d168120a188681 (diff) | |
download | mariadb-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.result | 30 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/deadlock_detect.test | 55 | ||||
-rw-r--r-- | mysql-test/suite/sys_vars/r/innodb_deadlock_detect_basic.result | 56 | ||||
-rw-r--r-- | mysql-test/suite/sys_vars/r/sysvars_innodb.result | 14 | ||||
-rw-r--r-- | mysql-test/suite/sys_vars/t/innodb_deadlock_detect_basic.test | 53 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 8 | ||||
-rw-r--r-- | storage/innobase/include/lock0lock.h | 5 | ||||
-rw-r--r-- | storage/innobase/lock/lock0lock.cc | 77 |
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); } |