mirror of
https://github.com/MariaDB/server.git
synced 2025-01-22 14:54:20 +01:00
c44665aeb1
------------------------------------------------------------ revno: 3035.4.1 committer: Davi Arnaut <Davi.Arnaut@Sun.COM> branch nick: 39897-6.0 timestamp: Thu 2009-01-15 12:17:57 -0200 message: Bug#39897: lock_multi fails in pushbuild: timeout waiting for processlist The problem is that relying on the "Table lock" thread state in its current position to detect that a thread is waiting on a lock is race prone. The "Table lock" state change happens before the thread actually tries to grab a lock on a table. The solution is to move the "Table lock" state so that its set only when a thread is actually going to wait for a lock. The state change happens after the thread fails to grab the lock (because it is owned by other thread) and proceeds to wait on a condition. This is considered part of work related to WL#4284 "Transactional DDL locking" Warning: this patch contains an incompatible change. When waiting on a lock in thr_lock.c, the server used to display "Locked" processlist state. After this patch, the state is "Table lock". The new state was actually intended to be display since year 2002, when Monty added it. But up until removal of thd->locked boolean member, this state was ignored by SHOW PROCESSLIST code.
668 lines
16 KiB
Text
668 lines
16 KiB
Text
# Save the initial number of concurrent sessions
|
|
--source include/count_sessions.inc
|
|
|
|
--disable_warnings
|
|
drop table if exists t1,t2;
|
|
--enable_warnings
|
|
|
|
# Test to see if select will get the lock ahead of low priority update
|
|
|
|
connect (locker,localhost,root,,);
|
|
connect (reader,localhost,root,,);
|
|
connect (writer,localhost,root,,);
|
|
|
|
connection locker;
|
|
create table t1(n int);
|
|
insert into t1 values (1);
|
|
lock tables t1 write;
|
|
connection writer;
|
|
send
|
|
update low_priority t1 set n = 4;
|
|
connection reader;
|
|
# Sleep a bit till the update of connection writer is in work and hangs
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Table lock" and info = "update low_priority t1 set n = 4";
|
|
--source include/wait_condition.inc
|
|
send
|
|
select n from t1;
|
|
connection locker;
|
|
# Sleep a bit till the select of connection reader is in work and hangs
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Table lock" and info = "select n from t1";
|
|
--source include/wait_condition.inc
|
|
unlock tables;
|
|
connection writer;
|
|
reap;
|
|
connection reader;
|
|
reap;
|
|
drop table t1;
|
|
|
|
connection locker;
|
|
create table t1(n int);
|
|
insert into t1 values (1);
|
|
lock tables t1 read;
|
|
connection writer;
|
|
send
|
|
update low_priority t1 set n = 4;
|
|
connection reader;
|
|
# Sleep a bit till the update of connection writer is in work and hangs
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Table lock" and info = "update low_priority t1 set n = 4";
|
|
--source include/wait_condition.inc
|
|
select n from t1;
|
|
connection locker;
|
|
unlock tables;
|
|
connection writer;
|
|
reap;
|
|
drop table t1;
|
|
|
|
#
|
|
# Test problem when using locks with multi-updates
|
|
# It should not block when multi-update is reading on a read-locked table
|
|
#
|
|
|
|
connection locker;
|
|
create table t1 (a int, b int);
|
|
create table t2 (c int, d int);
|
|
insert into t1 values(1,1);
|
|
insert into t1 values(2,2);
|
|
insert into t2 values(1,2);
|
|
lock table t1 read;
|
|
connection writer;
|
|
update t1,t2 set c=a where b=d;
|
|
connection reader;
|
|
select c from t2;
|
|
connection locker;
|
|
unlock tables;
|
|
drop table t1;
|
|
drop table t2;
|
|
|
|
#
|
|
# Test problem when using locks on many tables and dropping a table that
|
|
# is to-be-locked by another thread
|
|
#
|
|
#
|
|
connection locker;
|
|
create table t1 (a int);
|
|
create table t2 (a int);
|
|
lock table t1 write, t2 write;
|
|
connection reader;
|
|
send
|
|
insert t1 select * from t2;
|
|
connection locker;
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Table lock" and info = "insert t1 select * from t2";
|
|
--source include/wait_condition.inc
|
|
drop table t2;
|
|
connection reader;
|
|
--error ER_NO_SUCH_TABLE
|
|
reap;
|
|
connection locker;
|
|
drop table t1;
|
|
|
|
#
|
|
# Same test as above, but with the dropped table locked twice
|
|
#
|
|
|
|
connection locker;
|
|
create table t1 (a int);
|
|
create table t2 (a int);
|
|
lock table t1 write, t2 write, t1 as t1_2 write, t2 as t2_2 write;
|
|
connection reader;
|
|
send
|
|
insert t1 select * from t2;
|
|
connection locker;
|
|
# Sleep a bit till the insert of connection reader is in work and hangs
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Table lock" and info = "insert t1 select * from t2";
|
|
--source include/wait_condition.inc
|
|
drop table t2;
|
|
connection reader;
|
|
--error ER_NO_SUCH_TABLE
|
|
reap;
|
|
connection locker;
|
|
drop table t1;
|
|
|
|
|
|
--echo End of 4.1 tests
|
|
|
|
#
|
|
# Bug#9998 MySQL client hangs on USE "database"
|
|
#
|
|
create table t1(a int);
|
|
lock tables t1 write;
|
|
connection reader;
|
|
show columns from t1;
|
|
connection locker;
|
|
unlock tables;
|
|
drop table t1;
|
|
|
|
#
|
|
# Bug#16986 Deadlock condition with MyISAM tables
|
|
#
|
|
|
|
# Need a matching user in mysql.user for multi-table select
|
|
--source include/add_anonymous_users.inc
|
|
|
|
connection locker;
|
|
USE mysql;
|
|
LOCK TABLES columns_priv WRITE, db WRITE, host WRITE, user WRITE;
|
|
FLUSH TABLES;
|
|
#
|
|
connection reader;
|
|
USE mysql;
|
|
# Note: This must be a multi-table select, otherwise the deadlock will not occur
|
|
send
|
|
SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1;
|
|
#
|
|
connection locker;
|
|
# Sleep a bit till the select of connection reader is in work and hangs
|
|
let $wait_condition=
|
|
SELECT COUNT(*) = 1 FROM information_schema.processlist
|
|
WHERE state = "Table lock" AND info =
|
|
"SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1";
|
|
--source include/wait_condition.inc
|
|
# Make test case independent from earlier grants.
|
|
--replace_result "Table is already up to date" "OK"
|
|
OPTIMIZE TABLES columns_priv, db, host, user;
|
|
UNLOCK TABLES;
|
|
#
|
|
connection reader;
|
|
reap;
|
|
USE test;
|
|
#
|
|
connection locker;
|
|
use test;
|
|
#
|
|
connection default;
|
|
#
|
|
# Test if CREATE TABLE with LOCK TABLE deadlocks.
|
|
#
|
|
connection writer;
|
|
CREATE TABLE t1 (c1 int);
|
|
LOCK TABLE t1 WRITE;
|
|
#
|
|
# This waits until t1 is unlocked.
|
|
connection locker;
|
|
send
|
|
FLUSH TABLES WITH READ LOCK;
|
|
#
|
|
connection writer;
|
|
# Sleep a bit till the flush of connection locker is in work and hangs
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Waiting for table" and info = "FLUSH TABLES WITH READ LOCK";
|
|
--source include/wait_condition.inc
|
|
# This must not block.
|
|
CREATE TABLE t2 (c1 int);
|
|
UNLOCK TABLES;
|
|
#
|
|
# This awakes now.
|
|
connection locker;
|
|
reap;
|
|
UNLOCK TABLES;
|
|
#
|
|
connection default;
|
|
DROP TABLE t1, t2;
|
|
#
|
|
# Test if CREATE TABLE SELECT with LOCK TABLE deadlocks.
|
|
#
|
|
connection writer;
|
|
CREATE TABLE t1 (c1 int);
|
|
LOCK TABLE t1 WRITE;
|
|
#
|
|
# This waits until t1 is unlocked.
|
|
connection locker;
|
|
send
|
|
FLUSH TABLES WITH READ LOCK;
|
|
#
|
|
# This must not block.
|
|
connection writer;
|
|
# Sleep a bit till the flush of connection locker is in work and hangs
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Waiting for table" and info = "FLUSH TABLES WITH READ LOCK";
|
|
--source include/wait_condition.inc
|
|
--error ER_TABLE_NOT_LOCKED
|
|
CREATE TABLE t2 AS SELECT * FROM t1;
|
|
UNLOCK TABLES;
|
|
#
|
|
# This awakes now.
|
|
connection locker;
|
|
reap;
|
|
UNLOCK TABLES;
|
|
#
|
|
connection default;
|
|
DROP TABLE t1;
|
|
|
|
--source include/delete_anonymous_users.inc
|
|
|
|
#
|
|
# Bug#19815 CREATE/RENAME/DROP DATABASE can deadlock on a global read lock
|
|
#
|
|
connect (con1,localhost,root,,);
|
|
connect (con2,localhost,root,,);
|
|
#
|
|
connection con1;
|
|
CREATE DATABASE mysqltest_1;
|
|
FLUSH TABLES WITH READ LOCK;
|
|
#
|
|
# With bug in place: acquire LOCK_mysql_create_table and
|
|
# wait in wait_if_global_read_lock().
|
|
connection con2;
|
|
send
|
|
DROP DATABASE mysqltest_1;
|
|
#
|
|
# With bug in place: try to acquire LOCK_mysql_create_table...
|
|
# When fixed: Reject dropping db because of the read lock.
|
|
connection con1;
|
|
# Wait a bit so that the session con2 is in state "Waiting for release of readlock"
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Waiting for release of readlock"
|
|
and info = "DROP DATABASE mysqltest_1";
|
|
--source include/wait_condition.inc
|
|
--error ER_CANT_UPDATE_WITH_READLOCK
|
|
DROP DATABASE mysqltest_1;
|
|
UNLOCK TABLES;
|
|
#
|
|
connection con2;
|
|
reap;
|
|
#
|
|
connection default;
|
|
disconnect con1;
|
|
disconnect con2;
|
|
# This must have been dropped by connection 2 already,
|
|
# which waited until the global read lock was released.
|
|
--error ER_DB_DROP_EXISTS
|
|
DROP DATABASE mysqltest_1;
|
|
|
|
#
|
|
# Bug#17264 MySQL Server freeze
|
|
#
|
|
connection locker;
|
|
# Disable warnings to allow test to run also without InnoDB
|
|
--disable_warnings
|
|
create table t1 (f1 int(12) unsigned not null auto_increment, primary key(f1)) engine=innodb;
|
|
--enable_warnings
|
|
lock tables t1 write;
|
|
connection writer;
|
|
send
|
|
alter table t1 auto_increment=0;
|
|
connection reader;
|
|
# Wait till connection writer is blocked
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Table lock" and info = "alter table t1 auto_increment=0";
|
|
--source include/wait_condition.inc
|
|
send
|
|
alter table t1 auto_increment=0;
|
|
connection locker;
|
|
# Wait till connection reader is blocked
|
|
let $wait_condition=
|
|
select count(*) = 2 from information_schema.processlist
|
|
where state = "Table lock" and info = "alter table t1 auto_increment=0";
|
|
--source include/wait_condition.inc
|
|
unlock tables;
|
|
connection writer;
|
|
reap;
|
|
connection reader;
|
|
reap;
|
|
connection locker;
|
|
drop table t1;
|
|
|
|
#
|
|
# Bug#43230: SELECT ... FOR UPDATE can hang with FLUSH TABLES WITH READ LOCK indefinitely
|
|
#
|
|
|
|
connect (con1,localhost,root,,);
|
|
connect (con2,localhost,root,,);
|
|
connect (con3,localhost,root,,);
|
|
connect (con4,localhost,root,,);
|
|
connect (con5,localhost,root,,);
|
|
|
|
create table t1 (a int);
|
|
create table t2 like t1;
|
|
|
|
connection con1;
|
|
--echo # con1
|
|
lock tables t1 write;
|
|
connection con2;
|
|
--echo # con2
|
|
send flush tables with read lock;
|
|
connection con5;
|
|
--echo # con5
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Waiting for table" and info = "flush tables with read lock";
|
|
--source include/wait_condition.inc
|
|
--echo # global read lock is taken
|
|
connection con3;
|
|
--echo # con3
|
|
send select * from t2 for update;
|
|
connection con5;
|
|
let $show_statement= SHOW PROCESSLIST;
|
|
let $field= State;
|
|
let $condition= = 'Waiting for release of readlock';
|
|
--source include/wait_show_condition.inc
|
|
--echo # waiting for release of read lock
|
|
connection con4;
|
|
--echo # con4
|
|
--echo # would hang and later cause a deadlock
|
|
flush tables t2;
|
|
connection con1;
|
|
--echo # clean up
|
|
unlock tables;
|
|
connection con2;
|
|
--reap
|
|
unlock tables;
|
|
connection con3;
|
|
--reap
|
|
connection default;
|
|
disconnect con5;
|
|
disconnect con4;
|
|
disconnect con3;
|
|
disconnect con2;
|
|
disconnect con1;
|
|
|
|
drop table t1,t2;
|
|
|
|
--echo #
|
|
--echo # Lightweight version:
|
|
--echo # Ensure that the wait for a GRL is done before opening tables.
|
|
--echo #
|
|
|
|
connect (con1,localhost,root,,);
|
|
connect (con2,localhost,root,,);
|
|
|
|
create table t1 (a int);
|
|
create table t2 like t1;
|
|
|
|
--echo #
|
|
--echo # UPDATE
|
|
--echo #
|
|
|
|
connection default;
|
|
--echo # default
|
|
flush tables with read lock;
|
|
connection con1;
|
|
--echo # con1
|
|
send update t2 set a = 1;
|
|
connection default;
|
|
--echo # default
|
|
let $show_statement= SHOW PROCESSLIST;
|
|
let $field= State;
|
|
let $condition= = 'Waiting for release of readlock';
|
|
--source include/wait_show_condition.inc
|
|
--echo # statement is waiting for release of read lock
|
|
connection con2;
|
|
--echo # con2
|
|
flush table t2;
|
|
connection default;
|
|
--echo # default
|
|
unlock tables;
|
|
connection con1;
|
|
--echo # con1
|
|
--reap
|
|
|
|
--echo #
|
|
--echo # LOCK TABLES .. WRITE
|
|
--echo #
|
|
|
|
connection default;
|
|
--echo # default
|
|
flush tables with read lock;
|
|
connection con1;
|
|
--echo # con1
|
|
send lock tables t2 write;
|
|
connection default;
|
|
--echo # default
|
|
let $show_statement= SHOW PROCESSLIST;
|
|
let $field= State;
|
|
let $condition= = 'Waiting for release of readlock';
|
|
--source include/wait_show_condition.inc
|
|
--echo # statement is waiting for release of read lock
|
|
connection con2;
|
|
--echo # con2
|
|
flush table t2;
|
|
connection default;
|
|
--echo # default
|
|
unlock tables;
|
|
connection con1;
|
|
--echo # con1
|
|
--reap
|
|
unlock tables;
|
|
|
|
connection default;
|
|
disconnect con2;
|
|
disconnect con1;
|
|
|
|
drop table t1,t2;
|
|
|
|
|
|
--echo End of 5.0 tests
|
|
|
|
|
|
#
|
|
# Bug#21281 Pending write lock is incorrectly removed when its
|
|
# statement being KILLed
|
|
#
|
|
create table t1 (i int);
|
|
connection locker;
|
|
lock table t1 read;
|
|
connection writer;
|
|
send
|
|
update t1 set i= 10;
|
|
connection reader;
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Table lock" and info = "update t1 set i= 10";
|
|
--source include/wait_condition.inc
|
|
send
|
|
select * from t1;
|
|
connection default;
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Table lock" and info = "select * from t1";
|
|
--source include/wait_condition.inc
|
|
let $ID= `select id from information_schema.processlist where state = "Table lock" and info = "update t1 set i= 10"`;
|
|
--replace_result $ID ID
|
|
eval kill query $ID;
|
|
connection reader;
|
|
--reap
|
|
connection writer;
|
|
--error ER_QUERY_INTERRUPTED
|
|
--reap
|
|
connection locker;
|
|
unlock tables;
|
|
connection default;
|
|
drop table t1;
|
|
|
|
#
|
|
# Bug#25856 HANDLER table OPEN in one connection lock DROP TABLE in another one
|
|
#
|
|
--disable_warnings
|
|
drop table if exists t1;
|
|
--enable_warnings
|
|
create table t1 (a int) ENGINE=MEMORY;
|
|
--echo --> client 2
|
|
connection locker;
|
|
--error ER_ILLEGAL_HA
|
|
handler t1 open;
|
|
--echo --> client 1
|
|
connection default;
|
|
drop table t1;
|
|
|
|
|
|
# Disconnect sessions used in many subtests above
|
|
disconnect locker;
|
|
disconnect reader;
|
|
disconnect writer;
|
|
|
|
|
|
#
|
|
# Bug#32395 Alter table under a impending global read lock causes a server crash
|
|
#
|
|
|
|
#
|
|
# Test ALTER TABLE under LOCK TABLES and FLUSH TABLES WITH READ LOCK
|
|
#
|
|
|
|
--disable_warnings
|
|
drop table if exists t1;
|
|
--enable_warnings
|
|
create table t1 (i int);
|
|
connect (flush,localhost,root,,test,,);
|
|
connection default;
|
|
--echo connection: default
|
|
lock tables t1 write;
|
|
connection flush;
|
|
--echo connection: flush
|
|
--send flush tables with read lock;
|
|
connection default;
|
|
--echo connection: default
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Waiting for table";
|
|
--source include/wait_condition.inc
|
|
alter table t1 add column j int;
|
|
connect (insert,localhost,root,,test,,);
|
|
connection insert;
|
|
--echo connection: insert
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Waiting for table";
|
|
--source include/wait_condition.inc
|
|
--send insert into t1 values (1,2);
|
|
--echo connection: default
|
|
connection default;
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Waiting for release of readlock";
|
|
--source include/wait_condition.inc
|
|
unlock tables;
|
|
connection flush;
|
|
--echo connection: flush
|
|
--reap
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Waiting for release of readlock";
|
|
--source include/wait_condition.inc
|
|
select * from t1;
|
|
unlock tables;
|
|
connection insert;
|
|
--reap
|
|
connection default;
|
|
let $wait_condition=
|
|
select count(*) = 1 from t1;
|
|
--source include/wait_condition.inc
|
|
select * from t1;
|
|
drop table t1;
|
|
disconnect flush;
|
|
disconnect insert;
|
|
|
|
#
|
|
# Test that FLUSH TABLES under LOCK TABLES protects write locked tables
|
|
# from a impending FLUSH TABLES WITH READ LOCK
|
|
#
|
|
|
|
--disable_warnings
|
|
drop table if exists t1;
|
|
--enable_warnings
|
|
create table t1 (i int);
|
|
connect (flush,localhost,root,,test,,);
|
|
connection default;
|
|
--echo connection: default
|
|
lock tables t1 write;
|
|
connection flush;
|
|
--echo connection: flush
|
|
--send flush tables with read lock;
|
|
connection default;
|
|
--echo connection: default
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Waiting for table";
|
|
--source include/wait_condition.inc
|
|
flush tables;
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Waiting for table";
|
|
--source include/wait_condition.inc
|
|
unlock tables;
|
|
connection flush;
|
|
--reap
|
|
connection default;
|
|
disconnect flush;
|
|
drop table t1;
|
|
|
|
#
|
|
# Bug#30331 Table_locks_waited shows inaccurate values
|
|
#
|
|
|
|
--disable_warnings
|
|
drop table if exists t1,t2;
|
|
--enable_warnings
|
|
create table t1 (a int);
|
|
flush status;
|
|
lock tables t1 read;
|
|
let $tlwa= `show status like 'Table_locks_waited'`;
|
|
connect (waiter,localhost,root,,);
|
|
connection waiter;
|
|
send insert into t1 values(1);
|
|
connection default;
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Table lock" and info = "insert into t1 values(1)";
|
|
--source include/wait_condition.inc
|
|
let $tlwb= `show status like 'Table_locks_waited'`;
|
|
unlock tables;
|
|
drop table t1;
|
|
disconnect waiter;
|
|
connection default;
|
|
--disable_query_log
|
|
eval SET @tlwa= SUBSTRING_INDEX('$tlwa', ' ', -1);
|
|
eval SET @tlwb= SUBSTRING_INDEX('$tlwb', ' ', -1);
|
|
--enable_query_log
|
|
select @tlwa < @tlwb;
|
|
|
|
--echo End of 5.1 tests
|
|
|
|
#
|
|
# Test that DROP TABLES does not wait for a impending FLUSH TABLES
|
|
# WITH READ LOCK
|
|
#
|
|
|
|
--disable_warnings
|
|
drop table if exists t1;
|
|
--enable_warnings
|
|
create table t1 (i int);
|
|
connect (flush,localhost,root,,test,,);
|
|
connection default;
|
|
--echo connection: default
|
|
lock tables t1 write;
|
|
connection flush;
|
|
--echo connection: flush
|
|
--send flush tables with read lock;
|
|
connection default;
|
|
--echo connection: default
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Waiting for table";
|
|
--source include/wait_condition.inc
|
|
flush tables;
|
|
let $wait_condition=
|
|
select count(*) = 1 from information_schema.processlist
|
|
where state = "Waiting for table";
|
|
--source include/wait_condition.inc
|
|
drop table t1;
|
|
connection flush;
|
|
--reap
|
|
connection default;
|
|
disconnect flush;
|
|
# Wait till all disconnects are completed
|
|
--source include/wait_until_count_sessions.inc
|