List:Commits« Previous MessageNext Message »
From:Li-Bing.Song Date:October 16 2010 12:03pm
Subject:bzr commit into mysql-5.1-bugteam branch (Li-Bing.Song:3530) Bug#56118
View as plain text  
#At file:///home/anders/work/bzrwork1/wt5/mysql-5.1-bugteam/ based on revid:luis.soares@stripped

 3530 Li-Bing.Song@stripped	2010-10-16
      Bug#56118 STOP SLAVE does not wait till trx with CREATE TMP TABLE ends,
                replication aborts
      
      When recieving a 'SLAVE STOP' command, slave SQL thread will roll back the
      transaction and stop immidiately if there is only transactional table updated,
      even through 'CREATE|DROP TEMPOARY TABLE' statement are in it. But These
      statements can never be rolled back. Because the temporary tables to the user
      session mapping remain until 'RESET SLAVE', Therefore it will abort SQL thread
      with an error that the table already exists or doesn't exist, when it restarts
      and executes the whole transaction again.
      
      After this patch, SQL thread always waits till the transaction ends and then stops,
      if 'CREATE|DROP TEMPOARY TABLE' statement are in it.
     @ mysql-test/extra/rpl_tests/rpl_stop_slave.test
        Auxiliary file which is used to test this bug.
     @ mysql-test/suite/rpl/t/rpl_stop_slave.test
        Test case for this bug.
     @ sql/slave.cc
        Checking if OPTION_KEEP_LOG is set. If it is set, SQL thread should wait
        until the transaction ends.
     @ sql/sql_parse.cc
        Add a debug point for testing this bug.

    added:
      mysql-test/extra/rpl_tests/rpl_stop_slave.test
      mysql-test/suite/rpl/r/rpl_stop_slave.result
      mysql-test/suite/rpl/t/rpl_stop_slave.test
    modified:
      sql/slave.cc
      sql/sql_parse.cc
=== added file 'mysql-test/extra/rpl_tests/rpl_stop_slave.test'
--- a/mysql-test/extra/rpl_tests/rpl_stop_slave.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/extra/rpl_tests/rpl_stop_slave.test	2010-10-16 12:03:44 +0000
@@ -0,0 +1,61 @@
+#
+# Auxiliary file which is used to test BUG#56118 
+#
+# Slave should apply all statements in the transaction before stop if any
+# temporary table is created or dropped.
+#
+# USEAGE: 
+# --let $tmp_table_stm= a SQL statement 
+# --source extra/rpl_tests/rpl_stop_slave.test
+#
+
+if (`SELECT "$tmp_table_stm" = ''`)
+{
+  --echo \$tmp_table_stm is NULL
+  --die $tmp_table_stm is NULL
+}
+
+--echo
+--echo [ On Master ]
+connection master;
+BEGIN;
+DELETE FROM t1;
+eval $tmp_table_stm;
+INSERT INTO t1 VALUES (1);
+DROP TEMPORARY TABLE tt1;
+COMMIT;
+
+--echo
+--echo [ On Slave ]
+connection slave;
+
+# To check if slave SQL thread is applying INSERT statement
+let $show_statement= SHOW PROCESSLIST;
+let $field= Info;
+let $condition= LIKE 'INSERT%';
+source include/wait_show_condition.inc;
+
+send STOP SLAVE SQL_THREAD;
+
+--echo
+--echo [ On Slave1 ]
+connection slave1;
+--echo # To resume slave SQL thread
+SET DEBUG_SYNC= 'now SIGNAL signal.continue';
+SET DEBUG_SYNC= 'RESET';
+
+--echo
+--echo [ On Slave ]
+connection slave;
+reap;
+source include/wait_for_slave_sql_to_stop.inc;
+
+--echo # Slave should stop after the transaction has committed.
+--echo # So t1 on master is same to t1 on slave.
+let diff_table_1=master:test.t1;
+let diff_table_2=slave:test.t1;
+source include/diff_tables.inc;
+
+connection slave;
+START SLAVE SQL_THREAD;
+source include/wait_for_slave_sql_to_start.inc;

