MDEV-29474 - main.lock_sync fails with timeout

Reimplement test for MDEV-28567 such that it behaves consistently and
consistently covers the code that it was intended to cover. The test
itself moved to mdl_sync.test, because lock_sync.test is supposed for
testing THR_LOCK, not MDL. Verified with DBUG_ASSERT(0) in
open_tables() (from 92bfc0e8c4) restored.

The test is failing with various symptoms as it was designed to be
sporadic. It did cover the code that it was supposed to cover in
less than 0.1% of runs. That is the following command succeeds often
with DBUG_ASSERT(0) in open_tables() (from 92bfc0e8c4) restored:
./mtr --repeat=25 --parallel=40 lock_sync{,,,,}{,,,,}

Test is also reimplemented such that it doesn't rely on buggy "MDL
overweight" behaviour as described in MDEV-36887. Previously it did
require preceding statements that fall in deadlock in default
connection. Now it works consistently in both cases: if there was
preceding deadlock and if there was none.

mdl_after_find_deadlock sync point added to avoid premature deadlock
handling by connections that don't intend to handle it. ALTER VIEW
connection is supposed to handle it, this makes test behaviour
consistent.

Tracking history:
92bfc0e8c4 MDEV-17554, assert added
f88511647a MDEV-28567, test added, assertion modified
fe3adde250 MDEV-29060, test moved, fix attempted
8ee93b9cb4 MDEV-29060, fix attempted
611d442510 fix attempted
77c465d5aa assertion modified
4c695c85bd assertion removed
MDEV-29474 - subj
MDEV-36665 - duplicate of MDEV-29474
This commit is contained in:
Sergey Vojtovich 2025-05-26 18:11:50 +04:00
parent 060c0e3ff5
commit bfbba94ead
5 changed files with 85 additions and 145 deletions

View File

@ -856,28 +856,3 @@ SET DEBUG_SYNC="RESET";
disconnect con1;
disconnect con2;
DROP TABLES t1, t2;
#
# MDEV-28567 Assertion `0' in open_tables upon function-related operation
#
CREATE TABLE t1 (a INT);
CREATE TABLE t2 (b INT);
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW UPDATE t2 SET b = 0;
CREATE TRIGGER tr2 BEFORE INSERT ON t2 FOR EACH ROW UPDATE t1 SET a = 6;
CREATE VIEW v1 AS SELECT * FROM t1;
SET AUTOCOMMIT=OFF;
SELECT * FROM t1;
a
DROP TRIGGER tr1;
INSERT INTO t2 SELECT * FROM t2;
SELECT f() FROM t2;
ERROR 42000: FUNCTION test.f does not exist
set debug_sync= 'after_open_table_mdl_shared signal s1';
ALTER VIEW v1 AS SELECT f() FROM t1;
CREATE FUNCTION f() RETURNS INT RETURN 1;
set debug_sync= 'now wait_for s1';
SELECT * FROM ( SELECT * FROM v1 ) sq;
COMMIT;
DROP VIEW v1;
DROP FUNCTION IF EXISTS f;
DROP TABLE t1, t2;
set debug_sync= 'reset';

View File

