From def3c98af47a95981437de3ccee8bc1a736cd4a7 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 14 Nov 2013 15:08:29 +0100 Subject: [PATCH] MDEV-5291: Slave transaction retry on temporary error leaves dangling error in SHOW SLAVE STATUS Make sure to clear the temporary error before the retry. --- .../suite/rpl/r/rpl_temporary_error2.result | 55 +++++++++++++ .../suite/rpl/t/rpl_temporary_error2.test | 77 +++++++++++++++++++ sql/slave.cc | 1 + 3 files changed, 133 insertions(+) create mode 100644 mysql-test/suite/rpl/r/rpl_temporary_error2.result create mode 100644 mysql-test/suite/rpl/t/rpl_temporary_error2.test diff --git a/mysql-test/suite/rpl/r/rpl_temporary_error2.result b/mysql-test/suite/rpl/r/rpl_temporary_error2.result new file mode 100644 index 00000000000..7c7663b8824 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_temporary_error2.result @@ -0,0 +1,55 @@ +include/master-slave.inc +[connection master] +*** Provoke a deadlock on the slave, check that transaction retry succeeds. *** +CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB; +CREATE TABLE t2 (a INT) ENGINE=InnoDB; +INSERT INTO t1(a) VALUES (1), (2), (3), (4), (5); +SELECT * FROM t1 ORDER BY a; +a b +1 NULL +2 NULL +3 NULL +4 NULL +5 NULL +SET sql_log_bin=0; +ALTER TABLE t2 ENGINE=MyISAM; +SET sql_log_bin=1; +BEGIN; +UPDATE t1 SET b=2 WHERE a=4; +INSERT INTO t2 VALUES (2); +DELETE FROM t2 WHERE a=2; +BEGIN; +UPDATE t1 SET b=1 WHERE a=2; +INSERT INTO t2 VALUES (1); +UPDATE t1 SET b=1 WHERE a=4; +COMMIT; +UPDATE t1 SET b=2 WHERE a=2; +SELECT * FROM t1 WHERE a<10 ORDER BY a; +a b +1 NULL +2 2 +3 NULL +4 2 +5 NULL +ROLLBACK; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +SELECT * FROM t1 ORDER BY a; +a b +1 NULL +2 1 +3 NULL +4 1 +5 NULL +* There will be two rows in t2 due to the retry. +SELECT * FROM t2 ORDER BY a; +a +1 +1 +retries +1 +Last_SQL_Errno = '0' +Last_SQL_Error = '' +DROP TABLE t1; +DROP TABLE t2; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_temporary_error2.test b/mysql-test/suite/rpl/t/rpl_temporary_error2.test new file mode 100644 index 00000000000..b4fbca7113b --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_temporary_error2.test @@ -0,0 +1,77 @@ +--source include/have_innodb.inc +--source include/master-slave.inc + +--echo *** Provoke a deadlock on the slave, check that transaction retry succeeds. *** +--connection master +CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB; +CREATE TABLE t2 (a INT) ENGINE=InnoDB; +INSERT INTO t1(a) VALUES (1), (2), (3), (4), (5); + +--sync_slave_with_master +SELECT * FROM t1 ORDER BY a; +# Use MyISAM for t2 on the slave, so we have a way to see how far the +# slave replication thread has proceeded in the transaction. +SET sql_log_bin=0; +ALTER TABLE t2 ENGINE=MyISAM; +SET sql_log_bin=1; +let $old_retry= query_get_value(SHOW STATUS LIKE 'Slave_retried_transactions', Value, 1); + +# Setup a separate connection that can deadlock with the replication thread. +# Docs say that InnoDB will try to roll back the smaller transaction. So +# let us make this transaction a big one, so the one in the replication +# thread will be selected for rollback and retry. +--connect (con_temp1,127.0.0.1,root,,test,$SERVER_MYPORT_2,) +--connection con_temp1 +BEGIN; +UPDATE t1 SET b=2 WHERE a=4; +--disable_query_log +--let $count=200 +while ($count) +{ + eval INSERT INTO t1(a) VALUES ($count + 10); + dec $count; +} +--enable_query_log +# Note that InnoDB also (undocumented?) tries to avoid rolling back a +# "transaction" that modified non-transactional tables. So be sure to also +# touch the MyISAM table in this transaction. +INSERT INTO t2 VALUES (2); +DELETE FROM t2 WHERE a=2; + +# Create the transaction that should participate in the deadlock on the slave. +--connection master +BEGIN; +UPDATE t1 SET b=1 WHERE a=2; +INSERT INTO t2 VALUES (1); +UPDATE t1 SET b=1 WHERE a=4; +COMMIT; +--save_master_pos + +--connection slave +# Wait until replication thread has gone to wait on the a=4 row lock. +--let $wait_condition= SELECT COUNT(*) = 1 FROM t2 WHERE a=1 +--source include/wait_condition.inc + +# Now provoke the deadlock by waiting on the a=2 row lock while the +# other thread is waiting for our a=4 row lock. +--connection con_temp1 +UPDATE t1 SET b=2 WHERE a=2; +SELECT * FROM t1 WHERE a<10 ORDER BY a; +ROLLBACK; + +--connection slave +--sync_with_master +SELECT * FROM t1 ORDER BY a; +--echo * There will be two rows in t2 due to the retry. +SELECT * FROM t2 ORDER BY a; +let $new_retry= query_get_value(SHOW STATUS LIKE 'Slave_retried_transactions', Value, 1); +--disable_query_log +eval SELECT $new_retry - $old_retry AS retries; +--enable_query_log +--let $status_items= Last_SQL_Errno, Last_SQL_Error +--source include/show_slave_status.inc + +--connection master +DROP TABLE t1; +DROP TABLE t2; +--source include/rpl_end.inc diff --git a/sql/slave.cc b/sql/slave.cc index 1907e704e3d..76c9a100104 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3453,6 +3453,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli, if (exec_res && (temp_err= has_temporary_error(thd))) { const char *errmsg; + rli->clear_error(); /* We were in a transaction which has been rolled back because of a temporary error;