Skip to content

Commit d3a2f60

Browse files
Shaohua Wangdr-m
authored andcommitted
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.
1 parent 1a5ca70 commit d3a2f60

File tree

8 files changed

+254
-44
lines changed

8 files changed

+254
-44
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
SET GLOBAL innodb_deadlock_detect=OFF;
2+
SET GLOBAL innodb_lock_wait_timeout=2;
3+
connection default;
4+
CREATE TABLE t1(
5+
id INT,
6+
PRIMARY KEY(id)
7+
) ENGINE=InnoDB;
8+
INSERT INTO t1 VALUES(1), (2), (3);
9+
BEGIN;
10+
SELECT * FROM t1 WHERE id = 1 FOR UPDATE;
11+
id
12+
1
13+
connect con1,localhost,root,,;
14+
BEGIN;
15+
SELECT * FROM t1 WHERE id = 2 FOR UPDATE;
16+
id
17+
2
18+
SELECT * FROM t1 WHERE id = 1 FOR UPDATE;
19+
connection default;
20+
SELECT * FROM t1 WHERE id = 2 FOR UPDATE;
21+
connection con1;
22+
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
23+
ROLLBACK;
24+
connection default;
25+
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
26+
ROLLBACK;
27+
DROP TABLE t1;
28+
disconnect con1;
29+
SET GLOBAL innodb_lock_wait_timeout=default;
30+
SET GLOBAL innodb_deadlock_detect=default;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#
2+
# wl#9383 INNODB: ADD AN OPTION TO TURN OFF/ON DEADLOCK CHECKER
3+
#
4+
5+
--source include/have_innodb.inc
6+
--source include/not_embedded.inc
7+
--source include/count_sessions.inc
8+
9+
SET GLOBAL innodb_deadlock_detect=OFF;
10+
SET GLOBAL innodb_lock_wait_timeout=2;
11+
12+
connection default;
13+
14+
CREATE TABLE t1(
15+
id INT,
16+
PRIMARY KEY(id)
17+
) ENGINE=InnoDB;
18+
19+
INSERT INTO t1 VALUES(1), (2), (3);
20+
21+
BEGIN;
22+
23+
SELECT * FROM t1 WHERE id = 1 FOR UPDATE;
24+
25+
connect (con1,localhost,root,,);
26+
27+
BEGIN;
28+
29+
SELECT * FROM t1 WHERE id = 2 FOR UPDATE;
30+
31+
send SELECT * FROM t1 WHERE id = 1 FOR UPDATE;
32+
33+
connection default;
34+
send SELECT * FROM t1 WHERE id = 2 FOR UPDATE;
35+
36+
connection con1;
37+
--error ER_LOCK_WAIT_TIMEOUT
38+
reap;
39+
40+
ROLLBACK;
41+
42+
connection default;
43+
--error ER_LOCK_WAIT_TIMEOUT
44+
reap;
45+
46+
ROLLBACK;
47+
48+
DROP TABLE t1;
49+
50+
disconnect con1;
51+
52+
--source include/wait_until_count_sessions.inc
53+
54+
SET GLOBAL innodb_lock_wait_timeout=default;
55+
SET GLOBAL innodb_deadlock_detect=default;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
SET @start_global_value = @@global.innodb_deadlock_detect;
2+
SELECT @start_global_value;
3+
@start_global_value
4+
1
5+
Valid values are 'ON' and 'OFF'
6+
select @@global.innodb_deadlock_detect in (0, 1);
7+
@@global.innodb_deadlock_detect in (0, 1)
8+
1
9+
select @@global.innodb_deadlock_detect;
10+
@@global.innodb_deadlock_detect
11+
1
12+
select @@session.innodb_deadlock_detect in (0, 1);
13+
ERROR HY000: Variable 'innodb_deadlock_detect' is a GLOBAL variable
14+
select @@session.innodb_deadlock_detect;
15+
ERROR HY000: Variable 'innodb_deadlock_detect' is a GLOBAL variable
16+
show global variables like 'innodb_deadlock_detect';
17+
Variable_name Value
18+
innodb_deadlock_detect ON
19+
show session variables like 'innodb_deadlock_detect';
20+
Variable_name Value
21+
innodb_deadlock_detect ON
22+
set global innodb_deadlock_detect='OFF';
23+
set session innodb_deadlock_detect='OFF';
24+
ERROR HY000: Variable 'innodb_deadlock_detect' is a GLOBAL variable and should be set with SET GLOBAL
25+
select @@global.innodb_deadlock_detect;
26+
@@global.innodb_deadlock_detect
27+
0
28+
set @@global.innodb_deadlock_detect=1;
29+
select @@global.innodb_deadlock_detect;
30+
@@global.innodb_deadlock_detect
31+
1
32+
set global innodb_deadlock_detect=0;
33+
select @@global.innodb_deadlock_detect;
34+
@@global.innodb_deadlock_detect
35+
0
36+
set @@global.innodb_deadlock_detect='ON';
37+
select @@global.innodb_deadlock_detect;
38+
@@global.innodb_deadlock_detect
39+
1
40+
set global innodb_deadlock_detect=1.1;
41+
ERROR 42000: Incorrect argument type to variable 'innodb_deadlock_detect'
42+
set global innodb_deadlock_detect=1e1;
43+
ERROR 42000: Incorrect argument type to variable 'innodb_deadlock_detect'
44+
set global innodb_deadlock_detect=2;
45+
ERROR 42000: Variable 'innodb_deadlock_detect' can't be set to the value of '2'
46+
set global innodb_deadlock_detect='AUTO';
47+
ERROR 42000: Variable 'innodb_deadlock_detect' can't be set to the value of 'AUTO'
48+
set global innodb_deadlock_detect=-3;
49+
ERROR 42000: Variable 'innodb_deadlock_detect' can't be set to the value of '-3'
50+
select @@global.innodb_deadlock_detect;
51+
@@global.innodb_deadlock_detect
52+
1
53+
SET @@global.innodb_deadlock_detect = @start_global_value;
54+
SELECT @@global.innodb_deadlock_detect;
55+
@@global.innodb_deadlock_detect
56+
1

mysql-test/suite/sys_vars/r/sysvars_innodb.result

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,20 @@ NUMERIC_BLOCK_SIZE NULL
580580
ENUM_VALUE_LIST NULL
581581
READ_ONLY YES
582582
COMMAND_LINE_ARGUMENT REQUIRED
583+
VARIABLE_NAME INNODB_DEADLOCK_DETECT
584+
SESSION_VALUE NULL
585+
GLOBAL_VALUE ON
586+
GLOBAL_VALUE_ORIGIN COMPILE-TIME
587+
DEFAULT_VALUE ON
588+
VARIABLE_SCOPE GLOBAL
589+
VARIABLE_TYPE BOOLEAN
590+
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.
591+
NUMERIC_MIN_VALUE NULL
592+
NUMERIC_MAX_VALUE NULL
593+
NUMERIC_BLOCK_SIZE NULL
594+
ENUM_VALUE_LIST OFF,ON
595+
READ_ONLY NO
596+
COMMAND_LINE_ARGUMENT NONE
583597
VARIABLE_NAME INNODB_DEBUG_FORCE_SCRUBBING
584598
SESSION_VALUE NULL
585599
GLOBAL_VALUE OFF
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
--source include/have_innodb.inc
2+
3+
SET @start_global_value = @@global.innodb_deadlock_detect;
4+
SELECT @start_global_value;
5+
6+
#
7+
# exists as global
8+
#
9+
--echo Valid values are 'ON' and 'OFF'
10+
select @@global.innodb_deadlock_detect in (0, 1);
11+
select @@global.innodb_deadlock_detect;
12+
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
13+
select @@session.innodb_deadlock_detect in (0, 1);
14+
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
15+
select @@session.innodb_deadlock_detect;
16+
show global variables like 'innodb_deadlock_detect';
17+
show session variables like 'innodb_deadlock_detect';
18+
19+
#
20+
# show that it's writable
21+
#
22+
set global innodb_deadlock_detect='OFF';
23+
--error ER_GLOBAL_VARIABLE
24+
set session innodb_deadlock_detect='OFF';
25+
select @@global.innodb_deadlock_detect;
26+
set @@global.innodb_deadlock_detect=1;
27+
select @@global.innodb_deadlock_detect;
28+
set global innodb_deadlock_detect=0;
29+
select @@global.innodb_deadlock_detect;
30+
set @@global.innodb_deadlock_detect='ON';
31+
select @@global.innodb_deadlock_detect;
32+
33+
#
34+
# incorrect types
35+
#
36+
--error ER_WRONG_TYPE_FOR_VAR
37+
set global innodb_deadlock_detect=1.1;
38+
--error ER_WRONG_TYPE_FOR_VAR
39+
set global innodb_deadlock_detect=1e1;
40+
--error ER_WRONG_VALUE_FOR_VAR
41+
set global innodb_deadlock_detect=2;
42+
--error ER_WRONG_VALUE_FOR_VAR
43+
set global innodb_deadlock_detect='AUTO';
44+
--error ER_WRONG_VALUE_FOR_VAR
45+
set global innodb_deadlock_detect=-3;
46+
select @@global.innodb_deadlock_detect;
47+
48+
#
49+
# Cleanup
50+
#
51+
52+
SET @@global.innodb_deadlock_detect = @start_global_value;
53+
SELECT @@global.innodb_deadlock_detect;

