Commit 5ab78cf3 authored by Vlad Lesin's avatar Vlad Lesin

MDEV-29515 innodb.deadlock_victim_race is unstable

The test is unstable because 'UPDATE t SET b = 100' latches a page and
waits for 'upd_cont' signal in lock_trx_handle_wait_enter sync point, then
purge requests RW_X_LATCH on the same page, and then 'SELECT * FROM t
WHERE a = 10 FOR UPDATE' requests RW_S_LATCH, waiting for RW_X_LATCH
requested by purge. 'UPDATE t SET b = 100' can't release page latch as
it waits for upd_cont signal, which must be emitted after 'SELECT * FROM
t WHERE a = 10 FOR UPDATE' acquired RW_S_LATCH. So we have a deadlock,
which is resolved by finishing the debug sync point wait by timeout, and
the 'UPDATE t SET b = 100' releases it's record locks rolling back the
transaction, and 'SELECT * FROM t WHERE a = 10 FOR UPDATE' is finished
successfully instead of finishing by lock wait timeout.

The fix is to forbid purging during the test by opening read view in a
separate connection before the first insert into the table.

Besides, 'lock_wait_end' syncpoint is not needed, as it enough to wait
the end of the SELECT execution to let the UPDATE to continue.
parent a762dad4
connect cancel_purge,localhost,root,,;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
connection default;
CREATE TABLE t (a int PRIMARY KEY, b int) engine = InnoDB;
CREATE TABLE t2 (a int PRIMARY KEY) engine = InnoDB;
INSERT INTO t VALUES (10, 10), (20, 20), (30, 30);
......@@ -24,11 +27,11 @@ connection default;
SET DEBUG_SYNC='now WAIT_FOR sel_locked';
ROLLBACK;
SET DEBUG_SYNC='now SIGNAL upd_cont';
SET innodb_lock_wait_timeout=1;
SET DEBUG_SYNC="now WAIT_FOR upd_locked";
SET DEBUG_SYNC="lock_wait_end SIGNAL upd_cont";
SET SESSION innodb_lock_wait_timeout=1;
SELECT * FROM t WHERE a = 10 FOR UPDATE;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SET DEBUG_SYNC="now SIGNAL upd_cont";
connection con_3;
a b
20 20
......@@ -40,3 +43,4 @@ connection default;
SET DEBUG_SYNC = 'RESET';
DROP TABLE t;
DROP TABLE t2;
disconnect cancel_purge;
......@@ -2,6 +2,17 @@
--source include/have_debug_sync.inc
--source include/count_sessions.inc
--connect(cancel_purge,localhost,root,,)
# Purge can cause deadlock in the test, requesting page's RW_X_LATCH for trx
# ids reseting, after trx 2 acqured RW_S_LATCH and suspended in debug sync point
# lock_trx_handle_wait_enter, waiting for upd_cont signal, which must be
# emitted after the last SELECT in this test. The last SELECT will hang waiting
# for purge RW_X_LATCH releasing, and trx 2 will be rolled back by timeout.
# The last SELECT will then be successfully executed instead of finishing by
# lock wait timeout.
START TRANSACTION WITH CONSISTENT SNAPSHOT;
--connection default
CREATE TABLE t (a int PRIMARY KEY, b int) engine = InnoDB;
CREATE TABLE t2 (a int PRIMARY KEY) engine = InnoDB;
......@@ -60,7 +71,6 @@ SET DEBUG_SYNC='now WAIT_FOR sel_locked';
ROLLBACK;
SET DEBUG_SYNC='now SIGNAL upd_cont';
SET innodb_lock_wait_timeout=1;
SET DEBUG_SYNC="now WAIT_FOR upd_locked";
# Locking queue:
# (10,10) (20,20) (30,30)
......@@ -72,11 +82,12 @@ SET DEBUG_SYNC="now WAIT_FOR upd_locked";
# sequential read (with rr_sequential() read record function), and requested
# lock on (30,30). But the deadlock has not been determined yet.
SET DEBUG_SYNC="lock_wait_end SIGNAL upd_cont";
SET SESSION innodb_lock_wait_timeout=1;
--error ER_LOCK_WAIT_TIMEOUT
# The deadlock will be determined in lock_wait() after lock wait timeout
# expired.
SELECT * FROM t WHERE a = 10 FOR UPDATE;
SET DEBUG_SYNC="now SIGNAL upd_cont";
--connection con_3
--reap
......@@ -98,5 +109,5 @@ SELECT * FROM t WHERE a = 10 FOR UPDATE;
SET DEBUG_SYNC = 'RESET';
DROP TABLE t;
DROP TABLE t2;
--disconnect cancel_purge
--source include/wait_until_count_sessions.inc
......@@ -44,7 +44,6 @@ Created 5/7/1996 Heikki Tuuri
#include "row0vers.h"
#include "pars0pars.h"
#include "srv0mon.h"
#include "scope.h"
#include <set>
......@@ -1724,12 +1723,6 @@ dberr_t lock_wait(que_thr_t *thr)
if (trx->mysql_thd)
DEBUG_SYNC_C("lock_wait_start");
/* Create the sync point for any quit from the function. */
ut_d(SCOPE_EXIT([trx]() {
if (trx->mysql_thd)
DEBUG_SYNC_C("lock_wait_end");
}));
/* InnoDB system transactions may use the global value of
innodb_lock_wait_timeout, because trx->mysql_thd == NULL. */
const ulong innodb_lock_wait_timeout= trx_lock_wait_timeout_get(trx);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment