-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
77ce06a
19592c6
0cf376f
948f9a8
1eadd6b
8d4c835
2b12fdc
b940c6c
4f63d11
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,37 @@ | ||
| 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,,; | ||
| connect con2, localhost, root,,; | ||
| connection con2; | ||
| 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 con2; | ||
| SELECT * FROM t1 WHERE id = 1 FOR UPDATE; | ||
| connection default; | ||
| connection con1; | ||
| SELECT * FROM t1 WHERE id = 2 FOR UPDATE; | ||
| ERROR 40001: Deadlock found when trying to get lock; try restarting transaction | ||
| connection con2; | ||
| id | ||
| 1 | ||
| COMMIT; | ||
| disconnect con1; | ||
| disconnect con2; | ||
| connection default; | ||
| DROP TABLE t1, t2; | ||
| SET GLOBAL innodb_print_all_deadlocks = @save; | ||
| # End of 10.5 tests |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| --source include/have_innodb.inc | ||
| --source include/not_embedded.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; | ||
| # Table to increase transaction weight (locks + undo records) | ||
| 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,,) | ||
| --connect(con2, localhost, root,,) | ||
|
|
||
| # con2: increase weight so con1 will be chosen as deadlock victim | ||
| --connection con2 | ||
| 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; | ||
|
|
||
| # con2 waits for row 1 | ||
| --connection con2 | ||
| --send SELECT * FROM t1 WHERE id = 1 FOR UPDATE | ||
|
|
||
| # Wait for con2 to enter lock wait | ||
| --connection default | ||
| 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 | ||
| --connection con1 | ||
| --error ER_LOCK_DEADLOCK | ||
| SELECT * FROM t1 WHERE id = 2 FOR UPDATE; | ||
|
|
||
| # Deadlock info must appear as Note | ||
| --let $level = query_get_value(SHOW WARNINGS, Level, 1) | ||
| if (`SELECT '$level' != 'Note'`) { | ||
| --die First warning should be Note with deadlock info | ||
| } | ||
|
|
||
| --connection con2 | ||
| --reap | ||
| COMMIT; | ||
|
|
||
| --disconnect con1 | ||
| --disconnect con2 | ||
| --connection default | ||
| DROP TABLE t1, t2; | ||
| SET GLOBAL innodb_print_all_deadlocks = @save; | ||
|
|
||
| --echo # End of 10.5 tests |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7182,6 +7182,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, | ||
|
|
@@ -7309,16 +7310,40 @@ and less modified rows. Bit 0 is used to prefer orig_trx in case of a tie. | |
| DBUG_EXECUTE_IF("innodb_deadlock_victim_self", victim= trx;); | ||
| ut_ad(victim->state == TRX_STATE_ACTIVE); | ||
|
|
||
| /* victim->lock.was_chosen_as_deadlock_victim must always be set before | ||
| releasing waiting locks and resetting trx->lock.wait_lock */ | ||
| 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); | ||
| if (len <= 0) | ||
| goto func_exit; | ||
| deadlock_info= static_cast<char*>(ut_malloc_nokey( | ||
| static_cast<size_t>(len) + 1)); | ||
| if (!deadlock_info) | ||
| goto func_exit; | ||
|
Comment on lines
+7324
to
+7327
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 memory will be leaked if Why would we do all this busy work if
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.
Which memory?
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. I mean in case of !deadlock_info it's not allocated, and besides ut_free is called unconditionally after func_exit. So do you mean some other memory?
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. Oh, sorry, I take my words back regarding |
||
| 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') | ||
| --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.
This is confusing, why you do these file operations? IMHO it would be better to change current print functions to produce string that is then written to both to the deadlock error file and pushed to warning.
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 temporary file is a relic from MySQL 5.5 or earlier when InnoDB was written in C and there was no formatted output facility into a heap-allocated buffer. That could be fixed separately. I seemed to remember that there is an option to expose the
lock_latest_err_filein the file system, but there only is the Boolean optioninnodb_status_filethat covers the entireSHOW ENGINE INNODB STATUSoutput.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.
@janlindstrom the nearly same is done from the side of SHOW INNODB STATUS -- the file is opened, ftell'd, etc. I think this work may remain local, wihtout the requirement to refactor.