Skip to content

Commit 917c1e3

Browse files
author
Dmitry Lenev
committed
Patch that changes approach to how we acquire metadata
locks for DML statements and changes the way MDL locks are acquired/granted in contended case. Instead of backing-off when a lock conflict is encountered and waiting for it to go away before restarting open_tables() process we now wait for lock to be released without releasing any previously acquired locks. If conflicting lock goes away we resume opening tables. If waiting leads to a deadlock we try to resolve it by backing-off and restarting open_tables() immediately. As result both waiting for possibility to acquire and acquiring of a metadata lock now always happen within the same MDL API call. This has allowed to make release of a lock and granting it to the most appropriate pending request an atomic operation. Thanks to this it became possible to wake up during release of lock only those waiters which requests can be satisfied at the moment as well as wake up only one waiter in case when granting its request would prevent all other requests from being satisfied. This solves thundering herd problem which occured in cases when we were releasing some lock and woke up many waiters for SNRW or X locks (this was the issue in bug#52289 "performance regression for MyISAM in sysbench OLTP_RW test". This also allowed to implement more fair (FIFO) scheduling among waiters with the same priority. It also opens the door for introducing new types of requests for metadata locks such as low-prio SNRW lock which is necessary in order to support LOCK TABLES LOW_PRIORITY WRITE. Notice that after this sometimes can report ER_LOCK_DEADLOCK error in cases in which it has not happened before. Particularly we will always report this error if waiting for conflicting lock has happened in the middle of transaction and resulted in a deadlock. Before this patch the error was not reported if deadlock could have been resolved by backing off all metadata locks acquired by the current statement. --BZR-- revision-id: [email protected] property-branch-nick: mysql-trunk-wfl-3 property-file-info: ld7:file_id49:mdl_sync.result-20090302184602-k11i6o8vh2s5umno-17:message302:Added test coverage for some aspects of deadlock handling in property-file-info: metadata locking subsystem. property-file-info: Adjusted test case after removing back-off in general case property-file-info: when conflicting metadata lock is encountered during property-file-info: open_tables() (now this happens only if waiting for property-file-info: conflicting lock to go away leads to a deadlock).4:path28:mysql-test/r/mdl_sync.resulted7:file_id48:sp_sync.result-20100112141220-hrw3yyvlvrr1hnom-17:message213:Adjusted test case after removing back-off in general case property-file-info: when conflicting metadata lock is encountered during property-file-info: open_tables() (now this happens only if waiting for property-file-info: conflicting lock to go away leads to a deadlock).4:path27:mysql-test/r/sp_sync.resulted7:file_id55:dml_setup_instrument-20100112014603-ubwjo2a0fk4thccy-517:message99:Adjusted test results after renaming MDL_context:: property-file-info: m_waiting_for_lock rwlock to m_LOCK_waiting_for.4:path58:mysql-test/suite/perfschema/r/dml_setup_instruments.resulted7:file_id66:sp1f-rpl_sp.result-20050505122047-6pz3qkb234acgvxly33c2rm665rolo6w7:message392:Adjusted test case after implementing new approach to property-file-info: acquiring metadata locks in open_tables(). We no longer property-file-info: release all MDL locks acquired by statement before waiting property-file-info: for conflicting lock to go away. As result DROP FUNCTION property-file-info: statement has to wait for DML statement which managed to property-file-info: acquire metadata lock on function being dropped and now property-file-info: waits for other conflicting metadata lock to go away.4:path36:mysql-test/suite/rpl/r/rpl_sp.resulted7:file_id64:sp1f-rpl_sp.test-20050505122048-sfpoerdz7zbb5decjlbpxcme6ew43qcp7:message392:Adjusted test case after implementing new approach to property-file-info: acquiring metadata locks in open_tables(). We no longer property-file-info: release all MDL locks acquired by statement before waiting property-file-info: for conflicting lock to go away. As result DROP FUNCTION property-file-info: statement has to wait for DML statement which managed to property-file-info: acquire metadata lock on function being dropped and now property-file-info: waits for other conflicting metadata lock to go away.4:path34:mysql-test/suite/rpl/t/rpl_sp.tested7:file_id47:mdl_sync.test-20090302184558-2nfnp7wh7pj6ra6e-17:message302:Added test coverage for some aspects of deadlock handling in property-file-info: metadata locking subsystem. property-file-info: Adjusted test case after removing back-off in general case property-file-info: when conflicting metadata lock is encountered during property-file-info: open_tables() (now this happens only if waiting for property-file-info: conflicting lock to go away leads to a deadlock).4:path26:mysql-test/t/mdl_sync.tested7:file_id46:sp_sync.test-20100112141216-z3d36b7ouqevywqw-17:message213:Adjusted test case after removing back-off in general case property-file-info: when conflicting metadata lock is encountered during property-file-info: open_tables() (now this happens only if waiting for property-file-info: conflicting lock to go away leads to a deadlock).4:path25:mysql-test/t/sp_sync.tested7:file_id40:mdl.cc-20080523121737-j62pi0m62eaw1hq6-17:message3296:Changed MDL subsystem to support new approach to acquring property-file-info: metadata locks in open tables and more fair and efficient property-file-info: scheduling of metadata locks. To implement this: property-file-info: - Made releasing of the lock and granting it to the most property-file-info: appropriate pending request atomic operation. As result it property-file-info: became possible to wake up only those waiters requests from property-file-info: which can be satisfied at the moment as well as wake-up property-file-info: only one waiter in case when granting its request would property-file-info: prevent all other requests from being satisfied. property-file-info: This solved thundering herd problem which occured in cases property-file-info: when we were releasing some lock and woke up many waiters property-file-info: for SNRW or X locks (this was the issue in Bug #52289 property-file-info: "performance regression for MyISAM in sysbench OLTP_RW property-file-info: test". property-file-info: To emphasize above changes wake_up_waiters() was renamed property-file-info: to MDL_context::reschedule_waiters(). property-file-info: - Changed code to add tickets for new requests to the back of property-file-info: waiters queue and to select tickets to be satisfied from property-file-info: the head of the queue if possible (this makes scheduling of property-file-info: requests with the same priority fair). To be able to do property-file-info: this efficiently we now use for waiting and granted queues property-file-info: version of I_P_List class which provides fast push_back() property-file-info: method. property-file-info: - Members and methods of MDL_context related to sending property-file-info: and waiting for signal were moved to separate MDL_wait property-file-info: class. property-file-info: - Since in order to avoid race conditions we must grant the property-file-info: lock only to the context which was not chosen as a victim property-file-info: of deadlock, killed or aborted due to timeout property-file-info: MDL_wait::set_status() (former awake()) was changed not to property-file-info: send signal if signal slot is already occupied and to property-file-info: indicate this fact through its return value. As another property-file-info: consequence MDL_wait::timed_wait() method was changed to property-file-info: handle timeout (optionally) and abort due to kill as property-file-info: signals which make signal slot occupied. property-file-info: - Renamed MDL_context::acquire_lock_impl() to acquire_lock(). property-file-info: Changed it to be able correctly process requests for shared property-file-info: locks when there are open HANDLERs, made this method more property-file-info: optimized for acquisition of shared locks. As part of this property-file-info: change moved code common between try_acquire_lock() and property-file-info: acquire_lock() to new try_acquire_lock_impl() method. property-file-info: Also adjusted acquire_lock()'s code to take into account property-file-info: the fact that in cases when lock is granted as result of property-file-info: MDL_context::reschedule_waiters() call (i.e. when it is property-file-info: granted after waiting for lock conflict to go away) property-file-info: updating MDL_lock state is responsibility of the thread property-file-info: calling reschedule_waiters(). property-file-info: - Changed MDL_context::find_deadlock() to send VICTIM property-file-info: signal even if victim is the context which has initiated property-file-info: deadlock detection. This is required in order to avoid property-file-info: races in cases when the same context simultaneously is property-file-info: chosen as a victim and its request for lock is satisfied. property-file-info: As result return value of this method became unnecessary property-file-info: and it was changed to return void. property-file-info: Adjusted MDL_lock::find_deadlock() method to take into property-file-info: account that now there can be a discrepancy between property-file-info: MDL_context::m_waiting_for value being set and real state property-file-info: of the ticket this member points to. property-file-info: - Renamed MDL_context::m_waiting_for_lock to m_LOCK_waiting_for property-file-info: and MDL_context::stop_waiting() to done_waiting_for(). property-file-info: - Finally, removed MDL_context::wait_for_lock() method.4:path10:sql/mdl.cced7:file_id39:mdl.h-20080523121748-o4y2wcq3maotb9do-17:message1538:Changed MDL subsystem to support new approach to acquring property-file-info: metadata locks in open tables and more fair and efficient property-file-info: scheduling of metadata locks. To implement this: property-file-info: - Members and methods of MDL_context related to sending property-file-info: and waiting for signal were moved to separate MDL_wait property-file-info: class. property-file-info: - Since now in order to avoid race conditions we must grant property-file-info: the lock only to the context which was not chosen as a property-file-info: victim of deadlock, killed or aborted due to timeout property-file-info: MDL_wait::set_status (former awake()) was changed not to property-file-info: send signal if signal slot is already occupied and to property-file-info: indicate this fact through its return value. property-file-info: Also NORMAL_WAKE_UP signal became GRANTED, and timeouts property-file-info: and aborts due to kill became full blown signals rather property-file-info: than simple return values. property-file-info: - MDL_wait::timed_wait() now takes extra parameter that property-file-info: indicates whether signal should be set if timeout is property-file-info: reached. property-file-info: - Enabled fast push_back() operation in MDL_context::m_tickets property-file-info: list to make move_ticket_after_trans_sentinel() method more property-file-info: efficient. property-file-info: - Removed MDL_context::wait_for_lock() method. property-file-info: - Renamed MDL_context::m_waiting_for_lock to m_LOCK_waiting_for property-file-info: and MDL_context::stop_waiting() to done_waiting_for(). property-file-info: - MDL_context::acquire_lock_impl() became acquire_lock(). property-file-info: - Introduced MDL_context::try_acquire_lock_impl() as a property-file-info: place for code shared by try_acquire_lock and property-file-info: acquire_lock(). property-file-info: - Due to fact that now VICTIM signal is sent even if victim property-file-info: is the context which has initiated deadlock detection property-file-info: find_deadlock() no longer needs a return value.4:path9:sql/mdl.hed7:file_id64:sp1f-sql_base.cc-19700101030959-w7tul2gb2n4jzayjwlslj3ybmf3uhk6a7:message2187:Implemented new approach to acquiring metadata locks in property-file-info: open_tables(). We no longer perform back-off when conflicting property-file-info: metadata lock is encountered. Instead we wait for this lock property-file-info: to go away while holding all locks which were acquired so property-file-info: far. Back-off is only used in situation when further waiting property-file-info: will cause a deadlock which could be avoided by performing property-file-info: back-off and restarting open_tables() process. Absence of property-file-info: waiting between back-off and restart of acquiring metadata property-file-info: locks can't lead to livelocks as MDL subsystem was changed property-file-info: to make release of lock and granting it to waiting lock property-file-info: an atomic action, so back-off will automatically give way property-file-info: to other participants of deadlock loop. property-file-info: Accordingly: property-file-info: - open_table_get_mdl_lock() and open_and_process_routine() property-file-info: were changed to wait for conflicting metadata lock to property-file-info: go away without back-off. Only if such wait leads to a property-file-info: deadlock back-off is requested. As part of this change property-file-info: new error handler class was introduced which converts, property-file-info: if possible, ER_LOCK_DEADLOCK error to a request for property-file-info: back-off and re-start of open_tables() process. property-file-info: - Open_table_context::recover_from_failed_open() was changed property-file-info: not to wait in case of metadata lock conflict. Instead we property-file-info: immediately proceed to re-acquiring locks. property-file-info: - Open_table_context::request_backoff_action() now always property-file-info: emits error if back-off is requested in the middle of property-file-info: transaction as we can't be sure that releasing lock property-file-info: which were acquired only by current statement will property-file-info: resolve a deadlock. Before this patch such situations were property-file-info: successfully detected thanks to the fact that we called property-file-info: MDL_context::wait_for_lock() method in property-file-info: recover_from_failed_open(). property-file-info: - In order to avoid deadlocks open_tables() code was adjusted property-file-info: to flush open HANDLERs for which there are pending requests property-file-info: for X locks before restarting the process of acquiring property-file-info: metadata locks. property-file-info: - Changed close_tables_for_reopen() not to reset MDL_request property-file-info: for tables belonging to the tail of prelocking list. It is property-file-info: no longer necessary as these MDL_request objects won't be property-file-info: used for any waiting. property-file-info: - Adjusted comment in tdc_wait_for_old_version() to avoid property-file-info: mentioning removed MDL_context::wait_for_lock() method.4:path15:sql/sql_base.cced7:file_id45:sql_base.h-20100331135644-cgcb6oowzqyx7fi3-127:message271:As we no longer wait for conflicting metadata lock away in property-file-info: Open_table_context::recover_from_failed_open() method, property-file-info: Open_table_context::OT_WAIT_MDL_LOCK action was renamed to property-file-info: OT_MDL_CONFLICT. property-file-info: Also Open_table_context::m_failed_mdl_request became property-file-info: unnecessary and was removed.4:path14:sql/sql_base.hed7:file_id45:sql_plist.h-20080523121803-vhna5yyou5h80k5o-17:message126:Extended I_P_List template to support efficient push_back() property-file-info: operation if it is parameterized with an appropriate policy property-file-info: class.4:path15:sql/sql_plist.hed7:file_id64:sp1f-sql_show.cc-19700101030959-umlljfnpplg452h7reeyqr4xnbmlkvfj7:message186:Adjusted code after removal of MDL_context::wait_for_lock() property-file-info: method. Now if one needs to acquire metadata lock with waiting property-file-info: one has to use a variant of MDL_context::acquire_lock() method.4:path15:sql/sql_show.ccee testament3-sha1: 466d90b279fee5b6112f0a9e917ff35964078170
1 parent b4696db commit 917c1e3

13 files changed

Lines changed: 1065 additions & 510 deletions

File tree

mysql-test/r/mdl_sync.result

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1765,6 +1765,7 @@ drop tables t1, t2;
17651765
# locking subsystem.
17661766
#
17671767
drop tables if exists t0, t1, t2, t3, t4, t5;
1768+
set debug_sync= 'RESET';
17681769
create table t1 (i int);
17691770
create table t2 (j int);
17701771
create table t3 (k int);
@@ -1943,6 +1944,98 @@ commit;
19431944
# Reap ALTER TABLE ... RENAME.
19441945
drop table t2;
19451946
#
1947+
# Test that in situation when MDL subsystem detects a deadlock
1948+
# but it turns out that it can be resolved by backing-off locks
1949+
# acquired by one of participating transactions (which is
1950+
# possible when one of transactions consists only of currently
1951+
# executed statement, e.g. in autocommit mode) no error is
1952+
# reported.
1953+
#
1954+
create table t1 (i int);
1955+
create table t2 (j int);
1956+
# Ensure that the below SELECT stops once it has acquired metadata
1957+
# lock on table 't2'.
1958+
set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish';
1959+
# Sending:
1960+
select * from t2, t1;
1961+
#
1962+
# Switching to connection 'deadlock_con1'.
1963+
# Wait till SELECT acquires MDL on 't2' and starts waiting for signal.
1964+
set debug_sync= 'now WAIT_FOR locked';
1965+
# Sending:
1966+
lock tables t1 write, t2 write;
1967+
#
1968+
# Switching to connection 'deadlock_con2'.
1969+
# Wait until LOCK TABLES acquires SNRW lock on 't1' and is blocked
1970+
# while trying to acquire SNRW lock on 't1'.
1971+
# Resume SELECT execution, this should eventually unblock LOCK TABLES.
1972+
set debug_sync= 'now SIGNAL finish';
1973+
#
1974+
# Switching to connection 'deadlock_con1'.
1975+
# Reaping LOCK TABLES.
1976+
unlock tables;
1977+
#
1978+
# Switching to connection 'default'.
1979+
# Reaping SELECT. It succeed and not report ER_LOCK_DEADLOCK error.
1980+
j i
1981+
drop tables t1, t2;
1982+
#
1983+
# Test coverage for situation in which a race has happened
1984+
# during deadlock detection process which led to unwarranted
1985+
# ER_LOCK_DEADLOCK error.
1986+
#
1987+
create table t1 (i int);
1988+
# Ensure that ALTER waits once it has acquired SNW lock.
1989+
set debug_sync='after_open_table_mdl_shared SIGNAL parked1 WAIT_FOR go1';
1990+
# Sending:
1991+
alter table t1 add column j int;
1992+
#
1993+
# Switching to connection 'deadlock_con1'.
1994+
# Wait till ALTER acquires SNW lock and stops.
1995+
set debug_sync='now WAIT_FOR parked1';
1996+
# Ensure that INSERT is paused once it detects that there is
1997+
# a conflicting metadata lock so it has to wait, but before
1998+
# deadlock detection is run.
1999+
set debug_sync='mdl_acquire_lock_wait SIGNAL parked2 WAIT_FOR go2';
2000+
# Sending:
2001+
insert into t1 values ();
2002+
#
2003+
# Switching to connection 'deadlock_con2'.
2004+
# Wait till INSERT is paused.
2005+
set debug_sync='now WAIT_FOR parked2';
2006+
# Resume ALTER execution. Eventually it will release its
2007+
# metadata lock and INSERT's request for SW lock will be
2008+
# satisified.
2009+
set debug_sync='now SIGNAL go1';
2010+
#
2011+
# Switching to connection 'default'.
2012+
# Reaping ALTER TABLE.
2013+
# Add a new request for SNW lock to waiting graph.
2014+
# Sending:
2015+
alter table t1 drop column j;
2016+
#
2017+
# Switching to connection 'deadlock_con2'.
2018+
# Wait until ALTER is blocked.
2019+
# Resume INSERT so it can start deadlock detection.
2020+
#
2021+
# At this point there is a discrepancy between the fact that INSERT's
2022+
# SW lock is already satisfied, but INSERT's connection is still
2023+
# marked as waiting for it. Looking for a loop in waiters graph
2024+
# without additional checks has detected a deadlock (INSERT waits
2025+
# for SW lock; which is not granted because of pending SNW lock from
2026+
# ALTER; which waits for active SW lock from INSERT). Since requests
2027+
# for SW and SNW locks have same weight ALTER was selected as a victim
2028+
# and ended with ER_LOCK_DEADLOCK error.
2029+
set debug_sync='now SIGNAL go2';
2030+
#
2031+
# Switching to connection 'deadlock_con1'.
2032+
# Reaping INSERT.
2033+
#
2034+
# Switching to connection 'default'.
2035+
# Reaping ALTER. It should succeed and not produce ER_LOCK_DEADLOCK.
2036+
drop table t1;
2037+
set debug_sync= 'RESET';
2038+
#
19462039
# Test for bug #46748 "Assertion in MDL_context::wait_for_locks()
19472040
# on INSERT + CREATE TRIGGER".
19482041
#
@@ -2175,7 +2268,7 @@ alter table t1 add column e int, rename to t2;;
21752268
#
21762269
# Switching to connection 'default'.
21772270
set debug_sync='now WAIT_FOR alter_table_locked';
2178-
set debug_sync='before_open_table_wait_refresh SIGNAL alter_go';
2271+
set debug_sync='mdl_acquire_lock_wait SIGNAL alter_go';
21792272
# The below statement should get ER_LOCK_DEADLOCK error
21802273
# (i.e. it should not allow ALTER to proceed, and then
21812274
# fail due to 't1' changing its name to 't2').

mysql-test/r/sp_sync.result

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,36 +59,37 @@ SET DEBUG_SYNC= 'RESET';
5959
#
6060
# Bug #48246 assert in close_thread_table
6161
#
62+
CREATE TABLE t0 (b INTEGER);
6263
CREATE TABLE t1 (a INTEGER);
6364
CREATE FUNCTION f1(b INTEGER) RETURNS INTEGER RETURN 1;
64-
CREATE PROCEDURE p1() SELECT COUNT(f1(a)) FROM t1;
65+
CREATE PROCEDURE p1() SELECT COUNT(f1(a)) FROM t1, t0;
66+
INSERT INTO t0 VALUES(1);
6567
INSERT INTO t1 VALUES(1), (2);
6668
# Connection 2
6769
CALL p1();
6870
COUNT(f1(a))
6971
2
70-
# Connection default
71-
SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR called';
72-
# Sending:
73-
CREATE TABLE t1 (a INTEGER);
74-
# Connection 2
75-
SET DEBUG_SYNC= 'now WAIT_FOR locked';
76-
SET DEBUG_SYNC= 'before_open_table_wait_refresh SIGNAL called WAIT_FOR created';
77-
# This call used to cause an assertion. MDL locking conflict will
78-
# cause back-off and retry. A variable indicating if a prelocking list
79-
# exists, used to be not reset properly causing an eventual assert.
72+
SET DEBUG_SYNC= 'after_open_table_mdl_shared SIGNAL locked_t1 WAIT_FOR go_for_t0';
73+
# This call used to cause an assertion. MDL deadlock with upcoming
74+
# LOCK TABLES statement will cause back-off and retry.
75+
# A variable indicating if a prelocking list exists, used to be not
76+
# reset properly causing an eventual assert.
8077
# Sending:
8178
CALL p1();
8279
# Connection default
83-
# Reaping: CREATE TABLE t1 (a INTEGER)
84-
ERROR 42S01: Table 't1' already exists
85-
SET DEBUG_SYNC= 'now SIGNAL created';
80+
SET DEBUG_SYNC= 'now WAIT_FOR locked_t1';
81+
# Issue LOCK TABLES statement which will enter in MDL deadlock
82+
# with CALL statement and as result will cause it to perform
83+
# back-off and retry.
84+
SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL go_for_t0';
85+
LOCK TABLES t0 WRITE, t1 WRITE;
86+
UNLOCK TABLES;
8687
# Connection 2
8788
# Reaping: CALL p1()
8889
COUNT(f1(a))
8990
2
9091
# Connection default
9192
DROP PROCEDURE p1;
9293
DROP FUNCTION f1;
93-
DROP TABLE t1;
94+
DROP TABLES t0, t1;
9495
SET DEBUG_SYNC= 'RESET';

mysql-test/suite/perfschema/r/dml_setup_instruments.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ wait/synch/rwlock/sql/LOCK_system_variables_hash YES YES
2525
wait/synch/rwlock/sql/LOCK_sys_init_connect YES YES
2626
wait/synch/rwlock/sql/LOCK_sys_init_slave YES YES
2727
wait/synch/rwlock/sql/LOGGER::LOCK_logger YES YES
28-
wait/synch/rwlock/sql/MDL_context::waiting_for_lock YES YES
28+
wait/synch/rwlock/sql/MDL_context::LOCK_waiting_for YES YES
2929
wait/synch/rwlock/sql/MDL_lock::rwlock YES YES
3030
wait/synch/rwlock/sql/Query_cache_query::lock YES YES
3131
wait/synch/rwlock/sql/THR_LOCK_servers YES YES

mysql-test/suite/rpl/r/rpl_sp.result

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,18 +1215,23 @@ lock table t2 write;
12151215
# Sending 'insert into t1 (a) values (f1())'...
12161216
insert into t1 (a) values (f1());
12171217
# Waitng for 'insert into t1 ...' to get blocked on table lock...
1218-
# Sending 'drop function f1'. It will abort the table lock wait.
1219-
drop function f1;
1218+
# Sending 'drop function f1'. It will wait till insert finishes.
1219+
drop function f1;;
12201220
# --> connection default
1221+
# Check that 'drop function f1' gets blocked.
12211222
# Now let's let 'insert' go through...
12221223
unlock tables;
1223-
# --> connection con1
1224+
# --> connection master
12241225
# Reaping 'insert into t1 (a) values (f1())'...
1225-
ERROR 42000: FUNCTION test.f1 does not exist
1226+
# --> connection master1
1227+
# Reaping 'drop function f1'
1228+
# --> connection master
12261229
select * from t1;
12271230
a
1231+
1
12281232
select * from t1;
12291233
a
1234+
1
12301235
drop table t1, t2;
12311236
drop function f1;
12321237
ERROR 42000: FUNCTION test.f1 does not exist

mysql-test/suite/rpl/t/rpl_sp.test

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -657,17 +657,25 @@ connection master1;
657657
let $wait_condition=select count(*)=1 from information_schema.processlist
658658
where state='Waiting for table' and info='insert into t1 (a) values (f1())';
659659
--source include/wait_condition.inc
660-
--echo # Sending 'drop function f1'. It will abort the table lock wait.
661-
drop function f1;
660+
--echo # Sending 'drop function f1'. It will wait till insert finishes.
661+
--send drop function f1;
662662
--echo # --> connection default
663663
connection default;
664+
--echo # Check that 'drop function f1' gets blocked.
665+
let $wait_condition=select count(*)=1 from information_schema.processlist
666+
where state='Waiting for table' and info='drop function f1';
667+
--source include/wait_condition.inc
664668
--echo # Now let's let 'insert' go through...
665669
unlock tables;
666-
--echo # --> connection con1
670+
--echo # --> connection master
667671
connection master;
668672
--echo # Reaping 'insert into t1 (a) values (f1())'...
669-
--error ER_SP_DOES_NOT_EXIST
670673
--reap
674+
--echo # --> connection master1
675+
connection master1;
676+
--echo # Reaping 'drop function f1'
677+
--reap
678+
--echo # --> connection master
671679
connection master;
672680
select * from t1;
673681
sync_slave_with_master;

mysql-test/t/mdl_sync.test

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2411,6 +2411,7 @@ drop tables t1, t2;
24112411
--disable_warnings
24122412
drop tables if exists t0, t1, t2, t3, t4, t5;
24132413
--enable_warnings
2414+
set debug_sync= 'RESET';
24142415

24152416
connect(deadlock_con1,localhost,root,,);
24162417
connect(deadlock_con2,localhost,root,,);
@@ -2700,6 +2701,136 @@ connection default;
27002701

27012702
drop table t2;
27022703

2704+
--echo #
2705+
--echo # Test that in situation when MDL subsystem detects a deadlock
2706+
--echo # but it turns out that it can be resolved by backing-off locks
2707+
--echo # acquired by one of participating transactions (which is
2708+
--echo # possible when one of transactions consists only of currently
2709+
--echo # executed statement, e.g. in autocommit mode) no error is
2710+
--echo # reported.
2711+
--echo #
2712+
create table t1 (i int);
2713+
create table t2 (j int);
2714+
--echo # Ensure that the below SELECT stops once it has acquired metadata
2715+
--echo # lock on table 't2'.
2716+
set debug_sync= 'after_open_table_mdl_shared SIGNAL locked WAIT_FOR finish';
2717+
--echo # Sending:
2718+
--send select * from t2, t1
2719+
2720+
--echo #
2721+
--echo # Switching to connection 'deadlock_con1'.
2722+
connection deadlock_con1;
2723+
--echo # Wait till SELECT acquires MDL on 't2' and starts waiting for signal.
2724+
set debug_sync= 'now WAIT_FOR locked';
2725+
--echo # Sending:
2726+
--send lock tables t1 write, t2 write
2727+
2728+
--echo #
2729+
--echo # Switching to connection 'deadlock_con2'.
2730+
connection deadlock_con2;
2731+
--echo # Wait until LOCK TABLES acquires SNRW lock on 't1' and is blocked
2732+
--echo # while trying to acquire SNRW lock on 't1'.
2733+
let $wait_condition=
2734+
select count(*) = 1 from information_schema.processlist
2735+
where state = "Waiting for table" and info = "lock tables t1 write, t2 write";
2736+
--source include/wait_condition.inc
2737+
--echo # Resume SELECT execution, this should eventually unblock LOCK TABLES.
2738+
set debug_sync= 'now SIGNAL finish';
2739+
2740+
--echo #
2741+
--echo # Switching to connection 'deadlock_con1'.
2742+
connection deadlock_con1;
2743+
--echo # Reaping LOCK TABLES.
2744+
--reap
2745+
unlock tables;
2746+
2747+
--echo #
2748+
--echo # Switching to connection 'default'.
2749+
connection default;
2750+
--echo # Reaping SELECT. It succeed and not report ER_LOCK_DEADLOCK error.
2751+
--reap
2752+
2753+
drop tables t1, t2;
2754+
2755+
--echo #
2756+
--echo # Test coverage for situation in which a race has happened
2757+
--echo # during deadlock detection process which led to unwarranted
2758+
--echo # ER_LOCK_DEADLOCK error.
2759+
--echo #
2760+
create table t1 (i int);
2761+
2762+
--echo # Ensure that ALTER waits once it has acquired SNW lock.
2763+
set debug_sync='after_open_table_mdl_shared SIGNAL parked1 WAIT_FOR go1';
2764+
--echo # Sending:
2765+
--send alter table t1 add column j int
2766+
2767+
--echo #
2768+
--echo # Switching to connection 'deadlock_con1'.
2769+
connection deadlock_con1;
2770+
--echo # Wait till ALTER acquires SNW lock and stops.
2771+
set debug_sync='now WAIT_FOR parked1';
2772+
--echo # Ensure that INSERT is paused once it detects that there is
2773+
--echo # a conflicting metadata lock so it has to wait, but before
2774+
--echo # deadlock detection is run.
2775+
set debug_sync='mdl_acquire_lock_wait SIGNAL parked2 WAIT_FOR go2';
2776+
--echo # Sending:
2777+
--send insert into t1 values ()
2778+
2779+
--echo #
2780+
--echo # Switching to connection 'deadlock_con2'.
2781+
connection deadlock_con2;
2782+
--echo # Wait till INSERT is paused.
2783+
set debug_sync='now WAIT_FOR parked2';
2784+
--echo # Resume ALTER execution. Eventually it will release its
2785+
--echo # metadata lock and INSERT's request for SW lock will be
2786+
--echo # satisified.
2787+
set debug_sync='now SIGNAL go1';
2788+
2789+
--echo #
2790+
--echo # Switching to connection 'default'.
2791+
connection default;
2792+
--echo # Reaping ALTER TABLE.
2793+
--reap
2794+
--echo # Add a new request for SNW lock to waiting graph.
2795+
--echo # Sending:
2796+
--send alter table t1 drop column j
2797+
2798+
--echo #
2799+
--echo # Switching to connection 'deadlock_con2'.
2800+
connection deadlock_con2;
2801+
--echo # Wait until ALTER is blocked.
2802+
let $wait_condition=
2803+
select count(*) = 1 from information_schema.processlist
2804+
where state = "Waiting for table" and info = "alter table t1 drop column j";
2805+
--source include/wait_condition.inc
2806+
--echo # Resume INSERT so it can start deadlock detection.
2807+
--echo #
2808+
--echo # At this point there is a discrepancy between the fact that INSERT's
2809+
--echo # SW lock is already satisfied, but INSERT's connection is still
2810+
--echo # marked as waiting for it. Looking for a loop in waiters graph
2811+
--echo # without additional checks has detected a deadlock (INSERT waits
2812+
--echo # for SW lock; which is not granted because of pending SNW lock from
2813+
--echo # ALTER; which waits for active SW lock from INSERT). Since requests
2814+
--echo # for SW and SNW locks have same weight ALTER was selected as a victim
2815+
--echo # and ended with ER_LOCK_DEADLOCK error.
2816+
set debug_sync='now SIGNAL go2';
2817+
2818+
--echo #
2819+
--echo # Switching to connection 'deadlock_con1'.
2820+
connection deadlock_con1;
2821+
--echo # Reaping INSERT.
2822+
--reap
2823+
2824+
--echo #
2825+
--echo # Switching to connection 'default'.
2826+
connection default;
2827+
--echo # Reaping ALTER. It should succeed and not produce ER_LOCK_DEADLOCK.
2828+
--reap
2829+
2830+
drop table t1;
2831+
2832+
set debug_sync= 'RESET';
2833+
27032834
disconnect deadlock_con1;
27042835
disconnect deadlock_con2;
27052836
disconnect deadlock_con3;
@@ -3097,7 +3228,7 @@ set debug_sync='after_lock_tables_takes_lock SIGNAL alter_table_locked WAIT_FOR
30973228
--echo # Switching to connection 'default'.
30983229
connection default;
30993230
set debug_sync='now WAIT_FOR alter_table_locked';
3100-
set debug_sync='before_open_table_wait_refresh SIGNAL alter_go';
3231+
set debug_sync='mdl_acquire_lock_wait SIGNAL alter_go';
31013232
--echo # The below statement should get ER_LOCK_DEADLOCK error
31023233
--echo # (i.e. it should not allow ALTER to proceed, and then
31033234
--echo # fail due to 't1' changing its name to 't2').

0 commit comments

Comments
 (0)