[MDEV-6769] DROP TRIGGER IF NOT EXIST binlogged on master but not on slave Created: 2014-09-23  Updated: 2015-02-22  Resolved: 2015-02-20

Status: Closed
Project: MariaDB Server
Component/s: Replication, Triggers
Affects Version/s: 5.5.40, 10.0.14
Fix Version/s: 10.0.17

Type: Bug Priority: Major
Reporter: Kristian Nielsen Assignee: Sergei Golubchik
Resolution: Fixed Votes: 0
Labels: None


 Description   

Consider this statement, when the trigger does not exist:

DROP TRIGGER IF EXISTS mytrig

This statement is binlogged on the master, but when replicated to a slave it
is not binlogged on the slave.

This causes binlogs to get out of sync and potentially breaking
replication. For example, when using GTID, if a slave stops after replicating
such a statement, it may not be able to connect to another server which is
missing that statement in its binlog.

Here is a test case that reproduces the problem:

--source include/have_innodb.inc
--let $rpl_topology=1->2,1->3
--source include/rpl_init.inc
 
connection server_1;
 
SET @old_strict= @@GLOBAL.gtid_strict_mode;
SET GLOBAL gtid_strict_mode=1;
CREATE TABLE t1 (a INT PRIMARY KEY, b VARCHAR(10)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1, "s1");
 
save_master_pos;
 
connection server_2;
sync_with_master;
--source include/stop_slave.inc
SET @old_strict= @@GLOBAL.gtid_strict_mode;
SET GLOBAL gtid_strict_mode=1;
CHANGE MASTER TO MASTER_USE_GTID=CURRENT_POS;
--source include/start_slave.inc
 
connection server_3;
sync_with_master;
--source include/stop_slave.inc
SET @old_strict= @@GLOBAL.gtid_strict_mode;
SET GLOBAL gtid_strict_mode=1;
CHANGE MASTER TO MASTER_USE_GTID=CURRENT_POS;
--source include/start_slave.inc
 
 
--connection server_1
# The bug is that this statement is binlogged on the master, but not on the
# slave. This means that the binlogs get out of sync, breaking GTID replication.
DROP TRIGGER IF EXISTS no_exist_trig;
 
SHOW BINLOG EVENTS;
SELECT @@GLOBAL.gtid_slave_pos;
SELECT @@GLOBAL.gtid_binlog_pos;
SELECT @@GLOBAL.gtid_current_pos;
--save_master_pos
 
--connection server_2
--sync_with_master
 
--source include/stop_slave.inc
SHOW BINLOG EVENTS;
SELECT @@GLOBAL.gtid_slave_pos;
SELECT @@GLOBAL.gtid_binlog_pos;
SELECT @@GLOBAL.gtid_current_pos;
 
--connection server_1
INSERT INTO t1 VALUES (2, "s1");
SELECT * FROM t1 ORDER BY a;
--save_master_pos
 
--connection server_3
--sync_with_master
SHOW BINLOG EVENTS;
SELECT @@GLOBAL.gtid_slave_pos;
SELECT @@GLOBAL.gtid_binlog_pos;
SELECT @@GLOBAL.gtid_current_pos;
--source include/save_master_gtid.inc
 
--connection server_2
# Switch S2 to use S3 as the master.
# Here we get the bug: S2's position is the GTID of DROP IF EXISTS, but this
# does not exist on S3, so we get an error (as we should) in strict mode.
 
--replace_result $SERVER_MYPORT_3 SERVER_MYPORT_3
eval CHANGE MASTER TO master_host = '127.0.0.1', master_port = $SERVER_MYPORT_3,
     MASTER_USE_GTID=CURRENT_POS;
--source include/start_slave.inc
--source include/sync_with_master_gtid.inc
SELECT * FROM t1 ORDER BY a;
 
 
# Clean up.
--connection server_2
--source include/stop_slave.inc
SET GLOBAL gtid_strict_mode= @old_strict;
--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
eval CHANGE MASTER TO master_host = '127.0.0.1', master_port = $SERVER_MYPORT_1,
     MASTER_USE_GTID=CURRENT_POS;
--source include/start_slave.inc
 
--connection server_3
--source include/stop_slave.inc
SET GLOBAL gtid_strict_mode= @old_strict;
--source include/start_slave.inc
 
--connection server_1
DROP TABLE t1;
SET GLOBAL gtid_strict_mode= @old_strict;
 
--source include/rpl_end.inc

Requires the following to be put in <testname>.cnf:

!include ../my.cnf
 
[mysqld.1]
log-slave-updates
loose-innodb
 
[mysqld.2]
log-slave-updates
loose-innodb
 
[mysqld.3]
log-slave-updates
loose-innodb
 
[ENV]
SERVER_MYPORT_3=		@mysqld.3.port
SERVER_MYSOCK_3=		@mysqld.3.socket



 Comments   
Comment by Kristian Nielsen [ 2014-09-23 ]

I noticed this code in mysql_execute_command():

  if (unlikely(thd->slave_thread))
  {
    if (lex->sql_command == SQLCOM_DROP_TRIGGER)
    {
      add_table_for_trigger(thd, thd->lex->spname, 1, &all_tables);
      if (!all_tables)
      {
        /*
          If table name cannot be loaded,
          it means the trigger does not exists possibly because
          CREATE TRIGGER was previously skipped for this trigger
          according to slave filtering rules.
          Returning success without producing any errors in this case.
        */
        DBUG_RETURN(0);
      }

This might be related, but I don't know much about the trigger code.

Generated at Thu Feb 08 07:14:26 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.