storage/innobase/handler/ha_innodb.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21246,6 +21246,13 @@ static MYSQL_SYSVAR_ULONG(concurrency_tickets, srv_n_free_tickets_to_enter,
2124621246
"Number of times a thread is allowed to enter InnoDB within the same SQL query after it has once got the ticket",
2124721247
NULL, NULL, 5000L, 1L, ~0UL, 0);
2124821248

21249+
static MYSQL_SYSVAR_BOOL(deadlock_detect, innobase_deadlock_detect,
21250+
PLUGIN_VAR_NOCMDARG,
21251+
"Enable/disable InnoDB deadlock detector (default ON)."
21252+
" if set to OFF, deadlock detection is skipped,"
21253+
" and we rely on innodb_lock_wait_timeout in case of deadlock.",
21254+
NULL, NULL, TRUE);
21255+
2124921256
static MYSQL_SYSVAR_LONG(fill_factor, innobase_fill_factor,
2125021257
PLUGIN_VAR_RQCMDARG,
2125121258
"Percentage of B-tree page filled during bulk insert",
@@ -21954,6 +21961,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
2195421961
MYSQL_SYSVAR(lock_schedule_algorithm),
2195521962
MYSQL_SYSVAR(locks_unsafe_for_binlog),
2195621963
MYSQL_SYSVAR(lock_wait_timeout),
21964+
MYSQL_SYSVAR(deadlock_detect),
2195721965
MYSQL_SYSVAR(page_size),
2195821966
MYSQL_SYSVAR(log_buffer_size),
2195921967
MYSQL_SYSVAR(log_file_size),

storage/innobase/include/lock0lock.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*****************************************************************************
22
33
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
4-
Copyright (c) 2017, MariaDB Corporation. All Rights Reserved.
4+
Copyright (c) 2017, MariaDB Corporation.
55
66
This program is free software; you can redistribute it and/or modify it under
77
the terms of the GNU General Public License as published by the Free Software
@@ -55,6 +55,9 @@ extern ulong innodb_lock_schedule_algorithm;
5555
// Forward declaration
5656
class ReadView;
5757

58+
/** The value of innodb_deadlock_detect */
59+
extern my_bool innobase_deadlock_detect;
60+
5861
/*********************************************************************//**
5962
Gets the size of a lock struct.
6063
@return size in bytes */

storage/innobase/lock/lock0lock.cc

Lines changed: 34 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ Created 5/7/1996 Heikki Tuuri
5656
/** Lock scheduling algorithm */
5757
ulong innodb_lock_schedule_algorithm = INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS;
5858

59+
/** The value of innodb_deadlock_detect */
60+
my_bool innobase_deadlock_detect;
61+
5962
/** Total number of cached record locks */
6063
static const ulint REC_LOCK_CACHE = 8;
6164

@@ -124,7 +127,7 @@ class DeadlockChecker {
124127
@return id of transaction chosen as victim or 0 */
125128
static const trx_t* check_and_resolve(
126129
const lock_t* lock,
127-
const trx_t* trx);
130+
trx_t*trx);
128131

129132
private:
130133
/** Do a shallow copy. Default destructor OK.
@@ -2136,25 +2139,8 @@ RecLock::deadlock_check(lock_t* lock)
21362139
ut_ad(lock->trx == m_trx);
21372140
ut_ad(trx_mutex_own(m_trx));
21382141

2139-
boolasync_rollback = m_trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC;
2140-
2141-
/* This is safe, because DeadlockChecker::check_and_resolve()
2142-
is invoked when a lock wait is enqueued for the currently
2143-
running transaction. Because m_trx is a running transaction
2144-
(it is not currently suspended because of a lock wait),
2145-
its state can only be changed by this thread, which is
2146-
currently associated with the transaction. */
2147-
2148-
trx_mutex_exit(m_trx);
2149-
2150-
/* If transaction is marked for ASYNC rollback then we should
2151-
not allow it to wait for another lock causing possible deadlock.
2152-
We return current transaction as deadlock victim here. */
2153-
2154-
const trx_t* victim_trx = async_rollback ? m_trx
2155-
: DeadlockChecker::check_and_resolve(lock, m_trx);
2156-
2157-
trx_mutex_enter(m_trx);
2142+
const trx_t* victim_trx =
2143+
DeadlockChecker::check_and_resolve(lock, m_trx);
21582144

21592145
/* Check the outcome of the deadlock test. It is possible that
21602146
the transaction that blocked our lock was rolled back and we
@@ -4653,25 +4639,8 @@ lock_table_enqueue_waiting(
46534639
/* Enqueue the lock request that will wait to be granted */
46544640
lock = lock_table_create(c_lock, table, mode | LOCK_WAIT, trx);
46554641

4656-
boolasync_rollback = trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC;
4657-
/* Release the mutex to obey the latching order.
4658-
This is safe, because DeadlockChecker::check_and_resolve()
4659-
is invoked when a lock wait is enqueued for the currently
4660-
running transaction. Because trx is a running transaction
4661-
(it is not currently suspended because of a lock wait),
4662-
its state can only be changed by this thread, which is
4663-
currently associated with the transaction. */
4664-
4665-
trx_mutex_exit(trx);
4666-
4667-
/* If transaction is marked for ASYNC rollback then we should
4668-
not allow it to wait for another lock causing possible deadlock.
4669-
We return current transaction as deadlock victim here. */
4670-
4671-
const trx_t* victim_trx = async_rollback ? trx
4672-
: DeadlockChecker::check_and_resolve(lock, trx);
4673-
4674-
trx_mutex_enter(trx);
4642+
const trx_t* victim_trx =
4643+
DeadlockChecker::check_and_resolve(lock, trx);
46754644

46764645
if (victim_trx != 0) {
46774646
ut_ad(victim_trx == trx);
@@ -8441,17 +8410,37 @@ and rolling it back. It will attempt to resolve all deadlocks. The returned
84418410
transaction id will be the joining transaction instance or NULL if some other
84428411
transaction was chosen as a victim and rolled back or no deadlock found.
84438412
8444-
@param lock lock the transaction is requesting
8445-
@param trx transaction requesting the lock
8413+
@param[in]lock lock the transaction is requesting
8414+
@param[in,out]trx transaction requesting the lock
84468415
84478416
@return transaction instanace chosen as victim or 0 */
84488417
const trx_t*
8449-
DeadlockChecker::check_and_resolve(const lock_t* lock, const trx_t* trx)
8418+
DeadlockChecker::check_and_resolve(const lock_t* lock, trx_t* trx)
84508419
{
84518420
ut_ad(lock_mutex_own());
8421+
ut_ad(trx_mutex_own(trx));
84528422
check_trx_state(trx);
84538423
ut_ad(!srv_read_only_mode);
84548424

8425+
/* If transaction is marked for ASYNC rollback then we should
8426+
not allow it to wait for another lock causing possible deadlock.
8427+
We return current transaction as deadlock victim here. */
8428+
if (trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC) {
8429+
return(trx);
8430+
} else if (!innobase_deadlock_detect) {
8431+
return(NULL);
8432+
}
8433+
8434+
/* Release the mutex to obey the latching order.
8435+
This is safe, because DeadlockChecker::check_and_resolve()
8436+
is invoked when a lock wait is enqueued for the currently
8437+
running transaction. Because m_trx is a running transaction
8438+
(it is not currently suspended because of a lock wait),
8439+
its state can only be changed by this thread, which is
8440+
currently associated with the transaction. */
8441+
8442+
trx_mutex_exit(trx);
8443+
84558444
const trx_t* victim_trx;
84568445
THD* start_mysql_thd;
84578446
bool report_waits = false;
@@ -8491,7 +8480,7 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, const trx_t* trx)
84918480

84928481
break;
84938482

8494-
} else if (victim_trx != 0 && victim_trx != trx) {
8483+
} else if (victim_trx != NULL && victim_trx != trx) {
84958484

84968485
ut_ad(victim_trx == checker.m_wait_lock->trx);
84978486

@@ -8512,6 +8501,8 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, const trx_t* trx)
85128501
lock_deadlock_found = true;
85138502
}
85148503

8504+
trx_mutex_enter(trx);
8505+
85158506
return(victim_trx);
85168507
}
85178508

0 commit comments

Comments
 (0)