[Commits] f5515d7: MDEV-6403: Temporary tables lost at STOP SLAVE in GTID mode if master has not rotated binlog since restart

Kristian Nielsen knielsen at knielsen-hq.org
Wed Mar 4 14:15:58 EET 2015


revision-id: f5515d7cf44c4680318219ae62d63e5bafcaefc2
parent(s): aa845d123c68e635fa43a4aec2bf2fdb8f5b0617
committer: Kristian Nielsen
branch nick: server
timestamp: 2015-03-04 13:10:37 +0100
message:

MDEV-6403: Temporary tables lost at STOP SLAVE in GTID mode if master has not rotated binlog since restart

The binlog contains specially marked format description events to mark
when a master restart happened (which could have caused temporary
tables to be silently dropped). Such events also cause slave to close
temporary tables.

However, there was a bug that if after this, slave re-connects to the
master in GTID mode, the master can send an old format description
event again. If temporary tables are closed when such event is seen
for the second time, it might drop temporary tables created after that
event, and cause replication failure.

With this patch, the restart flag of the format description event is
cleared by the master when it is sent to the slave in a subsequent
connection, to avoid the errorneous temp table close.

---
 mysql-test/suite/rpl/r/rpl_gtid_misc.result |   25 ++++++++++++++
 mysql-test/suite/rpl/t/rpl_gtid_misc.test   |   50 +++++++++++++++++++++++++++
 sql/rpl_gtid.cc                             |   25 ++++++++++++++
 sql/rpl_gtid.h                              |    1 +
 sql/sql_repl.cc                             |   25 ++++++++++++++
 5 files changed, 126 insertions(+)

diff --git a/mysql-test/suite/rpl/r/rpl_gtid_misc.result b/mysql-test/suite/rpl/r/rpl_gtid_misc.result
new file mode 100644
index 0000000..cdaac1b
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_gtid_misc.result
@@ -0,0 +1,25 @@
+include/master-slave.inc
+[connection master]
+*** MDEV-6403: Temporary tables lost at STOP SLAVE in GTID mode if master has not rotated binlog since restart ***
+CREATE TABLE t1 (a INT PRIMARY KEY);
+include/stop_slave.inc
+SET sql_log_bin= 0;
+INSERT INTO t1 VALUES (1);
+SET sql_log_bin= 1;
+CHANGE MASTER TO master_use_gtid= current_pos;
+CREATE TEMPORARY TABLE t2 LIKE t1;
+INSERT INTO t2 VALUE (1);
+INSERT INTO t1 SELECT * FROM t2;
+DROP TEMPORARY TABLE t2;
+START SLAVE;
+include/wait_for_slave_sql_error.inc [errno=1062]
+STOP SLAVE IO_THREAD;
+SET sql_log_bin= 0;
+DELETE FROM t1 WHERE a=1;
+SET sql_log_bin= 1;
+include/start_slave.inc
+SELECT * FROM t1 ORDER BY a;
+a
+1
+DROP TABLE t1;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_gtid_misc.test b/mysql-test/suite/rpl/t/rpl_gtid_misc.test
new file mode 100644
index 0000000..66d98ec
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_gtid_misc.test
@@ -0,0 +1,50 @@
+--source include/master-slave.inc
+
+--echo *** MDEV-6403: Temporary tables lost at STOP SLAVE in GTID mode if master has not rotated binlog since restart ***
+
+--connection master
+CREATE TABLE t1 (a INT PRIMARY KEY);
+--sync_slave_with_master
+
+--connection slave
+--source include/stop_slave.inc
+# Inject a duplicate key error that will make the slave stop in the middle of
+# a sequence of transactions that use a temporary table.
+SET sql_log_bin= 0;
+INSERT INTO t1 VALUES (1);
+SET sql_log_bin= 1;
+CHANGE MASTER TO master_use_gtid= current_pos;
+
+--connection master
+
+# Make some queries that use a temporary table.
+CREATE TEMPORARY TABLE t2 LIKE t1;
+INSERT INTO t2 VALUE (1);
+INSERT INTO t1 SELECT * FROM t2;
+DROP TEMPORARY TABLE t2;
+--save_master_pos
+
+--connection slave
+START SLAVE;
+--let $slave_sql_errno=1062
+--source include/wait_for_slave_sql_error.inc
+
+# Restart the slave.
+# The bug was that the IO thread would receive again the restart
+# format_description event at the start of the master's binlog, and this
+# event would cause the SQL thread to discard all active temporary tables.
+
+STOP SLAVE IO_THREAD;
+
+SET sql_log_bin= 0;
+DELETE FROM t1 WHERE a=1;
+SET sql_log_bin= 1;
+
+--source include/start_slave.inc
+--sync_with_master
+SELECT * FROM t1 ORDER BY a;
+
+--connection master
+DROP TABLE t1;
+
+--source include/rpl_end.inc
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index e5620ec..b5fc72e 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -1849,6 +1849,31 @@ void rpl_binlog_state::free()
 
 
 /*
+  Check if the GTID position has been reached, for mysql_binlog_send().
+
+  The position has not been reached if we have anything in the state, unless
+  it has either the START_ON_EMPTY_DOMAIN flag set (which means it does not
+  belong to this master at all), or the START_OWN_SLAVE_POS (which means that
+  we start on an old position from when the server was a slave with
+  --log-slave-updates=0).
+*/
+bool
+slave_connection_state::is_pos_reached()
+{
+  uint32 i;
+
+  for (i= 0; i < hash.records; ++i)
+  {
+    entry *e= (entry *)my_hash_element(&hash, i);
+    if (!(e->flags & (START_OWN_SLAVE_POS|START_ON_EMPTY_DOMAIN)))
+      return false;
+  }
+
+  return true;
+}
+
+
+/*
   Execute a MASTER_GTID_WAIT().
   The position to wait for is in gtid_str in string form.
   The timeout in microseconds is in timeout_us, zero means no timeout.
diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h
index 3e9e2fc..7b516fe 100644
--- a/sql/rpl_gtid.h
+++ b/sql/rpl_gtid.h
@@ -287,6 +287,7 @@ struct slave_connection_state
   int to_string(String *out_str);
   int append_to_string(String *out_str);
   int get_gtid_list(rpl_gtid *gtid_list, uint32 list_size);
+  bool is_pos_reached();
 };
 
 
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 7cbc21c..26355e4 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -2377,6 +2377,31 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
         info.fdev= tmp;
 
         (*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
+
+        if (info.using_gtid_state)
+        {
+          /*
+            If this event has the field `created' set, then it will cause the
+            slave to delete all active temporary tables. This must not happen
+            if the slave received any later GTIDs in a previous connect, as
+            those GTIDs might have created new temporary tables that are still
+            needed.
+
+            So here, we check if the starting GTID position was already
+            reached before this format description event. If not, we clear the
+            `created' flag to preserve temporary tables on the slave. (If the
+            slave connects at a position past this event, it means that it
+            already received and handled it in a previous connect).
+          */
+          if (!info.gtid_state.is_pos_reached())
+          {
+            int4store((char*) packet->ptr()+LOG_EVENT_MINIMAL_HEADER_LEN+
+                      ST_CREATED_OFFSET+ev_offset, (ulong) 0);
+            if (info.current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
+                info.current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
+              fix_checksum(packet, ev_offset);
+          }
+        }
       }
 
 #ifndef DBUG_OFF


More information about the commits mailing list