If an intermediate slave S1 has replication filters enabled, its @@gtid_slave_pos may contain a GTID that is filtered and doesn't propagate to lower-level slaves with S1 as master. If then later S1 is demoted to a slave, it may attempt to connect to the filtered position. This is normally disallowed in --gtid-strict-mode. But if --gtid-ignore-duplicates is enabled, we should allow it, as in this case we can trust the GTID sequence numbers between different server ids. So we can know that the next GTID is the right one for the filtered slave GTID position. This allows advanced users to use replication filtering in topologies like this and still run with --gtid-strict-mode enabled. Signed-off-by: Kristian Nielsen <knielsen@knielsen-hq.org> --- mysql-test/suite/rpl/r/rpl_mdev26632.result | 78 ++++++++++++++ mysql-test/suite/rpl/t/rpl_mdev26632.cnf | 28 +++++ mysql-test/suite/rpl/t/rpl_mdev26632.test | 109 ++++++++++++++++++++ sql/sql_repl.cc | 8 +- 4 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/rpl/r/rpl_mdev26632.result create mode 100644 mysql-test/suite/rpl/t/rpl_mdev26632.cnf create mode 100644 mysql-test/suite/rpl/t/rpl_mdev26632.test diff --git a/mysql-test/suite/rpl/r/rpl_mdev26632.result b/mysql-test/suite/rpl/r/rpl_mdev26632.result new file mode 100644 index 00000000000..84080b94de8 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_mdev26632.result @@ -0,0 +1,78 @@ +include/rpl_init.inc [topology=1->2->3] +*** Test GTID master switch in a topology with filtered events. +*** With --gtid-ignore-duplicate and --gtid-strict-mode, should allow +*** GTID connect at a GTID position that is filtered on the new master. +connection server_1; +ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; +CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1,1); +CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB; +INSERT INTO t3 VALUES (1,1); +INSERT INTO t1 VALUES (2,1); +INSERT INTO t3 VALUES (2,1); +include/save_master_gtid.inc +connection server_2; +CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1,2); +include/sync_with_master_gtid.inc +include/save_master_gtid.inc +connection server_3; +include/sync_with_master_gtid.inc +*** Promote 3 as new master, demote 2 as slave of 3. +*** GTID position of 2 in domain 0 is filtered on 3. +connection server_2; +include/stop_slave.inc +connection server_3; +include/stop_slave.inc +CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_1, +MASTER_USE_GTID=SLAVE_POS; +connection server_2; +CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_3, +MASTER_USE_GTID=SLAVE_POS; +include/start_slave.inc +connection server_3; +include/start_slave.inc +connection server_1; +INSERT INTO t1 VALUES (3,1); +INSERT INTO t3 VALUES (3,1); +include/save_master_gtid.inc +connection server_3; +INSERT INTO t2 VALUES (2,2); +include/sync_with_master_gtid.inc +include/save_master_gtid.inc +connection server_2; +include/sync_with_master_gtid.inc +SELECT * FROM t1 ORDER BY a; +a b +1 1 +2 1 +3 1 +SELECT * FROM t3 ORDER BY a; +ERROR 42S02: Table 'test.t3' doesn't exist +SELECT * FROM t2 ORDER BY a; +a b +1 2 +2 2 +*** Restore original topology. +connection server_3; +include/stop_slave.inc +connection server_2; +include/stop_slave.inc +CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_1, +MASTER_USE_GTID=SLAVE_POS; +include/start_slave.inc +connection server_3; +CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_2, +MASTER_USE_GTID=SLAVE_POS; +include/start_slave.inc +connection server_1; +DROP TABLE t1; +DROP TABLE t3; +include/save_master_gtid.inc +connection server_2; +DROP TABLE t2; +include/sync_with_master_gtid.inc +include/save_master_gtid.inc +connection server_3; +include/sync_with_master_gtid.inc +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_mdev26632.cnf b/mysql-test/suite/rpl/t/rpl_mdev26632.cnf new file mode 100644 index 00000000000..5eda3ad0725 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_mdev26632.cnf @@ -0,0 +1,28 @@ +!include ../my.cnf + +[mysqld.1] +log-slave-updates +loose-innodb +gtid-domain-id=1 +gtid-strict-mode=1 +gtid-ignore-duplicates=1 + +[mysqld.2] +log-slave-updates +loose-innodb +gtid-domain-id=0 +replicate-ignore-table=test.t3 +gtid-strict-mode=1 +gtid-ignore-duplicates=1 + +[mysqld.3] +log-slave-updates +loose-innodb +gtid-domain-id=0 +replicate-ignore-table=test.t3 +gtid-strict-mode=1 +gtid-ignore-duplicates=1 + +[ENV] +SERVER_MYPORT_3= @mysqld.3.port +SERVER_MYSOCK_3= @mysqld.3.socket diff --git a/mysql-test/suite/rpl/t/rpl_mdev26632.test b/mysql-test/suite/rpl/t/rpl_mdev26632.test new file mode 100644 index 00000000000..842bae8234c --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_mdev26632.test @@ -0,0 +1,109 @@ +--source include/have_innodb.inc +--source include/have_binlog_format_mixed.inc + +--let $rpl_topology=1->2->3 +--source include/rpl_init.inc + +--echo *** Test GTID master switch in a topology with filtered events. +--echo *** With --gtid-ignore-duplicate and --gtid-strict-mode, should allow +--echo *** GTID connect at a GTID position that is filtered on the new master. + +--connection server_1 + +ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB; +CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB; +INSERT INTO t1 VALUES (1,1); +CREATE TABLE t3 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB; +INSERT INTO t3 VALUES (1,1); +INSERT INTO t1 VALUES (2,1); +INSERT INTO t3 VALUES (2,1); +--source include/save_master_gtid.inc + +--connection server_2 +CREATE TABLE t2 (a INT PRIMARY KEY, b INT) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1,2); + +--let $slave_timeout= 10 +--source include/sync_with_master_gtid.inc +--source include/save_master_gtid.inc + +--connection server_3 +--source include/sync_with_master_gtid.inc + +--echo *** Promote 3 as new master, demote 2 as slave of 3. +--echo *** GTID position of 2 in domain 0 is filtered on 3. + +--connection server_2 +--source include/stop_slave.inc + +--connection server_3 +--source include/stop_slave.inc +--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=SLAVE_POS; + +--connection server_2 +--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=SLAVE_POS; +--source include/start_slave.inc + +--connection server_3 +--source include/start_slave.inc + +--connection server_1 +INSERT INTO t1 VALUES (3,1); +INSERT INTO t3 VALUES (3,1); +--source include/save_master_gtid.inc + +--connection server_3 +INSERT INTO t2 VALUES (2,2); + +--source include/sync_with_master_gtid.inc +--source include/save_master_gtid.inc + +--connection server_2 +--source include/sync_with_master_gtid.inc + +SELECT * FROM t1 ORDER BY a; +# Verify that table t3 is being filtered. +--error 1146 +SELECT * FROM t3 ORDER BY a; +SELECT * FROM t2 ORDER BY a; + + +--echo *** Restore original topology. + +--connection server_3 +--source include/stop_slave.inc + +--connection server_2 +--source include/stop_slave.inc +--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=SLAVE_POS; +--source include/start_slave.inc + +--connection server_3 +--replace_result $SERVER_MYPORT_2 SERVER_MYPORT_2 +eval CHANGE MASTER TO master_host = '127.0.0.1', master_port = $SERVER_MYPORT_2, + MASTER_USE_GTID=SLAVE_POS; +--source include/start_slave.inc + + +# Cleanup + +--connection server_1 +DROP TABLE t1; +DROP TABLE t3; +--source include/save_master_gtid.inc + +--connection server_2 +DROP TABLE t2; +--source include/sync_with_master_gtid.inc +--source include/save_master_gtid.inc + +--connection server_3 +--source include/sync_with_master_gtid.inc + +--source include/rpl_end.inc diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 0d2e61f7f59..e3b6d5fb7f3 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1823,13 +1823,19 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type, { if (info->slave_gtid_strict_mode && event_gtid.seq_no > gtid->seq_no && - !(gtid_entry->flags & slave_connection_state::START_OWN_SLAVE_POS)) + !(gtid_entry->flags & slave_connection_state::START_OWN_SLAVE_POS) && + !info->slave_gtid_ignore_duplicates) { /* In strict mode, it is an error if the slave requests to start in a "hole" in the master's binlog: a GTID that does not exist, even though both the prior and subsequent seq_no exists for same domain_id and server_id. + + But in --gtid-ignore-duplicates this is relaxed, as this + implies that we trust the sequence numbers between different + server_id. Thus, we want to allow the slave to connect at the + "hole", which could eg. be a filtered event. */ info->error= ER_GTID_START_FROM_BINLOG_HOLE; *error_gtid= *gtid; -- 2.30.2