@ -1079,123 +1079,3 @@ DROP TABLES t1, t2;
# Check that all connections opened by test cases in this file are really
# gone so execution of other tests won't be affected by their presence.
--source include/wait_until_count_sessions.inc
--echo #
--echo # MDEV-28567 Assertion `0' in open_tables upon function-related operation
--echo #
# To get MDL trace run this case like this:
# mtr --mysqld=--debug=d,mdl,query:i:o,/tmp/mdl.log ...
# Cleanup trace like this:
# sed -i -re '/(mysql|performance_schema|sys|mtr)\// d; /MDL_BACKUP_|MDL_INTENTION_/ d; /\/(t2|tr1|tr2)/ d' /tmp/mdl.log
CREATE TABLE t1 (a INT);
CREATE TABLE t2 (b INT);
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW UPDATE t2 SET b = 0;
CREATE TRIGGER tr2 BEFORE INSERT ON t2 FOR EACH ROW UPDATE t1 SET a = 6;
CREATE VIEW v1 AS SELECT * FROM t1;
SET AUTOCOMMIT=OFF;
SELECT * FROM t1;
# T@6
# Seized: test/t1 (MDL_SHARED_READ)
--connect (con1,localhost,root,,test)
--send
DROP TRIGGER tr1;
# T@7
# Seized: test/t1 (MDL_SHARED_NO_WRITE)
# Waiting: test/t1 (MDL_EXCLUSIVE)
# Waiting: test/t1 (MDL_SHARED_WRITE)
# Deadlock: test/t1 (MDL_SHARED_WRITE)
--connection default
--error 0, ER_LOCK_DEADLOCK
INSERT INTO t2 SELECT * FROM t2;
# T@6
# Released: test/t1 (MDL_SHARED_READ)
# T@7
# Acquired: test/t1 (MDL_EXCLUSIVE) (good)
--error ER_SP_DOES_NOT_EXIST
SELECT f() FROM t2;
# T@6
# Seized: test/f (MDL_SHARED)
# T@7
# Released: test/t1 (MDL_EXCLUSIVE)
# Good1: continue T@6 below
# Bad1: continue T@8 below
# Now we hold test/f, the below code creates concurrent
# waiting of 3 threads for test/f which leads to deadlock (Bad)
# To achive Good comment out 'now wait_for s1' below and run multiple times.
--connect (con2,localhost,root,,test)
set debug_sync= 'after_open_table_mdl_shared signal s1';
--send
ALTER VIEW v1 AS SELECT f() FROM t1;
# T@8
# Good2: Waiting: test/v1 (MDL_EXCLUSIVE)
# Good2-3: continue T@7 below
# Good5: Acquired: test/v1 (MDL_EXCLUSIVE)
# Good5: Seized: test/v1 (MDL_EXCLUSIVE)
# Good5-6: continue T@7 below
# Good7: Seized: test/t1 (MDL_SHARED_READ)
# Good7: Waiting: test/f (MDL_SHARED)
# Good7-8: continue T@7 below
# Good9: Acquired: test/f (MDL_SHARED)
# Good9: Released: test/f (MDL_SHARED)
# Good9: Released: test/t1 (MDL_SHARED_READ)
# Good9: Released: test/v1 (MDL_EXCLUSIVE)
# Good9: command finished without error
# Bad1: Seized: test/v1 (MDL_EXCLUSIVE)
# Bad1: Seized: test/v1 (MDL_EXCLUSIVE)
# Bad1: Seized: test/t1 (MDL_SHARED_READ)
# Bad1-2: continue T@6 below
# Bad4: Waiting: test/f (MDL_SHARED)
# Bad4: Deadlock: test/f (MDL_SHARED)
# Bad4: command finished with error
--connection con1
--reap
--send
CREATE FUNCTION f() RETURNS INT RETURN 1;
# T@7
# Good3: Waiting: test/f (MDL_EXCLUSIVE)
# Good3-4: continue T@6 below
# Good6: Acquired: test/f (MDL_EXCLUSIVE)
# Good6-7: continue T@8 above
# Good8: Released: test/f (MDL_EXCLUSIVE)
# Good8-9: continue T@8 above
# Bad3: Waiting: test/f (MDL_EXCLUSIVE)
# Bad3-4: continue T@8 above
--connection default
set debug_sync= 'now wait_for s1';
--disable_result_log
SELECT * FROM ( SELECT * FROM v1 ) sq;
--enable_result_log
# T@6
# Good1: Seized: test/v1 (MDL_SHARED_READ)
# Good1-2: continue T@8 above
# Good4: Seized: test/t1 (MDL_SHARED_READ)
# Bad2: Waiting: test/v1 (MDL_SHARED_READ)
# Bad2-3: continue T@7 above
# Cleanup
COMMIT;
# Good4: Released: test/t1 (MDL_SHARED_READ)
# Good4: Released: test/v1 (MDL_SHARED_READ)
# Good4: Released: test/f (MDL_SHARED)
# Good4-5: continue T@8 above
--connection con2
--error 0, ER_SP_DOES_NOT_EXIST
--reap
--disconnect con1
--disconnect con2
--connection default
--source include/wait_until_count_sessions.inc
DROP VIEW v1;
DROP FUNCTION IF EXISTS f;
DROP TABLE t1, t2;
set debug_sync= 'reset';

View File

@ -3015,3 +3015,34 @@ connection default;
SET debug_sync='RESET';
DROP TABLE t1;
disconnect con1;
#
# MDEV-28567 Assertion `0' in open_tables upon function-related operation
#
CREATE VIEW v1 AS SELECT 1;
CREATE FUNCTION f1() RETURNS INT RETURN (SELECT * FROM v1);
connect con1, localhost, root;
SET debug_sync='open_and_process_table SIGNAL ready1 WAIT_FOR go1';
ALTER VIEW v1 AS SELECT f1();
connect con2, localhost, root;
SET debug_sync='now WAIT_FOR ready1';
SET debug_sync='mdl_after_find_deadlock SIGNAL ready2';
SELECT f1();
connect con3, localhost, root;
SET debug_sync='now WAIT_FOR ready2';
SET debug_sync='mdl_after_find_deadlock SIGNAL ready3';
DROP FUNCTION f1;
connection default;
SET debug_sync='now WAIT_FOR ready3';
SET debug_sync='now SIGNAL go1';
connection con3;
disconnect con3;
connection con2;
f1()
1
disconnect con2;
connection con1;
ERROR 42000: FUNCTION test.f1 does not exist
disconnect con1;
connection default;
DROP VIEW v1;
SET debug_sync='reset';

View File

@ -4010,6 +4010,58 @@ DROP TABLE t1;
disconnect con1;
--echo #
--echo # MDEV-28567 Assertion `0' in open_tables upon function-related operation
--echo #
#
# This test covers deadlock recovery code in open_tables() after
# open_and_process_routine(). ALTER VIEW v1 connection must fall as a deadlock
# victim to hit this code.
#
# default connection cannot be used since it may have deadlock weight spoiled
# by "MDL deadlock overweight" feature from previous deadlocks. In this case
# ALTER VIEW v1 won't fall as a deadlock victim and the test won't cover
# intended code.
#
CREATE VIEW v1 AS SELECT 1;
CREATE FUNCTION f1() RETURNS INT RETURN (SELECT * FROM v1);
connect con1, localhost, root;
SET debug_sync='open_and_process_table SIGNAL ready1 WAIT_FOR go1';
send ALTER VIEW v1 AS SELECT f1(); # X v1, S f1
connect con2, localhost, root;
SET debug_sync='now WAIT_FOR ready1';
SET debug_sync='mdl_after_find_deadlock SIGNAL ready2';
send SELECT f1(); # S f1, SR v1
connect con3, localhost, root;
SET debug_sync='now WAIT_FOR ready2';
SET debug_sync='mdl_after_find_deadlock SIGNAL ready3';
send DROP FUNCTION f1; # X f1
connection default;
SET debug_sync='now WAIT_FOR ready3';
SET debug_sync='now SIGNAL go1';
connection con3;
reap;
disconnect con3;
connection con2;
reap;
disconnect con2;
connection con1;
--error ER_SP_DOES_NOT_EXIST
reap;
disconnect con1;
connection default;
DROP VIEW v1;
SET debug_sync='reset';
# Check that all connections opened by test cases in this file are really
# gone so execution of other tests won't be affected by their presence.
--source include/wait_until_count_sessions.inc

View File

@ -2396,6 +2396,8 @@ MDL_context::acquire_lock(MDL_request *mdl_request, double lock_wait_timeout)
find_deadlock();
DEBUG_SYNC(get_thd(), "mdl_after_find_deadlock");
struct timespec abs_timeout, abs_shortwait;
set_timespec_nsec(abs_timeout,
(ulonglong)(lock_wait_timeout * 1000000000ULL));