=== added file 'mysql-test/suite/rpl/r/rpl_stop_slave.result'
--- a/mysql-test/suite/rpl/r/rpl_stop_slave.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/r/rpl_stop_slave.result	2010-10-16 12:03:44 +0000
@@ -0,0 +1,127 @@
+stop slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+reset master;
+reset slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+start slave;
+
+# BUG#56118 STOP SLAVE does not wait till trx with CREATE TMP TABLE ends
+#
+# If a temporary table is created or dropped, the transaction should be
+# regarded similarly that a non-transactional table is modified. So 
+# STOP SLAVE should wait until the transaction has finished.
+CREATE TABLE t1(c1 INT) ENGINE=InnoDB;
+CREATE TABLE t2(c1 INT) ENGINE=InnoDB;
+SET DEBUG_SYNC= 'RESET';
+include/stop_slave.inc
+
+# Suspend the INSERT statement in current transaction on SQL thread.
+# It guarantees that SQL thread is applying the transaction when
+# STOP SLAVE command launchs.
+SET GLOBAL debug= 'd,after_mysql_insert';
+include/start_slave.inc
+
+# CREATE TEMPORARY TABLE with InnoDB engine
+# -----------------------------------------
+
+[ On Master ]
+BEGIN;
+DELETE FROM t1;
+CREATE TEMPORARY TABLE tt1(c1 INT) ENGINE = InnoDB;
+INSERT INTO t1 VALUES (1);
+DROP TEMPORARY TABLE tt1;
+COMMIT;
+
+[ On Slave ]
+STOP SLAVE SQL_THREAD;
+
+[ On Slave1 ]
+# To resume slave SQL thread
+SET DEBUG_SYNC= 'now SIGNAL signal.continue';
+SET DEBUG_SYNC= 'RESET';
+
+[ On Slave ]
+# Slave should stop after the transaction has committed.
+# So t1 on master is same to t1 on slave.
+Comparing tables master:test.t1 and slave:test.t1
+START SLAVE SQL_THREAD;
+
+# CREATE TEMPORARY TABLE with MyISAM engine
+# -----------------------------------------
+
+[ On Master ]
+BEGIN;
+DELETE FROM t1;
+CREATE TEMPORARY TABLE tt1(c1 INT) ENGINE = MyISAM;
+INSERT INTO t1 VALUES (1);
+DROP TEMPORARY TABLE tt1;
+COMMIT;
+
+[ On Slave ]
+STOP SLAVE SQL_THREAD;
+
+[ On Slave1 ]
+# To resume slave SQL thread
+SET DEBUG_SYNC= 'now SIGNAL signal.continue';
+SET DEBUG_SYNC= 'RESET';
+
+[ On Slave ]
+# Slave should stop after the transaction has committed.
+# So t1 on master is same to t1 on slave.
+Comparing tables master:test.t1 and slave:test.t1
+START SLAVE SQL_THREAD;
+
+# CREATE TEMPORARY TABLE ... SELECT with InnoDB engine
+# ----------------------------------------------------
+
+[ On Master ]
+BEGIN;
+DELETE FROM t1;
+CREATE TEMPORARY TABLE tt1(c1 INT) ENGINE = InnoDB
+SELECT c1 FROM t2;
+INSERT INTO t1 VALUES (1);
+DROP TEMPORARY TABLE tt1;
+COMMIT;
+
+[ On Slave ]
+STOP SLAVE SQL_THREAD;
+
+[ On Slave1 ]
+# To resume slave SQL thread
+SET DEBUG_SYNC= 'now SIGNAL signal.continue';
+SET DEBUG_SYNC= 'RESET';
+
+[ On Slave ]
+# Slave should stop after the transaction has committed.
+# So t1 on master is same to t1 on slave.
+Comparing tables master:test.t1 and slave:test.t1
+START SLAVE SQL_THREAD;
+
+# CREATE TEMPORARY TABLE ... SELECT with MyISAM engine
+# ----------------------------------------------------
+
+[ On Master ]
+BEGIN;
+DELETE FROM t1;
+CREATE TEMPORARY TABLE tt1(c1 INT) ENGINE = MyISAM
+SELECT 1 AS c1;
+INSERT INTO t1 VALUES (1);
+DROP TEMPORARY TABLE tt1;
+COMMIT;
+
+[ On Slave ]
+STOP SLAVE SQL_THREAD;
+
+[ On Slave1 ]
+# To resume slave SQL thread
+SET DEBUG_SYNC= 'now SIGNAL signal.continue';
+SET DEBUG_SYNC= 'RESET';
+
+[ On Slave ]
+# Slave should stop after the transaction has committed.
+# So t1 on master is same to t1 on slave.
+Comparing tables master:test.t1 and slave:test.t1
+START SLAVE SQL_THREAD;
+# Test end
+SET GLOBAL debug= '$debug_save';
+DROP TABLE t1, t2;

