mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
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.
This commit is contained in:
parent
5f31581fff
commit
131e3e38fd
5 changed files with 268 additions and 1 deletions
61
mysql-test/extra/rpl_tests/rpl_stop_slave.test
Normal file
61
mysql-test/extra/rpl_tests/rpl_stop_slave.test
Normal file
|
@ -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;
|
127
mysql-test/suite/rpl/r/rpl_stop_slave.result
Normal file
127
mysql-test/suite/rpl/r/rpl_stop_slave.result
Normal file
|
@ -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;
|
60
mysql-test/suite/rpl/t/rpl_stop_slave.test
Normal file
60
mysql-test/suite/rpl/t/rpl_stop_slave.test
Normal file
|
@ -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;
|
11
sql/slave.cc
11
sql/slave.cc
|
@ -740,8 +740,17 @@ static bool sql_slave_killed(THD* thd, Relay_log_info* rli)
|
|||
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),
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue