-
-
Notifications
You must be signed in to change notification settings - Fork 2k
MDEV-16335: Include deadlock detail information in SHOW WARNINGS #4492
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c899cd9
77d377f
c6ed814
58acf24
bc5cf24
f76cb09
b94f90f
d7d77d2
62a330c
374a9d7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| call mtr.add_suppression("InnoDB: Transaction was aborted due to "); | ||
| call mtr.add_suppression("Transactions deadlock detected"); | ||
| SET @save = @@GLOBAL.innodb_print_all_deadlocks; | ||
| SET GLOBAL innodb_print_all_deadlocks = ON; | ||
| CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB; | ||
| CREATE TABLE t2 (a INT) ENGINE=InnoDB; | ||
| INSERT INTO t1 VALUES (1), (2); | ||
| INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); | ||
| connect con1, localhost, root,,; | ||
| connection default; | ||
| BEGIN; | ||
| UPDATE t2 SET a = a + 1; | ||
| SELECT * FROM t1 WHERE id = 2 FOR UPDATE; | ||
| id | ||
| 2 | ||
| connection con1; | ||
| BEGIN; | ||
| SELECT * FROM t1 WHERE id = 1 FOR UPDATE; | ||
| id | ||
| 1 | ||
| connection default; | ||
| SELECT * FROM t1 WHERE id = 1 FOR UPDATE; | ||
| connection con1; | ||
| SELECT * FROM t1 WHERE id = 2 FOR UPDATE; | ||
| ERROR 40001: Deadlock found when trying to get lock; try restarting transaction | ||
| SHOW WARNINGS; | ||
| Level Code Message | ||
| Note 1213 TIMESTAMP 0xTHD | ||
| *** (1) TRANSACTION: | ||
| TRANSACTION TRX_ID, ACTIVE N sec starting index read | ||
| mysql tables in use 1, locked 1 | ||
| LOCK WAIT N lock struct(s), heap size N, N row lock(s) | ||
| MariaDB thread id TID, OS thread handle TID, query id QID localhost root Statistics | ||
| SELECT * FROM t1 WHERE id = 2 FOR UPDATE | ||
| *** WAITING FOR THIS LOCK TO BE GRANTED: | ||
| RECORD LOCKS space id ID page no N n bits N index PRIMARY of table `test`.`t1` trx id TRX_ID lock_mode X locks rec but not gap waiting | ||
| Record lock, heap no 3 PHYSICAL RECORD: n_fields N; compact format; info bits N | ||
| 0: len 4; hex HEX; asc DATA;; | ||
|
|
||
| *** WE ROLL BACK TRANSACTION (2) | ||
| Error 1213 Deadlock found when trying to get lock; try restarting transaction | ||
| connection default; | ||
| id | ||
| 1 | ||
| COMMIT; | ||
| disconnect con1; | ||
| DROP TABLE t1, t2; | ||
| SET GLOBAL innodb_print_all_deadlocks = @save; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| --source include/have_innodb.inc | ||
|
|
||
| # MDEV-34686: Deadlock info available in session warnings | ||
| # Verify deadlock info appears in SHOW WARNINGS when innodb_print_all_deadlocks=ON | ||
|
|
||
| call mtr.add_suppression("InnoDB: Transaction was aborted due to "); | ||
| call mtr.add_suppression("Transactions deadlock detected"); | ||
|
|
||
| SET @save = @@GLOBAL.innodb_print_all_deadlocks; | ||
| SET GLOBAL innodb_print_all_deadlocks = ON; | ||
|
|
||
| CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB; | ||
| CREATE TABLE t2 (a INT) ENGINE=InnoDB; | ||
| INSERT INTO t1 VALUES (1), (2); | ||
| # 10 rows: UPDATE creates ~20 weight (undo + locks) vs con1's ~3 weight | ||
| INSERT INTO t2 VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10); | ||
|
|
||
| --connect(con1, localhost, root,,) | ||
|
|
||
| # default: increase weight so con1 will be chosen as deadlock victim | ||
| --connection default | ||
| BEGIN; | ||
| UPDATE t2 SET a = a + 1; | ||
| SELECT * FROM t1 WHERE id = 2 FOR UPDATE; | ||
|
|
||
| # con1: minimal weight | ||
| --connection con1 | ||
| BEGIN; | ||
| SELECT * FROM t1 WHERE id = 1 FOR UPDATE; | ||
|
|
||
| # default waits for row 1 | ||
| --connection default | ||
| --send SELECT * FROM t1 WHERE id = 1 FOR UPDATE | ||
|
|
||
| # Wait for default to enter lock wait | ||
| --connection con1 | ||
| let $wait_condition= | ||
| SELECT COUNT(*) >= 2 FROM information_schema.innodb_locks; | ||
| --source include/wait_condition.inc | ||
|
|
||
| # con1 triggers deadlock - will be victim due to lower weight | ||
| --error ER_LOCK_DEADLOCK | ||
| SELECT * FROM t1 WHERE id = 2 FOR UPDATE; | ||
|
|
||
| # Deadlock info must appear as Note | ||
| --replace_regex /[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/TIMESTAMP/ /0x[0-9a-f]+/0xTHD/ /OS thread handle [0-9]+/OS thread handle TID/ /::1 // /127\.0\.0\.1 // /localhost::[0-9]* // /thread id [0-9]+/thread id TID/ /query id [0-9]+/query id QID/ /trx id [0-9]+/trx id TRX_ID/ /TRANSACTION [0-9]+/TRANSACTION TRX_ID/ /space id [0-9]+/space id ID/ /page no [0-9]+/page no N/ /n bits [0-9]+/n bits N/ /n_fields [0-9]+/n_fields N/ /info bits [0-9]+/info bits N/ /ACTIVE [0-9]+ sec/ACTIVE N sec/ /heap size [0-9]+/heap size N/ /[0-9]+ lock struct\(s\)/N lock struct(s)/ /[0-9]+ row lock\(s\)/N row lock(s)/ /undo log entries [0-9]+/undo log entries N/ /hex [0-9a-f]+; asc .*;;/hex HEX; asc DATA;;/ | ||
| SHOW WARNINGS; | ||
|
|
||
| --connection default | ||
| --reap | ||
| COMMIT; | ||
|
|
||
| --disconnect con1 | ||
| DROP TABLE t1, t2; | ||
| SET GLOBAL innodb_print_all_deadlocks = @save; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7166,6 +7166,7 @@ and less modified rows. Bit 0 is used to prefer orig_trx in case of a tie. | |
| static const char rollback_msg[]= "*** WE ROLL BACK TRANSACTION (%u)\n"; | ||
| char buf[9 + sizeof rollback_msg]; | ||
| trx_t *victim= nullptr; | ||
| char *deadlock_info= nullptr; | ||
|
|
||
| /* Here, lock elision does not make sense, because | ||
| for the output we are going to invoke system calls, | ||
|
|
@@ -7298,11 +7299,39 @@ and less modified rows. Bit 0 is used to prefer orig_trx in case of a tie. | |
| victim->lock.was_chosen_as_deadlock_victim= true; | ||
| DEBUG_SYNC_C("deadlock_report_before_lock_releasing"); | ||
| lock_cancel_waiting_and_release<true>(victim->lock.wait_lock); | ||
|
|
||
| if (!srv_print_all_deadlocks || !current_trx || victim != trx || | ||
| !trx->mysql_thd) | ||
| goto func_exit; | ||
| fflush(lock_latest_err_file); | ||
| long len= ftell(lock_latest_err_file); | ||
dr-m marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (len <= 0) | ||
| goto func_exit; | ||
| deadlock_info= static_cast<char*>(ut_malloc_nokey( | ||
| static_cast<size_t>(len) + 1)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The constructor-style cast |
||
| if (!deadlock_info) | ||
| goto func_exit; | ||
dr-m marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| rewind(lock_latest_err_file); | ||
| size_t deadlock_info_len= fread(deadlock_info, 1, | ||
| static_cast<size_t>(len), | ||
| lock_latest_err_file); | ||
| ut_ad(deadlock_info_len <= static_cast<size_t>(len)); | ||
| if (deadlock_info_len && deadlock_info[deadlock_info_len - 1] == '\n') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one is still not resolved:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where would it crash? The dereferencing |
||
| --deadlock_info_len; | ||
| deadlock_info[deadlock_info_len]= '\0'; | ||
| } | ||
|
|
||
| func_exit: | ||
| if (current_trx) | ||
| lock_sys.wr_unlock(); | ||
|
|
||
| if (deadlock_info) | ||
| { | ||
| push_warning(trx->mysql_thd, Sql_condition::WARN_LEVEL_NOTE, | ||
| ER_LOCK_DEADLOCK, deadlock_info); | ||
| ut_free(deadlock_info); | ||
| } | ||
|
|
||
| return victim; | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cheapest condition should be checked first. Here, we are checking the most expensive condition (reading a global variable) first. Please move
!srv_print_all_deadlockslast. (Yes, this contradicts the advice to check the lowest-probability condition first; we know that by default this code path is disabled.)