=== added file 'mysql-test/suite/rpl/t/rpl_stop_slave.test'
--- a/mysql-test/suite/rpl/t/rpl_stop_slave.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_stop_slave.test	2010-10-16 12:03:44 +0000
@@ -0,0 +1,60 @@
+source include/master-slave.inc;
+source include/have_innodb.inc;
+source include/have_debug.inc;
+source include/have_debug_sync.inc;
+source include/have_binlog_format_mixed_or_statement.inc;
+
+--echo
+--echo # BUG#56118 STOP SLAVE does not wait till trx with CREATE TMP TABLE ends
+--echo #
+--echo # If a temporary table is created or dropped, the transaction should be
+--echo # regarded similarly that a non-transactional table is modified. So 
+--echo # STOP SLAVE should wait until the transaction has finished.
+
+CREATE TABLE t1(c1 INT) ENGINE=InnoDB;
+CREATE TABLE t2(c1 INT) ENGINE=InnoDB;
+
+sync_slave_with_master;
+SET DEBUG_SYNC= 'RESET';
+source include/stop_slave.inc;
+
+--echo
+--echo # Suspend the INSERT statement in current transaction on SQL thread.
+--echo # It guarantees that SQL thread is applying the transaction when
+--echo # STOP SLAVE command launchs.
+let $debug_save= `SELECT @@GLOBAL.debug`;
+SET GLOBAL debug= 'd,after_mysql_insert';
+source include/start_slave.inc;
+
+--echo
+--echo # CREATE TEMPORARY TABLE with InnoDB engine
+--echo # -----------------------------------------
+let $tmp_table_stm= CREATE TEMPORARY TABLE tt1(c1 INT) ENGINE = InnoDB;
+source extra/rpl_tests/rpl_stop_slave.test;
+
+--echo
+--echo # CREATE TEMPORARY TABLE with MyISAM engine
+--echo # -----------------------------------------
+let $tmp_table_stm= CREATE TEMPORARY TABLE tt1(c1 INT) ENGINE = MyISAM;
+source extra/rpl_tests/rpl_stop_slave.test;
+
+--echo
+--echo # CREATE TEMPORARY TABLE ... SELECT with InnoDB engine
+--echo # ----------------------------------------------------
+let $tmp_table_stm= CREATE TEMPORARY TABLE tt1(c1 INT) ENGINE = InnoDB
+                    SELECT c1 FROM t2;
+source extra/rpl_tests/rpl_stop_slave.test;
+
+--echo
+--echo # CREATE TEMPORARY TABLE ... SELECT with MyISAM engine
+--echo # ----------------------------------------------------
+let $tmp_table_stm= CREATE TEMPORARY TABLE tt1(c1 INT) ENGINE = MyISAM
+                    SELECT 1 AS c1;
+source extra/rpl_tests/rpl_stop_slave.test;
+
+--echo # Test end
+SET GLOBAL debug= '$debug_save';
+
+connection master;
+DROP TABLE t1, t2;
+source include/master-slave-end.inc;

=== modified file 'sql/slave.cc'
--- a/sql/slave.cc	2010-10-13 07:26:50 +0000
+++ b/sql/slave.cc	2010-10-16 12:03:44 +0000
@@ -740,8 +740,17 @@ static bool sql_slave_killed(THD* thd, R
   DBUG_ASSERT(rli->slave_running == 1);// tracking buffer overrun
   if (abort_loop || thd->killed || rli->abort_slave)
   {
+    /*
+      The transaction should always be binlogged if OPTION_KEEP_LOG is set
+      (it implies that something can not be rolled back). And such case
+      should be regarded similarly as modifing a non-transactional table
+      because retrying of the transaction will lead to an error or inconsistency
+      as well.
+      Example: OPTION_KEEP_LOG is set if a temporary table is created or dropped.
+    */
     if (rli->abort_slave && rli->is_in_group() &&
-        thd->transaction.all.modified_non_trans_table)
+        (thd->transaction.all.modified_non_trans_table ||
+         (thd->options & OPTION_KEEP_LOG)))
       DBUG_RETURN(0);
     /*
       If we are in an unsafe situation (stopping could corrupt replication),

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-08-18 04:56:06 +0000
+++ b/sql/sql_parse.cc	2010-10-16 12:03:44 +0000
@@ -27,6 +27,7 @@
 #include "sp_cache.h"
 #include "events.h"
 #include "sql_trigger.h"
+#include "debug_sync.h"
 
 /**
   @defgroup Runtime_Environment Runtime Environment
@@ -3258,6 +3259,15 @@ end_with_restore_list:
       thd->first_successful_insert_id_in_cur_stmt=
         thd->first_successful_insert_id_in_prev_stmt;
 
+    DBUG_EXECUTE_IF("after_mysql_insert",
+                    {
+                      const char act[]=
+                        "now "
+                        "wait_for signal.continue";
+                      DBUG_ASSERT(opt_debug_sync_timeout > 0);
+                      DBUG_ASSERT(!debug_sync_set_action(current_thd,
+                                                         STRING_WITH_LEN(act)));
+                    };);
     break;
   }
   case SQLCOM_REPLACE_SELECT:


Attachment: [text/bzr-bundle] bzr/li-bing.song@sun.com-20101016120344-y60y4iiecisblytt.bundle
Thread
bzr commit into mysql-5.1-bugteam branch (Li-Bing.Song:3530) Bug#56118Li-Bing.Song16 Oct