Bug #78196 PARTIALLY FAILED DROP TABLE FAILS TO CONSUME GTID ON BINLOGLESS SLAVE
Submitted: 25 Aug 2015 1:25 Modified: 20 May 2016 16:17
Reporter: João Gramacho Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Replication Severity:S3 (Non-critical)
Version:5.7 OS:Any
Assigned to: CPU Architecture:Any

[25 Aug 2015 1:25] João Gramacho
Description:
Drop table has a particular way of writing to the binary log. It will fully write drop statements into the binary log even for partially failed statements.

The current code write DROP TABLE statements to the binary log manually, calling thd->binlog_query() function.

Apparently, this code is missing the handling of GTID on a binlog less slave when some error happened, but the statement is written to the binary log.

How to repeat:
Apply the following diff on MySQL 5.7:
=====================================

diff --git a/mysql-test/suite/rpl/t/rpl_drop-slave.opt b/mysql-test/suite/rpl/t/rpl_drop-slave.opt
new file mode 100644
index 0000000..526ec2f
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_drop-slave.opt
@@ -0,0 +1,2 @@
+--disable-log-bin
+--disable-log-slave-updates
diff --git a/mysql-test/suite/rpl/t/rpl_drop.test b/mysql-test/suite/rpl/t/rpl_drop.test
index 8728ed2..b4a0919 100644
--- a/mysql-test/suite/rpl/t/rpl_drop.test
+++ b/mysql-test/suite/rpl/t/rpl_drop.test
@@ -4,8 +4,12 @@
 source include/master-slave.inc;
 
 create table t1 (a int);
+create table t3 (a int);
 --error 1051
 drop table t1, t2;
+drop table t3;
+# Sleep to hit the issue faster (avoiding large timeout)
+--sleep 1
 --source include/sync_slave_sql_with_master.inc
 
 # End of 4.1 tests

And then run the following mtr test command:
===========================================

./mtr --mem --debug --mysqld=--enforce-gtid-consistency --mysqld=--log-slave-updates --mysqld=--gtid-mode=on rpl_drop

Suggested fix:
The following diff is a patch prototype. It was not extensively tested.

diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index f724c56..bfa32a0 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -2750,6 +2750,25 @@ err:
                                    error_code);
       }
     }
+    else /* !dont_log_query && mysql_bin_log.is_open() */
+    {
+      /*
+        Even when binary logging is not enable, we must check if we need to
+        handle the GTID of the transaction that partially failed. Not handling
+        the GTID here will cause the GTID to be rolled back as this statement
+        has failed. Binlog-less slaves can have GTIDs enabled and will store
+        the GTID on mysql.gtid_executed table.
+      */
+      if (error &&
+          (!opt_bin_log || (thd->slave_thread && !opt_log_slave_updates)) &&
+          thd->owned_gtid.sidno > 0 &&
+          !thd->is_operating_gtid_table_implicitly &&
+          !thd->is_operating_substatement_implicitly)
+      {
+        gtid_state->save(thd);
+        gtid_state->update_on_commit(thd);
+      }
+    }
   }
 
   if (!drop_temporary)
[26 Aug 2015 13:42] Sven Sandberg
Posted by developer:
 
This also affects partially failing ACL statements. See BUG#21697422, which has been marked as a duplicate of the present one, assuming  a single patch will fix both bugs.

Please also investigate if there are other non-DML statements which are affected. Guessing DROP VIEW and RENAME TABLE: these statements can drop/rename multiple tables, so probably they can fail after having dropped/renamed some but not all tables.

main.no_binlog_gtid_next_single_stmt_trx_rollback will probably be disabled (at least on trunk) due to this bug; please re-enable it after fixing.
[20 May 2016 16:17] David Moss
Posted by developer:
 
This has been fixed in upcoming versions and the following was added to the 5.7.13 changelog:
A partially failed statement was not correctly consuming an auto-generated or specified GTID when binary logging was disabled. The fix ensures that a partially failed DROP TABLE, a partially failed DROP USER or a partially failed DROP VIEW consume respectively the relevant GTID and save it into @@GLOBAL.GTID_EXECUTED and mysql.gtid_executed tables when binary logging is disabled.