Commit 9fae9930 authored by unknown's avatar unknown

MDEV-26: Global transaction ID.

Implement START SLAVE UNTIL master_gtid_pos = "<GTID position>".

Add test cases, including a test showing how to use this to promote
a new master among a set of slaves.
parent 7202c21b
......@@ -178,6 +178,7 @@ while ($_rpl_server)
{
RESET MASTER;
RESET SLAVE;
SET GLOBAL gtid_pos= "";
}
eval SET auto_increment_increment= $rpl_server_count;
eval SET auto_increment_offset= $_rpl_server;
......
include/rpl_init.inc [topology=1->2, 1->3, 1->4, 1->5]
ALTER TABLE mysql.rpl_slave_state ENGINE=InnoDB;
CREATE TABLE t4 (a INT, b INT, PRIMARY KEY (a,b)) Engine=InnoDB;
CREATE FUNCTION extract_gtid(d VARCHAR(100), s VARCHAR(100))
RETURNS VARCHAR(100) DETERMINISTIC
BEGIN
SET s= CONCAT(",", s, ",");
SET s= SUBSTR(s FROM LOCATE(CONCAT(",", d, "-"), s) + 1);
SET s= SUBSTR(s FROM 1 FOR LOCATE(",", s) - 1);
RETURN s;
END|
include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=1;
include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=1;
include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=1;
include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=1;
SET gtid_domain_id= 1;
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1);
INSERT INTO t1 VALUES (2);
INSERT INTO t4 VALUES (1, 1);
INSERT INTO t1 VALUES (3);
INSERT INTO t1 VALUES (4);
INSERT INTO t4 VALUES (1, 3);
SET gtid_domain_id= 2;
CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t2 VALUES (1);
INSERT INTO t2 VALUES (2);
INSERT INTO t4 VALUES (2, 1);
INSERT INTO t2 VALUES (3);
INSERT INTO t2 VALUES (4);
INSERT INTO t4 VALUES (2, 3);
SET gtid_domain_id= 3;
CREATE TABLE t3 (a INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t3 VALUES (1);
INSERT INTO t3 VALUES (2);
INSERT INTO t4 VALUES (3, 1);
INSERT INTO t3 VALUES (3);
INSERT INTO t3 VALUES (4);
INSERT INTO t4 VALUES (3, 3);
START SLAVE UNTIL master_gtid_pos= "1-1-7,2-1-14,3-1-21";
START SLAVE UNTIL master_gtid_pos= "1-1-4,2-1-14,3-1-24";
START SLAVE UNTIL master_gtid_pos= "2-1-11,3-1-21,1-1-10";
START SLAVE UNTIL master_gtid_pos= "3-1-18,1-1-7,2-1-17";
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
SELECT * FROM t2 ORDER BY a;
a
1
2
SELECT * FROM t3 ORDER BY a;
a
1
2
SELECT * FROM t4 ORDER BY a,b;
a b
1 1
2 1
3 1
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
SELECT * FROM t2 ORDER BY a;
a
1
2
SELECT * FROM t3 ORDER BY a;
a
1
2
3
4
SELECT * FROM t4 ORDER BY a,b;
a b
2 1
3 1
3 3
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
SELECT * FROM t2 ORDER BY a;
a
SELECT * FROM t3 ORDER BY a;
a
1
2
SELECT * FROM t4 ORDER BY a,b;
a b
1 1
1 3
3 1
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
SELECT * FROM t2 ORDER BY a;
a
1
2
3
4
SELECT * FROM t3 ORDER BY a;
a
SELECT * FROM t4 ORDER BY a,b;
a b
1 1
2 1
2 3
*** Now replicate all extra changes from 3,4,5 to 2, in preparation for making 2 the new master. ***
CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_3;
START SLAVE UNTIL master_gtid_pos = "1-1-4,0-1-3,3-1-24,2-1-14";
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
SELECT * FROM t2 ORDER BY a;
a
1
2
SELECT * FROM t3 ORDER BY a;
a
1
2
3
4
SELECT * FROM t4 ORDER BY a,b;
a b
1 1
2 1
3 1
3 3
CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_4;
START SLAVE UNTIL master_gtid_pos = "1-1-10,0-1-3,3-1-21,2-1-11";
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
SELECT * FROM t2 ORDER BY a;
a
1
2
SELECT * FROM t3 ORDER BY a;
a
1
2
3
4
SELECT * FROM t4 ORDER BY a,b;
a b
1 1
1 3
2 1
3 1
3 3
CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_5;
START SLAVE UNTIL master_gtid_pos = "1-1-7,0-1-3,3-1-18,2-1-17";
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
SELECT * FROM t2 ORDER BY a;
a
1
2
3
4
SELECT * FROM t3 ORDER BY a;
a
1
2
3
4
SELECT * FROM t4 ORDER BY a,b;
a b
1 1
1 3
2 1
2 3
3 1
3 3
*** Now make 2 master and point 3,4,5 to the new master 2
SET gtid_domain_id= 1;
INSERT INTO t1 values (5);
INSERT INTO t4 values (1,5);
SET gtid_domain_id= 2;
INSERT INTO t2 values (5);
INSERT INTO t4 values (2,5);
SET gtid_domain_id= 3;
INSERT INTO t3 values (5);
INSERT INTO t4 values (3,5);
CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_2;
include/start_slave.inc
CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_2;
include/start_slave.inc
CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_2;
include/start_slave.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t2 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t3 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t4 ORDER BY a,b;
a b
1 1
1 3
1 5
2 1
2 3
2 5
3 1
3 3
3 5
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t2 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t3 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t4 ORDER BY a,b;
a b
1 1
1 3
1 5
2 1
2 3
2 5
3 1
3 3
3 5
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t2 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t3 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t4 ORDER BY a,b;
a b
1 1
1 3
1 5
2 1
2 3
2 5
3 1
3 3
3 5
*** Now let the old master join up as slave. ***
CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_2,
master_user = "root", master_use_gtid = 1;
include/start_slave.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t2 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t3 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t4 ORDER BY a,b;
a b
1 1
1 3
1 5
2 1
2 3
2 5
3 1
3 3
3 5
*** Finally move things back and clean up. ***
include/stop_slave.inc
RESET SLAVE ALL;
CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_1;
include/start_slave.inc
include/stop_slave.inc
CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_1;
include/start_slave.inc
include/stop_slave.inc
CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_1;
include/start_slave.inc
include/stop_slave.inc
CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_1;
include/start_slave.inc
SET gtid_domain_id = 0;
DROP TABLE t1;
DROP TABLE t2;
DROP TABLE t3;
DROP TABLE t4;
DROP FUNCTION extract_gtid;
include/rpl_end.inc
include/rpl_init.inc [topology=1->2]
ALTER TABLE mysql.rpl_slave_state ENGINE=InnoDB;
CREATE FUNCTION extract_gtid(d VARCHAR(100), s VARCHAR(100))
RETURNS VARCHAR(100) DETERMINISTIC
BEGIN
SET s= CONCAT(",", s, ",");
SET s= SUBSTR(s FROM LOCATE(CONCAT(",", d, "-"), s) + 1);
SET s= SUBSTR(s FROM 1 FOR LOCATE(",", s) - 1);
RETURN s;
END|
START SLAVE UNTIL master_gtid_pos = "";
ERROR HY000: Slave is already running
include/stop_slave_io.inc
START SLAVE UNTIL master_gtid_pos = "";
ERROR HY000: Slave is already running
START SLAVE IO_THREAD;
include/wait_for_slave_io_to_start.inc
include/stop_slave_sql.inc
START SLAVE UNTIL master_gtid_pos = "";
ERROR HY000: Slave is already running
include/stop_slave_io.inc
START SLAVE UNTIL master_gtid_pos = "";
ERROR HY000: START SLAVE UNTIL master_gtid_pos requires that slave is using GTID
CHANGE MASTER TO master_use_gtid=1;
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES(1);
INSERT INTO t1 VALUES(2);
START SLAVE UNTIL master_gtid_pos = "0-1-100,1-1-100,2-2-200,1-3-100,4-4-400";
ERROR HY000: GTID 1-3-100 and 1-1-100 conflict (duplicate domain id 1)
START SLAVE UNTIL master_log_file = "master-bin.000001", master_log_pos = 4, master_gtid_pos = "";
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'master_gtid_pos = ""' at line 1
START SLAVE IO_THREAD UNTIL master_gtid_pos = "";
ERROR HY000: Incorrect parameter or combination of parameters for START SLAVE UNTIL
START SLAVE SQL_THREAD UNTIL master_gtid_pos = "";
ERROR HY000: Incorrect parameter or combination of parameters for START SLAVE UNTIL
START SLAVE UNTIL master_gtid_pos = '0-1-4';
include/wait_for_slave_to_stop.inc
SELECT * FROM t1;
a
1
include/start_slave.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
include/stop_slave.inc
START SLAVE UNTIL master_gtid_pos = "1-10-100,2-20-200";
include/wait_for_slave_to_start.inc
Using_Gtid = '1'
Until_Condition = 'Gtid'
include/stop_slave.inc
include/start_slave.inc
*** Test UNTIL condition in an earlier binlog than the start GTID. ***
include/stop_slave.inc
SET gtid_domain_id = 1;
INSERT INTO t1 VALUES (3);
SET gtid_domain_id = 2;
CREATE TABLE t2 (a INT);
INSERT INTO t2 VALUES (3);
FLUSH LOGS;
SET gtid_domain_id = 1;
INSERT INTO t1 VALUES (4);
SET gtid_domain_id = 2;
INSERT INTO t2 VALUES (4);
FLUSH LOGS;
SET gtid_domain_id = 1;
INSERT INTO t1 VALUES (5);
SET gtid_domain_id = 2;
INSERT INTO t2 VALUES (5);
FLUSH LOGS;
SET gtid_domain_id = 1;
INSERT INTO t1 VALUES (6);
SET gtid_domain_id = 2;
INSERT INTO t2 VALUES (6);
SET gtid_domain_id = 0;
show binary logs;
Log_name File_size
master-bin.000001 #
master-bin.000002 #
master-bin.000003 #
master-bin.000004 #
START SLAVE UNTIL master_gtid_pos='1-1-11,2-1-12';
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
5
SELECT * FROM t2 ORDER BY a;
a
3
4
5
START SLAVE UNTIL master_gtid_pos='1-1-13,2-1-8';
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
5
6
SELECT * FROM t2 ORDER BY a;
a
3
4
5
START SLAVE UNTIL master_gtid_pos='1-1-11';
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
5
6
SELECT * FROM t2 ORDER BY a;
a
3
4
5
include/start_slave.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
5
6
SELECT * FROM t2 ORDER BY a;
a
3
4
5
6
*** Test when the UNTIL position is right at the end of the binlog file prior to the starting position ***
include/stop_slave.inc
FLUSH LOGS;
SET gtid_domain_id = 1;
INSERT INTO t1 VALUES (7);
SET gtid_domain_id = 0;
START SLAVE UNTIL master_gtid_pos='1-1-13';
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
5
6
include/start_slave.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
5
6
7
*** Test when UNTIL condition is after a stand-alone event (not a transaction). ***
include/stop_slave.inc
CREATE TABLE t3 (a INT);
DROP TABLE t3;
START SLAVE UNTIL master_gtid_pos='1-1-15,0-1-16,2-1-14';
include/wait_for_slave_to_stop.inc
SHOW CREATE TABLE t3;
Table Create Table
t3 CREATE TABLE `t3` (
`a` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
include/start_slave.inc
*** Test UNTIL condition that has not yet been logged. ***
include/stop_slave.inc
RESET SLAVE ALL;
RESET MASTER;
SET GLOBAL gtid_pos='';
RESET MASTER;
INSERT INTO t1 VALUES (10);
INSERT INTO t1 VALUES (11);
INSERT INTO t1 VALUES (12);
DELETE FROM t1 WHERE a >= 10;
RESET MASTER;
INSERT INTO t1 VALUES (10);
CHANGE MASTER TO master_host = '127.0.0.1', master_port = SERVER_MYPORT_1,
master_user = "root", master_use_gtid = 1;
START SLAVE UNTIL master_gtid_pos = '0-1-2';
include/wait_for_slave_to_start.inc
INSERT INTO t1 VALUES (11);
INSERT INTO t1 VALUES (12);
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
5
6
7
10
11
include/start_slave.inc
SELECT * FROM t1 ORDER BY a;
a
1
2
3
4
5
6
7
10
11
12
DROP TABLE t1;
DROP TABLE t2;
DROP FUNCTION extract_gtid;
include/rpl_end.inc
!include ../my.cnf
[mysqld.1]
log-slave-updates
loose-innodb
[mysqld.2]
log-slave-updates
skip-slave-start
loose-innodb
[mysqld.3]
log-slave-updates
skip-slave-start
loose-innodb
[mysqld.4]
log-slave-updates
skip-slave-start
loose-innodb
[mysqld.5]
log-slave-updates
skip-slave-start
loose-innodb
[ENV]
SERVER_MYPORT_3= @mysqld.3.port
SERVER_MYSOCK_3= @mysqld.3.socket
SERVER_MYPORT_4= @mysqld.4.port
SERVER_MYSOCK_4= @mysqld.4.socket
SERVER_MYPORT_5= @mysqld.5.port
SERVER_MYSOCK_5= @mysqld.5.socket
--source include/have_innodb.inc
--let $rpl_topology=1->2, 1->3, 1->4, 1->5
--source include/rpl_init.inc
# Set up a topology with one master and 4 slaves.
#
# Replicate some events leaving the four slaves at different points
# in different domains.
#
# Then promote one slave as new master, bringing it ahead of all others
# using START SLAVE UNTIL master_gtid_pos.
--connection server_1
ALTER TABLE mysql.rpl_slave_state ENGINE=InnoDB;
CREATE TABLE t4 (a INT, b INT, PRIMARY KEY (a,b)) Engine=InnoDB;
# Function to extract one GTID from a list.
delimiter |;
CREATE FUNCTION extract_gtid(d VARCHAR(100), s VARCHAR(100))
RETURNS VARCHAR(100) DETERMINISTIC
BEGIN
SET s= CONCAT(",", s, ",");
SET s= SUBSTR(s FROM LOCATE(CONCAT(",", d, "-"), s) + 1);
SET s= SUBSTR(s FROM 1 FOR LOCATE(",", s) - 1);
RETURN s;
END|
delimiter ;|
--save_master_pos
--connection server_2
--sync_with_master
--source include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=1;
--connection server_3
--sync_with_master
--source include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=1;
--connection server_4
--sync_with_master
--source include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=1;
--connection server_5
--sync_with_master
--source include/stop_slave.inc
CHANGE MASTER TO master_use_gtid=1;
# Create three separate replication streams on master server_1.
#
# Then use START SLAVE UNTIL to get the different streams interleaved
# differently spread over multiple binlogs on the different slaves, to
# test that new master promotion is able to deal with this.
--connection server_1
SET gtid_domain_id= 1;
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
--let $d1_begin= `SELECT extract_gtid("1", @@GLOBAL.gtid_pos)`
INSERT INTO t1 VALUES (1);
INSERT INTO t1 VALUES (2);
INSERT INTO t4 VALUES (1, 1);
--let $d1_mid= `SELECT extract_gtid("1", @@GLOBAL.gtid_pos)`
INSERT INTO t1 VALUES (3);
INSERT INTO t1 VALUES (4);
INSERT INTO t4 VALUES (1, 3);
--let $d1_end= `SELECT extract_gtid("1", @@GLOBAL.gtid_pos)`
SET gtid_domain_id= 2;
CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB;
--let $d2_begin= `SELECT extract_gtid("2", @@GLOBAL.gtid_pos)`
INSERT INTO t2 VALUES (1);
INSERT INTO t2 VALUES (2);
INSERT INTO t4 VALUES (2, 1);
--let $d2_mid= `SELECT extract_gtid("2", @@GLOBAL.gtid_pos)`
INSERT INTO t2 VALUES (3);
INSERT INTO t2 VALUES (4);
INSERT INTO t4 VALUES (2, 3);
--let $d2_end= `SELECT extract_gtid("2", @@GLOBAL.gtid_pos)`
SET gtid_domain_id= 3;
CREATE TABLE t3 (a INT PRIMARY KEY) ENGINE=InnoDB;
--let $d3_begin= `SELECT extract_gtid("3", @@GLOBAL.gtid_pos)`
INSERT INTO t3 VALUES (1);
INSERT INTO t3 VALUES (2);
INSERT INTO t4 VALUES (3, 1);
--let $d3_mid= `SELECT extract_gtid("3", @@GLOBAL.gtid_pos)`
INSERT INTO t3 VALUES (3);
INSERT INTO t3 VALUES (4);
INSERT INTO t4 VALUES (3, 3);
--let $d3_end= `SELECT extract_gtid("3", @@GLOBAL.gtid_pos)`
# Slave server_2 (that will be promoted to master) is in the middle
# of each stream.
--connection server_2
eval START SLAVE UNTIL master_gtid_pos= "$d1_mid,$d2_mid,$d3_mid";
# The remaining slaves sit at different points each in different domains.
--connection server_3
eval START SLAVE UNTIL master_gtid_pos= "$d1_begin,$d2_mid,$d3_end";
--connection server_4
eval START SLAVE UNTIL master_gtid_pos= "$d2_begin,$d3_mid,$d1_end";
--connection server_5
eval START SLAVE UNTIL master_gtid_pos= "$d3_begin,$d1_mid,$d2_end";
--connection server_2
--source include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT * FROM t3 ORDER BY a;
SELECT * FROM t4 ORDER BY a,b;
--connection server_3
--source include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT * FROM t3 ORDER BY a;
SELECT * FROM t4 ORDER BY a,b;
--connection server_4
--source include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT * FROM t3 ORDER BY a;
SELECT * FROM t4 ORDER BY a,b;
--connection server_5
--source include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT * FROM t3 ORDER BY a;
SELECT * FROM t4 ORDER BY a,b;
--echo *** Now replicate all extra changes from 3,4,5 to 2, in preparation for making 2 the new master. ***
--connection server_3
--let $server3_pos= `SELECT @@GLOBAL.gtid_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;
eval START SLAVE UNTIL master_gtid_pos = "$server3_pos";
--source include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT * FROM t3 ORDER BY a;
SELECT * FROM t4 ORDER BY a,b;
--connection server_4
--let $server4_pos= `SELECT @@GLOBAL.gtid_pos`
--connection server_2
--replace_result $SERVER_MYPORT_4 SERVER_MYPORT_4
eval CHANGE MASTER TO master_host = '127.0.0.1', master_port = $SERVER_MYPORT_4;
eval START SLAVE UNTIL master_gtid_pos = "$server4_pos";
--source include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT * FROM t3 ORDER BY a;
SELECT * FROM t4 ORDER BY a,b;
--connection server_5
--let $server5_pos= `SELECT @@GLOBAL.gtid_pos`
--connection server_2
--replace_result $SERVER_MYPORT_5 SERVER_MYPORT_5
eval CHANGE MASTER TO master_host = '127.0.0.1', master_port = $SERVER_MYPORT_5;
eval START SLAVE UNTIL master_gtid_pos = "$server5_pos";
--source include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT * FROM t3 ORDER BY a;
SELECT * FROM t4 ORDER BY a,b;
--echo *** Now make 2 master and point 3,4,5 to the new master 2
--connection server_2
SET gtid_domain_id= 1;
INSERT INTO t1 values (5);
INSERT INTO t4 values (1,5);
SET gtid_domain_id= 2;
INSERT INTO t2 values (5);
INSERT INTO t4 values (2,5);
SET gtid_domain_id= 3;
INSERT INTO t3 values (5);
INSERT INTO t4 values (3,5);
--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;
--source include/start_slave.inc
--connection server_4
--replace_result $SERVER_MYPORT_2 SERVER_MYPORT_2
eval CHANGE MASTER TO master_host = '127.0.0.1', master_port = $SERVER_MYPORT_2;
--source include/start_slave.inc
--connection server_5
--replace_result $SERVER_MYPORT_2 SERVER_MYPORT_2
eval CHANGE MASTER TO master_host = '127.0.0.1', master_port = $SERVER_MYPORT_2;
--source include/start_slave.inc
--connection server_2
--save_master_pos
--connection server_3
--sync_with_master
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT * FROM t3 ORDER BY a;
SELECT * FROM t4 ORDER BY a,b;
--connection server_5
--sync_with_master
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT * FROM t3 ORDER BY a;
SELECT * FROM t4 ORDER BY a,b;
--connection server_5
--sync_with_master
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT * FROM t3 ORDER BY a;
SELECT * FROM t4 ORDER BY a,b;
--echo *** Now let the old master join up as slave. ***
--connection server_1
--replace_result $SERVER_MYPORT_2 SERVER_MYPORT_2
eval CHANGE MASTER TO master_host = '127.0.0.1', master_port = $SERVER_MYPORT_2,
master_user = "root", master_use_gtid = 1;
--source include/start_slave.inc
--sync_with_master
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT * FROM t3 ORDER BY a;
SELECT * FROM t4 ORDER BY a,b;
--echo *** Finally move things back and clean up. ***
--connection server_1
--source include/stop_slave.inc
RESET SLAVE ALL;
--connection server_2
--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
eval CHANGE MASTER TO master_host = '127.0.0.1', master_port = $SERVER_MYPORT_1;
--source include/start_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;
--source include/start_slave.inc
--connection server_4
--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;
--source include/start_slave.inc
--connection server_5
--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;
--source include/start_slave.inc
--connection server_1
SET gtid_domain_id = 0;
DROP TABLE t1;
DROP TABLE t2;
DROP TABLE t3;
DROP TABLE t4;
DROP FUNCTION extract_gtid;
--source include/rpl_end.inc
--source include/have_innodb.inc
--let $rpl_topology=1->2
--source include/rpl_init.inc
--connection server_1
ALTER TABLE mysql.rpl_slave_state ENGINE=InnoDB;
# Function to extract one GTID from a list.
delimiter |;
CREATE FUNCTION extract_gtid(d VARCHAR(100), s VARCHAR(100))
RETURNS VARCHAR(100) DETERMINISTIC
BEGIN
SET s= CONCAT(",", s, ",");
SET s= SUBSTR(s FROM LOCATE(CONCAT(",", d, "-"), s) + 1);
SET s= SUBSTR(s FROM 1 FOR LOCATE(",", s) - 1);
RETURN s;
END|
delimiter ;|
--save_master_pos
--connection server_2
--sync_with_master
# Both replication threads must be stopped for UNTIL master_gtid_pos.
--error ER_SLAVE_WAS_RUNNING
START SLAVE UNTIL master_gtid_pos = "";
--source include/stop_slave_io.inc
--error ER_SLAVE_WAS_RUNNING
START SLAVE UNTIL master_gtid_pos = "";
START SLAVE IO_THREAD;
--source include/wait_for_slave_io_to_start.inc
--source include/stop_slave_sql.inc
--error ER_SLAVE_WAS_RUNNING
START SLAVE UNTIL master_gtid_pos = "";
--source include/stop_slave_io.inc
# UNTIL master_gtid_pos only valid if GTID is used.
--error ER_UNTIL_REQUIRES_USING_GTID
START SLAVE UNTIL master_gtid_pos = "";
CHANGE MASTER TO master_use_gtid=1;
--connection server_1
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES(1);
--let $gtid_pos=`SELECT @@GLOBAL.gtid_pos`
INSERT INTO t1 VALUES(2);
--connection server_2
# Test various incorrect syntax for UNTIL master_gtid_pos.
--error ER_DUPLICATE_GTID_DOMAIN
START SLAVE UNTIL master_gtid_pos = "0-1-100,1-1-100,2-2-200,1-3-100,4-4-400";
--error ER_PARSE_ERROR
START SLAVE UNTIL master_log_file = "master-bin.000001", master_log_pos = 4, master_gtid_pos = "";
--error ER_BAD_SLAVE_UNTIL_COND
START SLAVE IO_THREAD UNTIL master_gtid_pos = "";
--error ER_BAD_SLAVE_UNTIL_COND
START SLAVE SQL_THREAD UNTIL master_gtid_pos = "";
eval START SLAVE UNTIL master_gtid_pos = '$gtid_pos';
--source include/wait_for_slave_to_stop.inc
SELECT * FROM t1;
--source include/start_slave.inc
--connection server_1
--save_master_pos
--connection server_2
--sync_with_master
SELECT * FROM t1 ORDER BY a;
# Test showing the UNTIL condition in SHOW SLAVE STATUS.
--source include/stop_slave.inc
START SLAVE UNTIL master_gtid_pos = "1-10-100,2-20-200";
--source include/wait_for_slave_to_start.inc
--let $status_items= Using_Gtid,Until_Condition
--source include/show_slave_status.inc
# Clear the UNTIL condition.
--source include/stop_slave.inc
--source include/start_slave.inc
--echo *** Test UNTIL condition in an earlier binlog than the start GTID. ***
--connection server_2
--source include/stop_slave.inc
--connection server_1
SET gtid_domain_id = 1;
INSERT INTO t1 VALUES (3);
SET gtid_domain_id = 2;
CREATE TABLE t2 (a INT);
INSERT INTO t2 VALUES (3);
--let $d1_point1= `SELECT extract_gtid("1", @@GLOBAL.gtid_pos)`
--let $d2_point1= `SELECT extract_gtid("2", @@GLOBAL.gtid_pos)`
FLUSH LOGS;
SET gtid_domain_id = 1;
INSERT INTO t1 VALUES (4);
SET gtid_domain_id = 2;
INSERT INTO t2 VALUES (4);
FLUSH LOGS;
SET gtid_domain_id = 1;
INSERT INTO t1 VALUES (5);
--let $d1_point2= `SELECT extract_gtid("1", @@GLOBAL.gtid_pos)`
--let $d2_point2= `SELECT extract_gtid("2", @@GLOBAL.gtid_pos)`
SET gtid_domain_id = 2;
INSERT INTO t2 VALUES (5);
FLUSH LOGS;
SET gtid_domain_id = 1;
INSERT INTO t1 VALUES (6);
--let $d1_point3= `SELECT extract_gtid("1", @@GLOBAL.gtid_pos)`
--let $d2_point3= `SELECT extract_gtid("2", @@GLOBAL.gtid_pos)`
SET gtid_domain_id = 2;
INSERT INTO t2 VALUES (6);
SET gtid_domain_id = 0;
--source include/show_binary_logs.inc
--save_master_pos
--connection server_2
# Let the slave reach an middle point in domain 1 and a late point in domain 2.
eval START SLAVE UNTIL master_gtid_pos='$d1_point2,$d2_point3';
--source include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
# Now test starting at a middle point in the binlogs when the stop position in
# one domain (domain 2) is early.
eval START SLAVE UNTIL master_gtid_pos='$d1_point3,$d2_point1';
--source include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
# Test that one UNTIL domain empty means stop that domain immediately.
eval START SLAVE UNTIL master_gtid_pos='$d1_point2';
--source include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
--source include/start_slave.inc
--sync_with_master
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
--echo *** Test when the UNTIL position is right at the end of the binlog file prior to the starting position ***
--connection server_2
--source include/stop_slave.inc
--connection server_1
FLUSH LOGS;
SET gtid_domain_id = 1;
INSERT INTO t1 VALUES (7);
SET gtid_domain_id = 0;
--save_master_pos
--connection server_2
eval START SLAVE UNTIL master_gtid_pos='$d1_point3';
--source include/wait_for_slave_to_stop.inc
# This should not show row 7, as we requested stop just before it.
SELECT * FROM t1 ORDER BY a;
--source include/start_slave.inc
--sync_with_master
SELECT * FROM t1 ORDER BY a;
--echo *** Test when UNTIL condition is after a stand-alone event (not a transaction). ***
--connection server_2
--source include/stop_slave.inc
--connection server_1
CREATE TABLE t3 (a INT);
--let $until_condition=`SELECT @@GLOBAL.gtid_pos`
DROP TABLE t3;
--save_master_pos
--connection server_2
eval START SLAVE UNTIL master_gtid_pos='$until_condition';
--source include/wait_for_slave_to_stop.inc
SHOW CREATE TABLE t3;
--source include/start_slave.inc
--sync_with_master
--echo *** Test UNTIL condition that has not yet been logged. ***
--connection server_2
--source include/stop_slave.inc
RESET SLAVE ALL;
RESET MASTER;
SET GLOBAL gtid_pos='';
--connection server_1
# Do it once to compute the right GTID, then throw it away and do it again
# for the actual test.
RESET MASTER;
INSERT INTO t1 VALUES (10);
INSERT INTO t1 VALUES (11);
--let $until_condition=`SELECT @@GLOBAL.gtid_pos`
INSERT INTO t1 VALUES (12);
DELETE FROM t1 WHERE a >= 10;
RESET MASTER;
INSERT INTO t1 VALUES (10);
--connection server_2
--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
eval CHANGE MASTER TO master_host = '127.0.0.1', master_port = $SERVER_MYPORT_1,
master_user = "root", master_use_gtid = 1;
eval START SLAVE UNTIL master_gtid_pos = '$until_condition';
--source include/wait_for_slave_to_start.inc
--connection server_1
INSERT INTO t1 VALUES (11);
INSERT INTO t1 VALUES (12);
--save_master_pos
--connection server_2
# This then should wait until it gets the row (11) and then stop, not
# yet having the row (12).
--source include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
--source include/start_slave.inc
--sync_with_master
# And now the row (12) should be there.
SELECT * FROM t1 ORDER BY a;
# Clean up.
--connection server_1
DROP TABLE t1;
DROP TABLE t2;
DROP FUNCTION extract_gtid;
--source include/rpl_end.inc
......@@ -330,7 +330,7 @@ static SYMBOL symbols[] = {
{ "LOW_PRIORITY", SYM(LOW_PRIORITY)},
{ "MASTER", SYM(MASTER_SYM)},
{ "MASTER_CONNECT_RETRY", SYM(MASTER_CONNECT_RETRY_SYM)},
{ "MASTER_USE_GTID", SYM(MASTER_USE_GTID_SYM)},
{ "MASTER_GTID_POS", SYM(MASTER_GTID_POS_SYM)},
{ "MASTER_HOST", SYM(MASTER_HOST_SYM)},
{ "MASTER_LOG_FILE", SYM(MASTER_LOG_FILE_SYM)},
{ "MASTER_LOG_POS", SYM(MASTER_LOG_POS_SYM)},
......@@ -345,6 +345,7 @@ static SYMBOL symbols[] = {
{ "MASTER_SSL_KEY", SYM(MASTER_SSL_KEY_SYM)},
{ "MASTER_SSL_VERIFY_SERVER_CERT", SYM(MASTER_SSL_VERIFY_SERVER_CERT_SYM)},
{ "MASTER_USER", SYM(MASTER_USER_SYM)},
{ "MASTER_USE_GTID", SYM(MASTER_USE_GTID_SYM)},
{ "MASTER_HEARTBEAT_PERIOD", SYM(MASTER_HEARTBEAT_PERIOD_SYM)},
{ "MATCH", SYM(MATCH)},
{ "MAX_CONNECTIONS_PER_HOUR", SYM(MAX_CONNECTIONS_PER_HOUR)},
......
......@@ -3280,7 +3280,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
there had been an entry (domain_id, server_id, 0).
*/
Gtid_list_log_event gl_ev(&rpl_global_gtid_binlog_state);
Gtid_list_log_event gl_ev(&rpl_global_gtid_binlog_state, 0);
if (gl_ev.write(&log_file))
goto err;
......@@ -8718,16 +8718,11 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
case GTID_LIST_EVENT:
if (first_round)
{
uint32 i;
Gtid_list_log_event *glev= (Gtid_list_log_event *)ev;
/* Initialise the binlog state from the Gtid_list event. */
rpl_global_gtid_binlog_state.reset();
for (i= 0; i < glev->count; ++i)
{
if (rpl_global_gtid_binlog_state.update(&(glev->list[i])))
goto err2;
}
if (rpl_global_gtid_binlog_state.load(glev->list, glev->count))
goto err2;
}
break;
......
......@@ -6320,6 +6320,7 @@ Gtid_list_log_event::Gtid_list_log_event(const char *buf, uint event_len,
: Log_event(buf, description_event), count(0), list(0)
{
uint32 i;
uint32 val;
uint8 header_size= description_event->common_header_len;
uint8 post_header_len= description_event->post_header_len[GTID_LIST_EVENT-1];
if (event_len < header_size + post_header_len ||
......@@ -6327,7 +6328,9 @@ Gtid_list_log_event::Gtid_list_log_event(const char *buf, uint event_len,
return;
buf+= header_size;
count= uint4korr(buf) & ((1<<28)-1);
val= uint4korr(buf);
count= val & ((1<<28)-1);
gl_flags= val & ((uint32)0xf << 28);
buf+= 4;
if (event_len - (header_size + post_header_len) < count*element_size ||
(!(list= (rpl_gtid *)my_malloc(count*sizeof(*list) + (count == 0),
......@@ -6348,8 +6351,9 @@ Gtid_list_log_event::Gtid_list_log_event(const char *buf, uint event_len,
#ifdef MYSQL_SERVER
Gtid_list_log_event::Gtid_list_log_event(rpl_binlog_state *gtid_set)
: count(gtid_set->count()), list(0)
Gtid_list_log_event::Gtid_list_log_event(rpl_binlog_state *gtid_set,
uint32 gl_flags_)
: count(gtid_set->count()), gl_flags(gl_flags_), list(0)
{
cache_type= EVENT_NO_CACHE;
/* Failure to allocate memory will be caught by is_valid() returning false. */
......@@ -6359,32 +6363,70 @@ Gtid_list_log_event::Gtid_list_log_event(rpl_binlog_state *gtid_set)
gtid_set->get_gtid_list(list, count);
}
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
bool
Gtid_list_log_event::write(IO_CACHE *file)
Gtid_list_log_event::to_packet(String *packet)
{
uint32 i;
uchar buf[element_size];
uchar *p;
uint32 needed_length;
DBUG_ASSERT(count < 1<<28);
if (write_header(file, get_data_size()))
return 1;
int4store(buf, count & ((1<<28)-1));
if (wrapper_my_b_safe_write(file, buf, GTID_LIST_HEADER_LEN))
return 1;
needed_length= packet->length() + 4 + count*element_size;
if (packet->reserve(needed_length))
return true;
p= (uchar *)packet->ptr() + packet->length();;
packet->length(needed_length);
int4store(p, (count & ((1<<28)-1)) | gl_flags);
p += 4;
for (i= 0; i < count; ++i)
{
int4store(buf, list[i].domain_id);
int4store(buf+4, list[i].server_id);
int8store(buf+8, list[i].seq_no);
if (wrapper_my_b_safe_write(file, buf, element_size))
return 1;
int4store(p, list[i].domain_id);
int4store(p+4, list[i].server_id);
int8store(p+8, list[i].seq_no);
p += 16;
}
return write_footer(file);
return false;
}
bool
Gtid_list_log_event::write(IO_CACHE *file)
{
char buf[128];
String packet(buf, sizeof(buf), system_charset_info);
packet.length(0);
if (to_packet(&packet))
return true;
return
write_header(file, get_data_size()) ||
wrapper_my_b_safe_write(file, (uchar *)packet.ptr(), packet.length()) ||
write_footer(file);
}
int
Gtid_list_log_event::do_apply_event(Relay_log_info const *rli)
{
int ret= Log_event::do_apply_event(rli);
if (rli->until_condition == Relay_log_info::UNTIL_GTID &&
(gl_flags & FLAG_UNTIL_REACHED))
{
char str_buf[128];
String str(str_buf, sizeof(str_buf), system_charset_info);
const_cast<Relay_log_info*>(rli)->until_gtid_pos.to_string(&str);
sql_print_information("Slave SQL thread stops because it reached its"
" UNTIL master_gtid_pos %s", str.c_ptr_safe());
const_cast<Relay_log_info*>(rli)->abort_slave= true;
}
return ret;
}
#ifdef HAVE_REPLICATION
void
Gtid_list_log_event::pack_info(THD *thd, Protocol *protocol)
{
......@@ -6439,12 +6481,24 @@ Gtid_list_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
*/
bool
Gtid_list_log_event::peek(const char *event_start, uint32 event_len,
uint8 checksum_alg,
rpl_gtid **out_gtid_list, uint32 *out_list_len)
{
const char *p;
uint32 count_field, count;
rpl_gtid *gtid_list;
if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
{
if (event_len > BINLOG_CHECKSUM_LEN)
event_len-= BINLOG_CHECKSUM_LEN;
else
event_len= 0;
}
else
DBUG_ASSERT(checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
checksum_alg == BINLOG_CHECKSUM_ALG_OFF);
if (event_len < LOG_EVENT_HEADER_LEN + GTID_LIST_HEADER_LEN)
return true;
p= event_start + LOG_EVENT_HEADER_LEN;
......
......@@ -3116,7 +3116,7 @@ class Gtid_log_event: public Log_event
<td>count</td>
<td>4 byte unsigned integer</td>
<td>The lower 28 bits are the number of GTIDs. The upper 4 bits are
reserved for flags bits for future expansion</td>
flags bits.</td>
</tr>
</table>
......@@ -3149,18 +3149,28 @@ class Gtid_log_event: public Log_event
</table>
The three elements in the body repeat COUNT times to form the GTID list.
At the time of writing, only one flag bit is in use.
Bit 28 of `count' is used for flag FLAG_UNTIL_REACHED, which is sent in a
Gtid_list event from the master to the slave to indicate that the START
SLAVE UNTIL master_gtid_pos=xxx condition has been reached. (This flag is
only sent in "fake" events generated on the fly, it is not written into
the binlog).
*/
class Gtid_list_log_event: public Log_event
{
public:
uint32 count;
uint32 gl_flags;
struct rpl_gtid *list;
static const uint element_size= 4+4+8;
static const uint32 FLAG_UNTIL_REACHED= (1<<28);
#ifdef MYSQL_SERVER
Gtid_list_log_event(rpl_binlog_state *gtid_set);
Gtid_list_log_event(rpl_binlog_state *gtid_set, uint32 gl_flags);
#ifdef HAVE_REPLICATION
void pack_info(THD *thd, Protocol *protocol);
#endif
......@@ -3173,10 +3183,13 @@ class Gtid_list_log_event: public Log_event
Log_event_type get_type_code() { return GTID_LIST_EVENT; }
int get_data_size() { return GTID_LIST_HEADER_LEN + count*element_size; }
bool is_valid() const { return list != NULL; }
#ifdef MYSQL_SERVER
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
bool to_packet(String *packet);
bool write(IO_CACHE *file);
virtual int do_apply_event(Relay_log_info const *rli);
#endif
static bool peek(const char *event_start, uint32 event_len,
uint8 checksum_alg,
rpl_gtid **out_gtid_list, uint32 *out_list_len);
};
......
......@@ -711,6 +711,22 @@ void rpl_binlog_state::free()
}
}
bool
rpl_binlog_state::load(struct rpl_gtid *list, uint32 count)
{
uint32 i;
reset();
for (i= 0; i < count; ++i)
{
if (update(&(list[i])))
return true;
}
return false;
}
rpl_binlog_state::~rpl_binlog_state()
{
free();
......@@ -1116,11 +1132,18 @@ slave_connection_state::remove(const rpl_gtid *in_gtid)
int
slave_connection_state::to_string(String *out_str)
{
out_str->length(0);
return append_to_string(out_str);
}
int
slave_connection_state::append_to_string(String *out_str)
{
uint32 i;
bool first;
out_str->length(0);
first= true;
for (i= 0; i < hash.records; ++i)
{
......
......@@ -142,6 +142,7 @@ struct rpl_binlog_state
void reset();
void free();
bool load(struct rpl_gtid *list, uint32 count);
int update(const struct rpl_gtid *gtid);
uint64 seq_no_from_state();
int write_to_iocache(IO_CACHE *dest);
......@@ -172,6 +173,7 @@ struct slave_connection_state
void remove(const rpl_gtid *gtid);
ulong count() const { return hash.records; }
int to_string(String *out_str);
int append_to_string(String *out_str);
};
extern bool rpl_slave_state_tostring_helper(String *dest, const rpl_gtid *gtid,
......
......@@ -1096,7 +1096,8 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev)
ulonglong log_pos;
DBUG_ENTER("Relay_log_info::is_until_satisfied");
DBUG_ASSERT(until_condition != UNTIL_NONE);
DBUG_ASSERT(until_condition == UNTIL_MASTER_POS ||
until_condition == UNTIL_RELAY_POS);
if (until_condition == UNTIL_MASTER_POS)
{
......
......@@ -263,7 +263,9 @@ class Relay_log_info : public Slave_reporting_capability
thread is running).
*/
enum {UNTIL_NONE= 0, UNTIL_MASTER_POS, UNTIL_RELAY_POS} until_condition;
enum {
UNTIL_NONE= 0, UNTIL_MASTER_POS, UNTIL_RELAY_POS, UNTIL_GTID
} until_condition;
char until_log_name[FN_REFLEN];
ulonglong until_log_pos;
/* extension extracted from log_name and converted to int */
......@@ -277,6 +279,8 @@ class Relay_log_info : public Slave_reporting_capability
UNTIL_LOG_NAMES_CMP_UNKNOWN= -2, UNTIL_LOG_NAMES_CMP_LESS= -1,
UNTIL_LOG_NAMES_CMP_EQUAL= 0, UNTIL_LOG_NAMES_CMP_GREATER= 1
} until_log_names_cmp_result;
/* Condition for UNTIL master_gtid_pos. */
slave_connection_state until_gtid_pos;
char cached_charset[6];
/*
......@@ -354,6 +358,8 @@ class Relay_log_info : public Slave_reporting_capability
bool is_until_satisfied(THD *thd, Log_event *ev);
inline ulonglong until_pos()
{
DBUG_ASSERT(until_condition == UNTIL_MASTER_POS ||
until_condition == UNTIL_RELAY_POS);
return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos :
group_relay_log_pos);
}
......
......@@ -6543,3 +6543,5 @@ ER_MASTER_GTID_POS_CONFLICTS_WITH_BINLOG
eng "Requested GTID_POS %u-%u-%llu conflicts with the binary log which contains a more recent GTID %u-%u-%llu. To use the requested GTID_POS, the old binlog must be removed with RESET MASTER to avoid out-of-order binlog"
ER_MASTER_GTID_POS_MISSING_DOMAIN
eng "Requested GTID_POS contains no value for replication domain %u. This conflicts with the binary log which contains GTID %u-%u-%llu. To use the requested GTID_POS, the old binlog must be removed with RESET MASTER to avoid out-of-order binlog"
ER_UNTIL_REQUIRES_USING_GTID
eng "START SLAVE UNTIL master_gtid_pos requires that slave is using GTID"
......@@ -1879,6 +1879,43 @@ when it try to get the value of TIME_ZONE global variable from master.";
goto err;
}
}
if (mi->rli.until_condition == Relay_log_info::UNTIL_GTID)
{
connect_state.length(0);
connect_state.append(STRING_WITH_LEN("SET @slave_until_gtid='"),
system_charset_info);
if (mi->rli.until_gtid_pos.append_to_string(&connect_state))
{
err_code= ER_OUTOFMEMORY;
errmsg= "The slave I/O thread stops because a fatal out-of-memory "
"error is encountered when it tries to compute @slave_until_gtid.";
sprintf(err_buff, "%s Error: Out of memory", errmsg);
goto err;
}
connect_state.append(STRING_WITH_LEN("'"), system_charset_info);
rc= mysql_real_query(mysql, connect_state.ptr(), connect_state.length());
if (rc)
{
err_code= mysql_errno(mysql);
if (is_network_error(err_code))
{
mi->report(ERROR_LEVEL, err_code,
"Setting @slave_until_gtid failed with error: %s",
mysql_error(mysql));
goto network_err;
}
else
{
/* Fatal error */
errmsg= "The slave I/O thread stops because a fatal error is "
"encountered when it tries to set @slave_until_gtid.";
sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
goto err;
}
}
}
}
if (!mi->using_gtid)
{
......@@ -2363,7 +2400,8 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
protocol->store(
mi->rli.until_condition==Relay_log_info::UNTIL_NONE ? "None":
( mi->rli.until_condition==Relay_log_info::UNTIL_MASTER_POS? "Master":
"Relay"), &my_charset_bin);
( mi->rli.until_condition==Relay_log_info::UNTIL_RELAY_POS? "Relay":
"Gtid")), &my_charset_bin);
protocol->store(mi->rli.until_log_name, &my_charset_bin);
protocol->store((ulonglong) mi->rli.until_log_pos);
......@@ -3057,7 +3095,8 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
This tests if the position of the beginning of the current event
hits the UNTIL barrier.
*/
if (rli->until_condition != Relay_log_info::UNTIL_NONE &&
if ((rli->until_condition == Relay_log_info::UNTIL_MASTER_POS ||
rli->until_condition == Relay_log_info::UNTIL_RELAY_POS) &&
rli->is_until_satisfied(thd, ev))
{
char buf[22];
......@@ -3954,7 +3993,8 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
saved_master_log_pos= rli->group_master_log_pos;
saved_skip= rli->slave_skip_counter;
}
if (rli->until_condition != Relay_log_info::UNTIL_NONE &&
if ((rli->until_condition == Relay_log_info::UNTIL_MASTER_POS ||
rli->until_condition == Relay_log_info::UNTIL_RELAY_POS) &&
rli->is_until_satisfied(thd, NULL))
{
char buf[22];
......@@ -4793,7 +4833,43 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
}
break;
case GTID_LIST_EVENT:
{
const char *errmsg;
Gtid_list_log_event *glev;
Log_event *tmp;
if (mi->rli.until_condition != Relay_log_info::UNTIL_GTID)
goto default_action;
if (!(tmp= Log_event::read_log_event(buf, event_len, &errmsg,
mi->rli.relay_log.description_event_for_queue,
opt_slave_sql_verify_checksum)))
{
error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
goto err;
}
glev= static_cast<Gtid_list_log_event *>(tmp);
if (glev->gl_flags & Gtid_list_log_event::FLAG_UNTIL_REACHED)
{
char str_buf[128];
String str(str_buf, sizeof(str_buf), system_charset_info);
mi->rli.until_gtid_pos.to_string(&str);
sql_print_information("Slave IO thread stops because it reached its"
" UNTIL master_gtid_pos %s", str.c_ptr_safe());
mi->abort_slave= true;
}
delete glev;
/*
Do not update position for fake Gtid_list event (which has a zero
end_log_pos).
*/
inc_pos= uint4korr(buf+LOG_POS_OFFSET) ? event_len : 0;
}
break;
default:
default_action:
inc_pos= event_len;
break;
}
......
......@@ -289,6 +289,8 @@ struct LEX_MASTER_INFO
char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher;
char *relay_log_name;
LEX_STRING connection_name;
/* Value in START SLAVE UNTIL master_gtid_pos=xxx */
LEX_STRING gtid_pos_str;
ulonglong pos;
ulong relay_log_pos;
ulong server_id;
......@@ -317,6 +319,8 @@ struct LEX_MASTER_INFO
heartbeat_period= 0;
ssl= ssl_verify_server_cert= heartbeat_opt=
repl_ignore_server_ids_opt= use_gtid_opt= LEX_MI_UNCHANGED;
gtid_pos_str.length= 0;
gtid_pos_str.str= NULL;
}
};
......
This diff is collapsed.
......@@ -1095,7 +1095,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token LOW_PRIORITY
%token LT /* OPERATOR */
%token MASTER_CONNECT_RETRY_SYM
%token MASTER_USE_GTID_SYM
%token MASTER_GTID_POS_SYM
%token MASTER_HOST_SYM
%token MASTER_LOG_FILE_SYM
%token MASTER_LOG_POS_SYM
......@@ -1111,6 +1111,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token MASTER_SSL_VERIFY_SERVER_CERT_SYM
%token MASTER_SYM
%token MASTER_USER_SYM
%token MASTER_USE_GTID_SYM
%token MASTER_HEARTBEAT_PERIOD_SYM
%token MATCH /* SQL-2003-R */
%token MAX_CONNECTIONS_PER_HOUR
......@@ -7215,6 +7216,10 @@ slave_until:
MYSQL_YYABORT;
}
}
| UNTIL_SYM MASTER_GTID_POS_SYM EQ TEXT_STRING_sys
{
Lex->mi.gtid_pos_str = $4;
}
;
slave_until_opts:
......@@ -13326,12 +13331,13 @@ keyword_sp:
| MAX_ROWS {}
| MASTER_SYM {}
| MASTER_HEARTBEAT_PERIOD_SYM {}
| MASTER_USE_GTID_SYM {}
| MASTER_GTID_POS_SYM {}
| MASTER_HOST_SYM {}
| MASTER_PORT_SYM {}
| MASTER_LOG_FILE_SYM {}
| MASTER_LOG_POS_SYM {}
| MASTER_USER_SYM {}
| MASTER_USE_GTID_SYM {}
| MASTER_PASSWORD_SYM {}
| MASTER_SERVER_ID_SYM {}
| MASTER_CONNECT_RETRY_SYM {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment