#At file:///export/home/z/mysql-6.0-codebase-bugfixing-bug46654/ based on revid:dlenev@stripped
2822 Jon Olav Hauglid 2009-09-17
Bug #46654 False deadlock on concurrent DML/DDL with partitions,
inconsistent behavior
This is a preliminary version of the patch.
The problem was that if one connection is running a multi-statement transaction
which involves a single partitioned table, and another connection attempts to
alter the table, the first connection gets ER_LOCK_DEADLOCK and cannot proceed
anymore, even when the ALTER TABLE statement in another connection is timed out
(which happens after innodb_lock_wait_timeout seconds).
The reason for this was that the prepare phase for ALTER TABLE for partitioned
tables removed the table from the table definition cache before it started
waiting on the lock. The transaction running in the first connection would
notice this and report ER_LOCK_DEADLOCK. The removal was unnecessary as it
also is done at a later point when the lock has been taken.
This patch changes the ALTER TABLE code so that tdc_remove_table() is only
called when the prepare phase fails.
Test case added in bug46654.test. The test will be merged into an existing
file before the final commit/push.
added:
mysql-test/r/bug46654.result
mysql-test/t/bug46654-master.opt
mysql-test/t/bug46654.test
modified:
sql/sql_partition.cc
sql/sql_table.cc
=== added file 'mysql-test/r/bug46654.result'
--- a/mysql-test/r/bug46654.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/bug46654.result 2009-09-17 07:31:09 +0000
@@ -0,0 +1,31 @@
+#
+# Bug #46654 False deadlock on concurrent DML/DDL
+# with partitions, inconsistent behavior
+#
+DROP TABLE IF EXISTS tbl_with_partitions;
+CREATE TABLE tbl_with_partitions ( i INT )
+ENGINE = InnoDB
+PARTITION BY HASH(i);
+# Connection 1
+# Insert with disabled autocommit
+SET AUTOCOMMIT = 0;
+INSERT INTO tbl_with_partitions VALUES (1);
+# Connection 2
+# Alter table, waits for lock
+ALTER TABLE tbl_with_partitions ADD COLUMN f INT;
+# Connection 1
+# Test 1: Insert while Alter table is waiting.
+INSERT INTO tbl_with_partitions VALUES (2);
+# Connection 2
+# Wait until Alter table times out
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+# Connection 1
+# Test 2: Insert after Alter table has timed out
+INSERT INTO tbl_with_partitions VALUES (3);
+# Connection 2
+# Test 3: Another Alter table, should wait and timeout.
+ALTER TABLE tbl_with_partitions ADD COLUMN f INT;
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+# Connection 1
+# Cleanup
+DROP TABLE tbl_with_partitions;
=== added file 'mysql-test/t/bug46654-master.opt'
--- a/mysql-test/t/bug46654-master.opt 1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/bug46654-master.opt 2009-09-17 07:31:09 +0000
@@ -0,0 +1 @@
+--innodb_lock_wait_timeout=2
=== added file 'mysql-test/t/bug46654.test'
--- a/mysql-test/t/bug46654.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/bug46654.test 2009-09-17 07:31:09 +0000
@@ -0,0 +1,66 @@
+--source include/have_innodb.inc
+--source include/have_partition.inc
+# Save the initial number of concurrent sessions.
+--source include/count_sessions.inc
+
+--echo #
+--echo # Bug #46654 False deadlock on concurrent DML/DDL
+--echo # with partitions, inconsistent behavior
+--echo #
+
+--disable_warnings
+DROP TABLE IF EXISTS tbl_with_partitions;
+--enable_warnings
+
+CREATE TABLE tbl_with_partitions ( i INT )
+ ENGINE = InnoDB
+ PARTITION BY HASH(i);
+
+connect(ddl,localhost,root);
+
+--echo # Connection 1
+--echo # Insert with disabled autocommit
+connection default;
+SET AUTOCOMMIT = 0;
+INSERT INTO tbl_with_partitions VALUES (1);
+
+--echo # Connection 2
+--echo # Alter table, waits for lock
+connection ddl;
+--send ALTER TABLE tbl_with_partitions ADD COLUMN f INT
+
+--echo # Connection 1
+--echo # Test 1: Insert while Alter table is waiting.
+connection default;
+let $wait_condition= SELECT COUNT(*)= 1 FROM information_schema.processlist
+ WHERE state= 'copy to tmp table' and
+ INFO='ALTER TABLE tbl_with_partitions ADD COLUMN f INT';
+--source include/wait_condition.inc
+INSERT INTO tbl_with_partitions VALUES (2);
+
+--echo # Connection 2
+--echo # Wait until Alter table times out
+connection ddl;
+--error ER_LOCK_WAIT_TIMEOUT
+--reap
+
+--echo # Connection 1
+--echo # Test 2: Insert after Alter table has timed out
+connection default;
+INSERT INTO tbl_with_partitions VALUES (3);
+
+--echo # Connection 2
+--echo # Test 3: Another Alter table, should wait and timeout.
+connection ddl;
+--error ER_LOCK_WAIT_TIMEOUT
+ALTER TABLE tbl_with_partitions ADD COLUMN f INT;
+
+--echo # Connection 1
+--echo # Cleanup
+connection default;
+disconnect ddl;
+DROP TABLE tbl_with_partitions;
+
+# 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
=== modified file 'sql/sql_partition.cc'
--- a/sql/sql_partition.cc 2009-09-11 10:45:04 +0000
+++ b/sql/sql_partition.cc 2009-09-17 07:31:09 +0000
@@ -3932,15 +3932,9 @@ bool mysql_unpack_partition(THD *thd,
We need to free any memory objects allocated on item_free_list
by the parser since we are keeping the old info from the first
parser call in CREATE TABLE.
- We'll ensure that this object isn't put into table cache also
- just to ensure we don't get into strange situations with the
- item objects.
*/
thd->free_items();
part_info= thd->work_part_info;
- tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
- table->s->db.str,
- table->s->table_name.str);
*work_part_info_used= true;
}
table->part_info= part_info;
@@ -4231,20 +4225,6 @@ uint prep_alter_part_table(THD *thd, TAB
{
DBUG_ENTER("prep_alter_part_table");
- /*
- We are going to manipulate the partition info on the table object
- so we need to ensure that the table instances cached and all other
- instances are properly closed.
- */
- if (table->part_info)
- {
- pthread_mutex_lock(&LOCK_open);
- tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
- table->s->db.str,
- table->s->table_name.str);
- pthread_mutex_unlock(&LOCK_open);
- }
-
thd->work_part_info= thd->lex->part_info;
if (thd->work_part_info &&
!(thd->work_part_info= thd->lex->part_info->get_clone()))
=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc 2009-09-16 15:43:00 +0000
+++ b/sql/sql_table.cc 2009-09-17 07:31:09 +0000
@@ -7195,6 +7195,18 @@ view_err:
if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
&partition_changed, &fast_alter_partition))
{
+ /*
+ If prepare fails, remove the table object as it may have been
+ changed before prepare failed.
+ */
+ if (table->part_info)
+ {
+ pthread_mutex_lock(&LOCK_open);
+ tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
+ table->s->db.str,
+ table->s->table_name.str);
+ pthread_mutex_unlock(&LOCK_open);
+ }
goto err;
}
#endif
Attachment: [text/bzr-bundle] bzr/jon.hauglid@sun.com-20090917073109-9ssv8dntkwg5g2aq.bundle