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) ...@@ -178,6 +178,7 @@ while ($_rpl_server)
{ {
RESET MASTER; RESET MASTER;
RESET SLAVE; RESET SLAVE;
SET GLOBAL gtid_pos= "";
} }
eval SET auto_increment_increment= $rpl_server_count; eval SET auto_increment_increment= $rpl_server_count;
eval SET auto_increment_offset= $_rpl_server; 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[] = { ...@@ -330,7 +330,7 @@ static SYMBOL symbols[] = {
{ "LOW_PRIORITY", SYM(LOW_PRIORITY)}, { "LOW_PRIORITY", SYM(LOW_PRIORITY)},
{ "MASTER", SYM(MASTER_SYM)}, { "MASTER", SYM(MASTER_SYM)},
{ "MASTER_CONNECT_RETRY", SYM(MASTER_CONNECT_RETRY_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_HOST", SYM(MASTER_HOST_SYM)},
{ "MASTER_LOG_FILE", SYM(MASTER_LOG_FILE_SYM)}, { "MASTER_LOG_FILE", SYM(MASTER_LOG_FILE_SYM)},
{ "MASTER_LOG_POS", SYM(MASTER_LOG_POS_SYM)}, { "MASTER_LOG_POS", SYM(MASTER_LOG_POS_SYM)},
...@@ -345,6 +345,7 @@ static SYMBOL symbols[] = { ...@@ -345,6 +345,7 @@ static SYMBOL symbols[] = {
{ "MASTER_SSL_KEY", SYM(MASTER_SSL_KEY_SYM)}, { "MASTER_SSL_KEY", SYM(MASTER_SSL_KEY_SYM)},
{ "MASTER_SSL_VERIFY_SERVER_CERT", SYM(MASTER_SSL_VERIFY_SERVER_CERT_SYM)}, { "MASTER_SSL_VERIFY_SERVER_CERT", SYM(MASTER_SSL_VERIFY_SERVER_CERT_SYM)},
{ "MASTER_USER", SYM(MASTER_USER_SYM)}, { "MASTER_USER", SYM(MASTER_USER_SYM)},
{ "MASTER_USE_GTID", SYM(MASTER_USE_GTID_SYM)},
{ "MASTER_HEARTBEAT_PERIOD", SYM(MASTER_HEARTBEAT_PERIOD_SYM)}, { "MASTER_HEARTBEAT_PERIOD", SYM(MASTER_HEARTBEAT_PERIOD_SYM)},
{ "MATCH", SYM(MATCH)}, { "MATCH", SYM(MATCH)},
{ "MAX_CONNECTIONS_PER_HOUR", SYM(MAX_CONNECTIONS_PER_HOUR)}, { "MAX_CONNECTIONS_PER_HOUR", SYM(MAX_CONNECTIONS_PER_HOUR)},
......
...@@ -3280,7 +3280,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name, ...@@ -3280,7 +3280,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
there had been an entry (domain_id, server_id, 0). 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)) if (gl_ev.write(&log_file))
goto err; goto err;
...@@ -8718,17 +8718,12 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, ...@@ -8718,17 +8718,12 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
case GTID_LIST_EVENT: case GTID_LIST_EVENT:
if (first_round) if (first_round)
{ {
uint32 i;
Gtid_list_log_event *glev= (Gtid_list_log_event *)ev; Gtid_list_log_event *glev= (Gtid_list_log_event *)ev;
/* Initialise the binlog state from the Gtid_list event. */ /* Initialise the binlog state from the Gtid_list event. */
rpl_global_gtid_binlog_state.reset(); if (rpl_global_gtid_binlog_state.load(glev->list, glev->count))
for (i= 0; i < glev->count; ++i)
{
if (rpl_global_gtid_binlog_state.update(&(glev->list[i])))
goto err2; goto err2;
} }
}
break; break;
case GTID_EVENT: case GTID_EVENT:
......
...@@ -6320,6 +6320,7 @@ Gtid_list_log_event::Gtid_list_log_event(const char *buf, uint event_len, ...@@ -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) : Log_event(buf, description_event), count(0), list(0)
{ {
uint32 i; uint32 i;
uint32 val;
uint8 header_size= description_event->common_header_len; uint8 header_size= description_event->common_header_len;
uint8 post_header_len= description_event->post_header_len[GTID_LIST_EVENT-1]; uint8 post_header_len= description_event->post_header_len[GTID_LIST_EVENT-1];
if (event_len < header_size + post_header_len || 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, ...@@ -6327,7 +6328,9 @@ Gtid_list_log_event::Gtid_list_log_event(const char *buf, uint event_len,
return; return;
buf+= header_size; 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; buf+= 4;
if (event_len - (header_size + post_header_len) < count*element_size || if (event_len - (header_size + post_header_len) < count*element_size ||
(!(list= (rpl_gtid *)my_malloc(count*sizeof(*list) + (count == 0), (!(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, ...@@ -6348,8 +6351,9 @@ Gtid_list_log_event::Gtid_list_log_event(const char *buf, uint event_len,
#ifdef MYSQL_SERVER #ifdef MYSQL_SERVER
Gtid_list_log_event::Gtid_list_log_event(rpl_binlog_state *gtid_set) Gtid_list_log_event::Gtid_list_log_event(rpl_binlog_state *gtid_set,
: count(gtid_set->count()), list(0) uint32 gl_flags_)
: count(gtid_set->count()), gl_flags(gl_flags_), list(0)
{ {
cache_type= EVENT_NO_CACHE; cache_type= EVENT_NO_CACHE;
/* Failure to allocate memory will be caught by is_valid() returning false. */ /* 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) ...@@ -6359,32 +6363,70 @@ Gtid_list_log_event::Gtid_list_log_event(rpl_binlog_state *gtid_set)
gtid_set->get_gtid_list(list, count); gtid_set->get_gtid_list(list, count);
} }
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
bool bool
Gtid_list_log_event::write(IO_CACHE *file) Gtid_list_log_event::to_packet(String *packet)
{ {
uint32 i; uint32 i;
uchar buf[element_size]; uchar *p;
uint32 needed_length;
DBUG_ASSERT(count < 1<<28); DBUG_ASSERT(count < 1<<28);
if (write_header(file, get_data_size())) needed_length= packet->length() + 4 + count*element_size;
return 1; if (packet->reserve(needed_length))
int4store(buf, count & ((1<<28)-1)); return true;
if (wrapper_my_b_safe_write(file, buf, GTID_LIST_HEADER_LEN)) p= (uchar *)packet->ptr() + packet->length();;
return 1; packet->length(needed_length);
int4store(p, (count & ((1<<28)-1)) | gl_flags);
p += 4;
for (i= 0; i < count; ++i) for (i= 0; i < count; ++i)
{ {
int4store(buf, list[i].domain_id); int4store(p, list[i].domain_id);
int4store(buf+4, list[i].server_id); int4store(p+4, list[i].server_id);
int8store(buf+8, list[i].seq_no); int8store(p+8, list[i].seq_no);
if (wrapper_my_b_safe_write(file, buf, element_size)) p += 16;
return 1;
} }
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 void
Gtid_list_log_event::pack_info(THD *thd, Protocol *protocol) 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) ...@@ -6439,12 +6481,24 @@ Gtid_list_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
*/ */
bool bool
Gtid_list_log_event::peek(const char *event_start, uint32 event_len, Gtid_list_log_event::peek(const char *event_start, uint32 event_len,
uint8 checksum_alg,
rpl_gtid **out_gtid_list, uint32 *out_list_len) rpl_gtid **out_gtid_list, uint32 *out_list_len)
{ {
const char *p; const char *p;
uint32 count_field, count; uint32 count_field, count;
rpl_gtid *gtid_list; 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) if (event_len < LOG_EVENT_HEADER_LEN + GTID_LIST_HEADER_LEN)
return true; return true;
p= event_start + LOG_EVENT_HEADER_LEN; p= event_start + LOG_EVENT_HEADER_LEN;
......
...@@ -3116,7 +3116,7 @@ public: ...@@ -3116,7 +3116,7 @@ public:
<td>count</td> <td>count</td>
<td>4 byte unsigned integer</td> <td>4 byte unsigned integer</td>
<td>The lower 28 bits are the number of GTIDs. The upper 4 bits are <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> </tr>
</table> </table>
...@@ -3149,18 +3149,28 @@ public: ...@@ -3149,18 +3149,28 @@ public:
</table> </table>
The three elements in the body repeat COUNT times to form the GTID list. 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 class Gtid_list_log_event: public Log_event
{ {
public: public:
uint32 count; uint32 count;
uint32 gl_flags;
struct rpl_gtid *list; struct rpl_gtid *list;
static const uint element_size= 4+4+8; static const uint element_size= 4+4+8;
static const uint32 FLAG_UNTIL_REACHED= (1<<28);
#ifdef MYSQL_SERVER #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 #ifdef HAVE_REPLICATION
void pack_info(THD *thd, Protocol *protocol); void pack_info(THD *thd, Protocol *protocol);
#endif #endif
...@@ -3173,10 +3183,13 @@ public: ...@@ -3173,10 +3183,13 @@ public:
Log_event_type get_type_code() { return GTID_LIST_EVENT; } Log_event_type get_type_code() { return GTID_LIST_EVENT; }
int get_data_size() { return GTID_LIST_HEADER_LEN + count*element_size; } int get_data_size() { return GTID_LIST_HEADER_LEN + count*element_size; }
bool is_valid() const { return list != NULL; } 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); bool write(IO_CACHE *file);
virtual int do_apply_event(Relay_log_info const *rli);
#endif #endif
static bool peek(const char *event_start, uint32 event_len, static bool peek(const char *event_start, uint32 event_len,
uint8 checksum_alg,
rpl_gtid **out_gtid_list, uint32 *out_list_len); rpl_gtid **out_gtid_list, uint32 *out_list_len);
}; };
......
...@@ -711,6 +711,22 @@ void rpl_binlog_state::free() ...@@ -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() rpl_binlog_state::~rpl_binlog_state()
{ {
free(); free();
...@@ -1116,11 +1132,18 @@ slave_connection_state::remove(const rpl_gtid *in_gtid) ...@@ -1116,11 +1132,18 @@ slave_connection_state::remove(const rpl_gtid *in_gtid)
int int
slave_connection_state::to_string(String *out_str) 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; uint32 i;
bool first; bool first;
out_str->length(0);
first= true; first= true;
for (i= 0; i < hash.records; ++i) for (i= 0; i < hash.records; ++i)
{ {
......
...@@ -142,6 +142,7 @@ struct rpl_binlog_state ...@@ -142,6 +142,7 @@ struct rpl_binlog_state
void reset(); void reset();
void free(); void free();
bool load(struct rpl_gtid *list, uint32 count);
int update(const struct rpl_gtid *gtid); int update(const struct rpl_gtid *gtid);
uint64 seq_no_from_state(); uint64 seq_no_from_state();
int write_to_iocache(IO_CACHE *dest); int write_to_iocache(IO_CACHE *dest);
...@@ -172,6 +173,7 @@ struct slave_connection_state ...@@ -172,6 +173,7 @@ struct slave_connection_state
void remove(const rpl_gtid *gtid); void remove(const rpl_gtid *gtid);
ulong count() const { return hash.records; } ulong count() const { return hash.records; }
int to_string(String *out_str); 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, 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) ...@@ -1096,7 +1096,8 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev)
ulonglong log_pos; ulonglong log_pos;
DBUG_ENTER("Relay_log_info::is_until_satisfied"); 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) if (until_condition == UNTIL_MASTER_POS)
{ {
......
...@@ -263,7 +263,9 @@ public: ...@@ -263,7 +263,9 @@ public:
thread is running). 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]; char until_log_name[FN_REFLEN];
ulonglong until_log_pos; ulonglong until_log_pos;
/* extension extracted from log_name and converted to int */ /* extension extracted from log_name and converted to int */
...@@ -277,6 +279,8 @@ public: ...@@ -277,6 +279,8 @@ public:
UNTIL_LOG_NAMES_CMP_UNKNOWN= -2, UNTIL_LOG_NAMES_CMP_LESS= -1, 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_EQUAL= 0, UNTIL_LOG_NAMES_CMP_GREATER= 1
} until_log_names_cmp_result; } until_log_names_cmp_result;
/* Condition for UNTIL master_gtid_pos. */
slave_connection_state until_gtid_pos;
char cached_charset[6]; char cached_charset[6];
/* /*
...@@ -354,6 +358,8 @@ public: ...@@ -354,6 +358,8 @@ public:
bool is_until_satisfied(THD *thd, Log_event *ev); bool is_until_satisfied(THD *thd, Log_event *ev);
inline ulonglong until_pos() 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 : return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos :
group_relay_log_pos); group_relay_log_pos);
} }
......
...@@ -6543,3 +6543,5 @@ ER_MASTER_GTID_POS_CONFLICTS_WITH_BINLOG ...@@ -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" 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 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" 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 @@ after_set_capability: ...@@ -1879,6 +1879,43 @@ after_set_capability:
goto err; 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) if (!mi->using_gtid)
{ {
...@@ -2363,7 +2400,8 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, ...@@ -2363,7 +2400,8 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
protocol->store( protocol->store(
mi->rli.until_condition==Relay_log_info::UNTIL_NONE ? "None": mi->rli.until_condition==Relay_log_info::UNTIL_NONE ? "None":
( mi->rli.until_condition==Relay_log_info::UNTIL_MASTER_POS? "Master": ( 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(mi->rli.until_log_name, &my_charset_bin);
protocol->store((ulonglong) mi->rli.until_log_pos); protocol->store((ulonglong) mi->rli.until_log_pos);
...@@ -3057,7 +3095,8 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) ...@@ -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 This tests if the position of the beginning of the current event
hits the UNTIL barrier. 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)) rli->is_until_satisfied(thd, ev))
{ {
char buf[22]; char buf[22];
...@@ -3954,7 +3993,8 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, ...@@ -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_master_log_pos= rli->group_master_log_pos;
saved_skip= rli->slave_skip_counter; 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)) rli->is_until_satisfied(thd, NULL))
{ {
char buf[22]; char buf[22];
...@@ -4793,7 +4833,43 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) ...@@ -4793,7 +4833,43 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
} }
break; 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:
default_action:
inc_pos= event_len; inc_pos= event_len;
break; break;
} }
......
...@@ -289,6 +289,8 @@ struct LEX_MASTER_INFO ...@@ -289,6 +289,8 @@ struct LEX_MASTER_INFO
char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher; char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher;
char *relay_log_name; char *relay_log_name;
LEX_STRING connection_name; LEX_STRING connection_name;
/* Value in START SLAVE UNTIL master_gtid_pos=xxx */
LEX_STRING gtid_pos_str;
ulonglong pos; ulonglong pos;
ulong relay_log_pos; ulong relay_log_pos;
ulong server_id; ulong server_id;
...@@ -317,6 +319,8 @@ struct LEX_MASTER_INFO ...@@ -317,6 +319,8 @@ struct LEX_MASTER_INFO
heartbeat_period= 0; heartbeat_period= 0;
ssl= ssl_verify_server_cert= heartbeat_opt= ssl= ssl_verify_server_cert= heartbeat_opt=
repl_ignore_server_ids_opt= use_gtid_opt= LEX_MI_UNCHANGED; 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); ...@@ -1095,7 +1095,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token LOW_PRIORITY %token LOW_PRIORITY
%token LT /* OPERATOR */ %token LT /* OPERATOR */
%token MASTER_CONNECT_RETRY_SYM %token MASTER_CONNECT_RETRY_SYM
%token MASTER_USE_GTID_SYM %token MASTER_GTID_POS_SYM
%token MASTER_HOST_SYM %token MASTER_HOST_SYM
%token MASTER_LOG_FILE_SYM %token MASTER_LOG_FILE_SYM
%token MASTER_LOG_POS_SYM %token MASTER_LOG_POS_SYM
...@@ -1111,6 +1111,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ...@@ -1111,6 +1111,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token MASTER_SSL_VERIFY_SERVER_CERT_SYM %token MASTER_SSL_VERIFY_SERVER_CERT_SYM
%token MASTER_SYM %token MASTER_SYM
%token MASTER_USER_SYM %token MASTER_USER_SYM
%token MASTER_USE_GTID_SYM
%token MASTER_HEARTBEAT_PERIOD_SYM %token MASTER_HEARTBEAT_PERIOD_SYM
%token MATCH /* SQL-2003-R */ %token MATCH /* SQL-2003-R */
%token MAX_CONNECTIONS_PER_HOUR %token MAX_CONNECTIONS_PER_HOUR
...@@ -7215,6 +7216,10 @@ slave_until: ...@@ -7215,6 +7216,10 @@ slave_until:
MYSQL_YYABORT; MYSQL_YYABORT;
} }
} }
| UNTIL_SYM MASTER_GTID_POS_SYM EQ TEXT_STRING_sys
{
Lex->mi.gtid_pos_str = $4;
}
; ;
slave_until_opts: slave_until_opts:
...@@ -13326,12 +13331,13 @@ keyword_sp: ...@@ -13326,12 +13331,13 @@ keyword_sp:
| MAX_ROWS {} | MAX_ROWS {}
| MASTER_SYM {} | MASTER_SYM {}
| MASTER_HEARTBEAT_PERIOD_SYM {} | MASTER_HEARTBEAT_PERIOD_SYM {}
| MASTER_USE_GTID_SYM {} | MASTER_GTID_POS_SYM {}
| MASTER_HOST_SYM {} | MASTER_HOST_SYM {}
| MASTER_PORT_SYM {} | MASTER_PORT_SYM {}
| MASTER_LOG_FILE_SYM {} | MASTER_LOG_FILE_SYM {}
| MASTER_LOG_POS_SYM {} | MASTER_LOG_POS_SYM {}
| MASTER_USER_SYM {} | MASTER_USER_SYM {}
| MASTER_USE_GTID_SYM {}
| MASTER_PASSWORD_SYM {} | MASTER_PASSWORD_SYM {}
| MASTER_SERVER_ID_SYM {} | MASTER_SERVER_ID_SYM {}
| MASTER_CONNECT_RETRY_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