MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Dmitry Lenev Date:February 15 2010 10:23am
Subject:bzr commit into mysql-5.5-next-mr branch (dlenev:3096) Bug#51134
View as plain text  
#At file:///home/dlenev/src/bzr/mysql-next-4284-bg51134/ based on revid:dlenev@stripped

 3096 Dmitry Lenev	2010-02-15
      Fix for bug #51134 "Crash in MDL_lock::destroy on a concurrent 
      DDL workload".
      
      When a RENAME TABLE or LOCK TABLE ... WRITE statement which
      mentioned the same table several times were aborted during 
      the process of acquring metadata locks (due to deadlock 
      which was discovered or because of KILL statement) server 
      might have crashed.
      
      When attempt to acquire all locks requested had failed we
      went through the list of requests and released locks which
      we have managed to acquire by that moment one by one. Since 
      in the scenario described above list of requests contained 
      duplicates this led to releasing the same ticket twice and 
      a crash as result.
      
      This patch solves the problem by employing different approach
      to releasing locks in case of failure to acquire all locks
      requested. 
      Now we take a MDL savepoint before starting acquiring locks 
      and simply rollback to it if things go bad.
     @ mysql-test/r/lock_multi.result
        Updated test results (see lock_multi.test).
     @ mysql-test/t/lock_multi.test
        Added test case for bug #51134 "Crash in MDL_lock::destroy
        on a concurrent DDL workload".
     @ sql/mdl.cc
        MDL_context::acquire_locks():
          When attempt to acquire all locks requested has failed do
          not go through the list of requests and release locks which
          we have managed to acquire one by one. 
          Since list of requests can contain duplicates such approach
          may lead to releasing the same ticket twice and a crash as
          result.
          Instead use the following approach - take a MDL savepoint
          before starting acquiring locks and simply rollback to it
          if things go bad.

    modified:
      mysql-test/r/lock_multi.result
      mysql-test/t/lock_multi.test
      sql/mdl.cc
=== modified file 'mysql-test/r/lock_multi.result'
--- a/mysql-test/r/lock_multi.result	2010-02-11 10:23:39 +0000
+++ b/mysql-test/r/lock_multi.result	2010-02-15 10:23:34 +0000
@@ -436,3 +436,29 @@ UNLOCK TABLES;
 # Reaping: DROP TABLE t1, t2
 # Connection default
 # Cleanup
+#
+# Test for bug #51134 "Crash in MDL_lock::destroy on a concurrent
+#                      DDL workload".
+#
+drop tables if exists t1, t2, t3;
+create table t3 (i int);
+# Switching to connection 'con1'
+# Lock 't3' so upcoming RENAME is blocked.
+lock table t3 read;
+# Switching to connection 'con2'
+# Remember ID for this connection.
+# Start statement which will try to acquire two instances
+# of X metadata lock on the same object.
+# Sending:
+rename tables t1 to t2, t2 to t3;;
+# Switching to connection 'default'
+# Wait until RENAME TABLE is blocked on table 't3'.
+# Kill RENAME TABLE.
+kill query ID;
+# Switching to connection 'con2'
+# RENAME TABLE should be aborted but should not crash.
+ERROR 70100: Query execution was interrupted
+# Switching to connection 'con1'
+unlock tables;
+# Switching to connection 'default'
+drop table t3;

=== modified file 'mysql-test/t/lock_multi.test'
--- a/mysql-test/t/lock_multi.test	2010-02-11 10:23:39 +0000
+++ b/mysql-test/t/lock_multi.test	2010-02-15 10:23:34 +0000
@@ -1038,5 +1038,59 @@ disconnect con2;
 disconnect con3;
 
 
+--echo #
+--echo # Test for bug #51134 "Crash in MDL_lock::destroy on a concurrent
+--echo #                      DDL workload".
+--echo #
+--disable_warnings
+drop tables if exists t1, t2, t3;
+--enable_warnings
+connect (con1, localhost, root, , );
+connect (con2, localhost, root, , );
+connection default;
+create table t3 (i int);
+
+--echo # Switching to connection 'con1'
+connection con1;
+--echo # Lock 't3' so upcoming RENAME is blocked.
+lock table t3 read;
+
+--echo # Switching to connection 'con2'
+connection con2;
+--echo # Remember ID for this connection.
+let $ID= `select connection_id()`;
+--echo # Start statement which will try to acquire two instances
+--echo # of X metadata lock on the same object.
+--echo # Sending:
+--send rename tables t1 to t2, t2 to t3;
+
+--echo # Switching to connection 'default'
+connection default;
+--echo # Wait until RENAME TABLE is blocked on table 't3'.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for table" and info = "rename tables t1 to t2, t2 to t3";
+--source include/wait_condition.inc
+--echo # Kill RENAME TABLE.
+--replace_result $ID ID
+eval kill query $ID;
+
+--echo # Switching to connection 'con2'
+connection con2;
+--echo # RENAME TABLE should be aborted but should not crash.
+--error ER_QUERY_INTERRUPTED
+--reap
+
+--echo # Switching to connection 'con1'
+connection con1;
+unlock tables;
+
+--echo # Switching to connection 'default'
+connection default;
+disconnect con1;
+disconnect con2;
+drop table t3;
+
+
 # Wait till all disconnects are completed
 --source include/wait_until_count_sessions.inc

=== modified file 'sql/mdl.cc'
--- a/sql/mdl.cc	2010-02-11 10:23:39 +0000
+++ b/sql/mdl.cc	2010-02-15 10:23:34 +0000
@@ -1532,6 +1532,7 @@ bool MDL_context::acquire_locks(MDL_requ
 {
   MDL_request_list::Iterator it(*mdl_requests);
   MDL_request **sort_buf, **p_req;
+  MDL_ticket *mdl_svp= mdl_savepoint();
   ssize_t req_count= static_cast<ssize_t>(mdl_requests->elements());
 
   if (req_count == 0)
@@ -1565,12 +1566,16 @@ bool MDL_context::acquire_locks(MDL_requ
   return FALSE;
 
 err:
-  /* Release locks we have managed to acquire so far. */
+  /*
+    Release locks we have managed to acquire so far.
+    Use rollback_to_savepoint() since there may be duplicate
+    requests that got assigned the same ticket.
+  */
+  rollback_to_savepoint(mdl_svp);
+  /* Reset lock requests back to its initial state. */
   for (req_count= p_req - sort_buf, p_req= sort_buf;
        p_req < sort_buf + req_count; p_req++)
   {
-    release_lock((*p_req)->ticket);
-    /* Reset lock request back to its initial state. */
     (*p_req)->ticket= NULL;
   }
   my_free(sort_buf, MYF(0));


Attachment: [text/bzr-bundle] bzr/dlenev@mysql.com-20100215102334-owp3gtj3bruirc5b.bundle
Thread
bzr commit into mysql-5.5-next-mr branch (dlenev:3096) Bug#51134Dmitry Lenev15 Feb