Commit ee2b7db3 authored by unknown's avatar unknown

MDEV-4478: Implement GTID "strict mode"

When @@GLOBAL.gtid_strict_mode=1, then certain operations result
in error that would otherwise result in out-of-order binlog files
between servers.

GTID sequence numbers are now allocated independently per domain;
this results in less/no holes in GTID sequences, increasing the
likelyhood that diverging binlogs will be caught by the slave when
GTID strict mode is enabled.
parent f5319394
......@@ -207,6 +207,10 @@ The following options may be given as the first argument:
multiple masters), each independent source server must
use a distinct domain_id. For simple tree-shaped
replication topologies, it can be left at its default, 0.
--gtid-strict-mode Enforce strict seq_no ordering of events in the binary
log. Slave stops with an error if it encounters an event
that would cause it to generate an out-of-order binlog if
executed.
-?, --help Display this help and exit.
--histogram-size=# Number of bytes used for a histogram. If set to 0, no
histograms are created by ANALYZE.
......@@ -966,6 +970,7 @@ gdb FALSE
general-log FALSE
group-concat-max-len 1024
gtid-domain-id 0
gtid-strict-mode FALSE
help TRUE
histogram-size 0
histogram-type SINGLE_PREC_HB
......
......@@ -55,7 +55,6 @@ wait/synch/mutex/sql/LOCK_global_index_stats
wait/synch/mutex/sql/LOCK_global_system_variables
wait/synch/mutex/sql/LOCK_global_table_stats
wait/synch/mutex/sql/LOCK_global_user_client_stats
wait/synch/mutex/sql/LOCK_gtid_counter
wait/synch/mutex/sql/LOCK_open
wait/synch/mutex/sql/LOCK_plugin
wait/synch/mutex/sql/LOCK_prepared_stmt_count
......
......@@ -41,7 +41,7 @@ master-bin.000002 #
master-bin.000003 #
SHOW BINLOG EVENTS IN 'master-bin.000003' LIMIT 1,1;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000003 # Gtid_list # # [1-1-3,2-1-4,0-1-1]
master-bin.000003 # Gtid_list # # [1-1-2,2-1-1,0-1-1]
SET SESSION debug_dbug="+d,crash_dispatch_command_before";
SELECT 1;
Got one of the listed errors
......@@ -53,7 +53,7 @@ master-bin.000003 #
master-bin.000004 #
SHOW BINLOG EVENTS IN 'master-bin.000004' LIMIT 1,1;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000004 # Gtid_list # # [1-1-3,0-1-1,2-1-4]
master-bin.000004 # Gtid_list # # [1-1-2,0-1-1,2-1-1]
SELECT * FROM t1 ORDER BY a;
a
1
......
......@@ -46,6 +46,12 @@ INSERT INTO t1 VALUES (100);
SET GLOBAL gtid_slave_pos = "100-100-100";
ERROR 25000: You are not allowed to execute this command in a transaction
ROLLBACK;
SET GLOBAL gtid_strict_mode= 1;
SET GLOBAL gtid_slave_pos = "0-1-1";
ERROR HY000: Specified GTID 0-1-1 conflicts with the binary log which contains a more recent GTID 0-2-11. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos.
SET GLOBAL gtid_slave_pos = "";
ERROR HY000: Specified value for @@gtid_slave_pos contains no value for replication domain 0. This conflicts with the binary log which contains GTID 0-2-11. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos.
SET GLOBAL gtid_strict_mode= 0;
SET GLOBAL gtid_slave_pos = "0-1-1";
Warnings:
Warning 1947 Specified GTID 0-1-1 conflicts with the binary log which contains a more recent GTID 0-2-11. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos.
......
......@@ -41,10 +41,10 @@ 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";
START SLAVE UNTIL master_gtid_pos= "1-1-4,2-1-4,3-1-4";
START SLAVE UNTIL master_gtid_pos= "1-1-1,2-1-4,3-1-7";
START SLAVE UNTIL master_gtid_pos= "2-1-1,3-1-4,1-1-7";
START SLAVE UNTIL master_gtid_pos= "3-1-1,1-1-4,2-1-7";
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
......@@ -119,7 +119,7 @@ a b
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";
START SLAVE UNTIL master_gtid_pos = "1-1-1,0-1-3,3-1-7,2-1-4";
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
......@@ -142,7 +142,7 @@ a b
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";
START SLAVE UNTIL master_gtid_pos = "1-1-7,0-1-3,3-1-4,2-1-1";
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
......@@ -168,7 +168,7 @@ a b
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";
START SLAVE UNTIL master_gtid_pos = "1-1-4,0-1-3,3-1-1,2-1-7";
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
......
......@@ -32,10 +32,10 @@ master-bin.000003 # Gtid_list # # [0-1-3]
FLUSH LOGS;
SHOW BINLOG EVENTS IN 'master-bin.000004' LIMIT 1,1;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000004 # Gtid_list # # [1-1-5,0-1-4]
master-bin.000004 # Gtid_list # # [1-1-1,0-1-4]
SHOW BINLOG EVENTS IN 'master-bin.000005' LIMIT 1,1;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000005 # Gtid_list # # [1-1-5,0-1-4]
master-bin.000005 # Gtid_list # # [1-1-1,0-1-4]
show binary logs;
Log_name File_size
master-bin.000002 #
......
include/rpl_init.inc [topology=1->2]
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
SET sql_log_bin= 0;
call mtr.add_suppression("Error writing file .*errno: 1950");
SET sql_log_bin= 1;
SET @old_gtid_strict_mode= @@GLOBAL.gtid_strict_mode;
SET GLOBAL gtid_strict_mode= 1;
include/stop_slave.inc
SET @old_gtid_strict_mode= @@GLOBAL.gtid_strict_mode;
SET GLOBAL gtid_strict_mode=1;
CHANGE MASTER TO master_use_gtid=slave_pos;
include/start_slave.inc
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1);
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (1)
master-bin.000001 # Xid # # COMMIT /* XID */
SET server_id= 3;
SET gtid_seq_no= 3;
ERROR HY000: An attempt was made to binlog GTID 0-3-3 which would create an out-of-order sequence number with existing GTID 0-1-3, and gtid strict mode is enabled.
SET SESSION debug_dbug="+d,ignore_set_gtid_seq_no_check";
SET gtid_seq_no= 3;
SET SESSION debug_dbug="-d,ignore_set_gtid_seq_no_check";
INSERT INTO t1 VALUES (2);
ERROR HY000: An attempt was made to binlog GTID 0-3-3 which would create an out-of-order sequence number with existing GTID 0-1-3, and gtid strict mode is enabled.
SET gtid_seq_no= 2;
ERROR HY000: An attempt was made to binlog GTID 0-3-2 which would create an out-of-order sequence number with existing GTID 0-1-3, and gtid strict mode is enabled.
SET SESSION debug_dbug="+d,ignore_set_gtid_seq_no_check";
SET gtid_seq_no= 2;
SET SESSION debug_dbug="-d,ignore_set_gtid_seq_no_check";
INSERT INTO t1 VALUES (3);
ERROR HY000: An attempt was made to binlog GTID 0-3-2 which would create an out-of-order sequence number with existing GTID 0-1-3, and gtid strict mode is enabled.
SET server_id= 1;
SET gtid_seq_no= 4;
INSERT INTO t1 VALUES (4);
SELECT * FROM t1 ORDER BY 1;
a
1
4
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (1)
master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (4)
master-bin.000001 # Xid # # COMMIT /* XID */
*** Test non-transactional GTID error (cannot be rolled back). ***
SET server_id= 3;
SET gtid_seq_no= 1;
ERROR HY000: An attempt was made to binlog GTID 0-3-1 which would create an out-of-order sequence number with existing GTID 0-1-4, and gtid strict mode is enabled.
SET SESSION debug_dbug="+d,ignore_set_gtid_seq_no_check";
SET gtid_seq_no= 1;
SET SESSION debug_dbug="-d,ignore_set_gtid_seq_no_check";
CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=MyISAM;
ERROR HY000: An attempt was made to binlog GTID 0-3-1 which would create an out-of-order sequence number with existing GTID 0-1-4, and gtid strict mode is enabled.
SET sql_log_bin= 0;
DROP TABLE t2;
SET sql_log_bin= 1;
CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=MyISAM;
SET gtid_seq_no= 1;
ERROR HY000: An attempt was made to binlog GTID 0-3-1 which would create an out-of-order sequence number with existing GTID 0-3-5, and gtid strict mode is enabled.
SET SESSION debug_dbug="+d,ignore_set_gtid_seq_no_check";
SET gtid_seq_no= 1;
SET SESSION debug_dbug="-d,ignore_set_gtid_seq_no_check";
INSERT INTO t2 VALUES (1);
ERROR HY000: An attempt was made to binlog GTID 0-3-1 which would create an out-of-order sequence number with existing GTID 0-3-5, and gtid strict mode is enabled.
SET server_id= 1;
SET gtid_seq_no= 6;
INSERT INTO t2 VALUES (2);
SELECT * FROM t2 ORDER BY a;
a
1
2
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (1)
master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (4)
master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=MyISAM
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t2 VALUES (2)
master-bin.000001 # Query # # COMMIT
*** Test that slave stops if it tries to apply a GTID that would create out-of-order binlog GTID sequence numbers. ***
SELECT * FROM t1 ORDER BY a;
a
1
4
SELECT * FROM t2 ORDER BY a;
a
2
SET sql_log_bin= 0;
call mtr.add_suppression("An attempt was made to binlog GTID .* which would create an out-of-order sequence number with existing GTID .*, and gtid strict mode is enabled");
call mtr.add_suppression("The binlog on the master is missing the GTID [-0-9]+ requested by the slave");
SET sql_log_bin= 1;
INSERT INTO t1 VALUES (5);
INSERT INTO t1 VALUES (6);
include/wait_for_slave_sql_error.inc [errno=1950]
STOP SLAVE IO_THREAD;
SET GLOBAL gtid_strict_mode=0;
include/start_slave.inc
SET GLOBAL gtid_strict_mode=1;
SELECT * FROM t1 ORDER BY a;
a
1
4
5
6
INSERT INTO t1 VALUES (7);
CREATE TABLE t3 (a INT PRIMARY KEY);
include/wait_for_slave_sql_error.inc [errno=1950]
SHOW CREATE TABLE t3;
ERROR 42S02: Table 'test.t3' doesn't exist
STOP SLAVE IO_THREAD;
SET GLOBAL gtid_strict_mode=0;
include/start_slave.inc
SET GLOBAL gtid_strict_mode=1;
SHOW CREATE TABLE t3;
Table t3
Create Table CREATE TABLE `t3` (
`a` int(11) NOT NULL,
PRIMARY KEY (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
INSERT INTO t1 VALUES (8);
INSERT INTO t2 VALUES (3);
include/wait_for_slave_sql_error.inc [errno=1950]
SELECT * FROM t2 ORDER BY a;
a
2
STOP SLAVE IO_THREAD;
SET GLOBAL gtid_strict_mode=0;
include/start_slave.inc
SET GLOBAL gtid_strict_mode=1;
SELECT * FROM t2 ORDER BY a;
a
2
3
*** Check slave requests starting from a hole on the master. ***
include/stop_slave.inc
INSERT INTO t1 VALUES (10);
SET gtid_seq_no= 100;
INSERT INTO t1 VALUES (11);
INSERT INTO t1 VALUES (12);
SET GLOBAL gtid_slave_pos= "0-1-50";
START SLAVE;
include/wait_for_slave_io_error.inc [errno=1236]
STOP SLAVE SQL_THREAD;
SET GLOBAL gtid_strict_mode= 0;
include/start_slave.inc
SELECT * FROM t1 ORDER BY a;
a
1
4
5
6
7
8
11
12
SET GLOBAL gtid_strict_mode= 1;
DROP TABLE t1, t2, t3;
SET GLOBAL gtid_strict_mode= @old_gtid_strict_mode;
SET GLOBAL gtid_strict_mode= @old_gtid_strict_mode;
include/rpl_end.inc
......@@ -79,7 +79,7 @@ master-bin.000001 #
master-bin.000002 #
master-bin.000003 #
master-bin.000004 #
START SLAVE UNTIL master_gtid_pos='1-1-11,2-1-12';
START SLAVE UNTIL master_gtid_pos='1-1-3,2-1-4';
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
......@@ -93,7 +93,7 @@ a
3
4
5
START SLAVE UNTIL master_gtid_pos='1-1-13,2-1-8';
START SLAVE UNTIL master_gtid_pos='1-1-4,2-1-2';
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
......@@ -108,7 +108,7 @@ a
3
4
5
START SLAVE UNTIL master_gtid_pos='1-1-11';
START SLAVE UNTIL master_gtid_pos='1-1-3';
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
......@@ -144,7 +144,7 @@ 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';
START SLAVE UNTIL master_gtid_pos='1-1-4';
include/wait_for_slave_to_stop.inc
SELECT * FROM t1 ORDER BY a;
a
......@@ -168,7 +168,7 @@ a
include/stop_slave.inc
CREATE TABLE t3 (a INT);
DROP TABLE t3;
START SLAVE UNTIL master_gtid_pos='1-1-15,2-1-14,0-1-16';
START SLAVE UNTIL master_gtid_pos='1-1-5,2-1-5,0-1-6';
include/wait_for_slave_to_stop.inc
SHOW CREATE TABLE t3;
Table Create Table
......
......@@ -80,7 +80,13 @@ SET GLOBAL gtid_slave_pos = "100-100-100";
ROLLBACK;
# In gtid non-strict mode, we get warnings for setting @@gtid_slave_pos back
# to earlier than what is in the binlog.
# to earlier than what is in the binlog. In strict mode, we get an error.
SET GLOBAL gtid_strict_mode= 1;
--error ER_MASTER_GTID_POS_CONFLICTS_WITH_BINLOG
SET GLOBAL gtid_slave_pos = "0-1-1";
--error ER_MASTER_GTID_POS_MISSING_DOMAIN
SET GLOBAL gtid_slave_pos = "";
SET GLOBAL gtid_strict_mode= 0;
SET GLOBAL gtid_slave_pos = "0-1-1";
SET GLOBAL gtid_slave_pos = "";
RESET MASTER;
......
--source include/have_debug.inc
--source include/have_innodb.inc
--source include/have_binlog_format_statement.inc
--let $rpl_topology=1->2
--source include/rpl_init.inc
--connection server_1
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
SET sql_log_bin= 0;
call mtr.add_suppression("Error writing file .*errno: 1950");
SET sql_log_bin= 1;
SET @old_gtid_strict_mode= @@GLOBAL.gtid_strict_mode;
SET GLOBAL gtid_strict_mode= 1;
--connection server_2
--source include/stop_slave.inc
SET @old_gtid_strict_mode= @@GLOBAL.gtid_strict_mode;
SET GLOBAL gtid_strict_mode=1;
CHANGE MASTER TO master_use_gtid=slave_pos;
--source include/start_slave.inc
--connection server_1
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1);
--source include/show_binlog_events.inc
SET server_id= 3;
--error ER_GTID_STRICT_OUT_OF_ORDER
SET gtid_seq_no= 3;
SET SESSION debug_dbug="+d,ignore_set_gtid_seq_no_check";
SET gtid_seq_no= 3;
SET SESSION debug_dbug="-d,ignore_set_gtid_seq_no_check";
--error ER_GTID_STRICT_OUT_OF_ORDER
INSERT INTO t1 VALUES (2);
--error ER_GTID_STRICT_OUT_OF_ORDER
SET gtid_seq_no= 2;
SET SESSION debug_dbug="+d,ignore_set_gtid_seq_no_check";
SET gtid_seq_no= 2;
SET SESSION debug_dbug="-d,ignore_set_gtid_seq_no_check";
--error ER_GTID_STRICT_OUT_OF_ORDER
INSERT INTO t1 VALUES (3);
SET server_id= 1;
SET gtid_seq_no= 4;
INSERT INTO t1 VALUES (4);
SELECT * FROM t1 ORDER BY 1;
--source include/show_binlog_events.inc
--echo *** Test non-transactional GTID error (cannot be rolled back). ***
SET server_id= 3;
--error ER_GTID_STRICT_OUT_OF_ORDER
SET gtid_seq_no= 1;
SET SESSION debug_dbug="+d,ignore_set_gtid_seq_no_check";
SET gtid_seq_no= 1;
SET SESSION debug_dbug="-d,ignore_set_gtid_seq_no_check";
--error ER_GTID_STRICT_OUT_OF_ORDER
CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=MyISAM;
# The table is still created, DDL cannot be rolled back.
# Fix it up for replication.
SET sql_log_bin= 0;
DROP TABLE t2;
SET sql_log_bin= 1;
CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=MyISAM;
--error ER_GTID_STRICT_OUT_OF_ORDER
SET gtid_seq_no= 1;
SET SESSION debug_dbug="+d,ignore_set_gtid_seq_no_check";
SET gtid_seq_no= 1;
SET SESSION debug_dbug="-d,ignore_set_gtid_seq_no_check";
--error ER_GTID_STRICT_OUT_OF_ORDER
INSERT INTO t2 VALUES (1);
# The value is still inserted, cannot be rolled back.
SET server_id= 1;
SET gtid_seq_no= 6;
INSERT INTO t2 VALUES (2);
SELECT * FROM t2 ORDER BY a;
--source include/show_binlog_events.inc
--echo *** Test that slave stops if it tries to apply a GTID that would create out-of-order binlog GTID sequence numbers. ***
--save_master_pos
--connection server_2
--sync_with_master
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SET sql_log_bin= 0;
call mtr.add_suppression("An attempt was made to binlog GTID .* which would create an out-of-order sequence number with existing GTID .*, and gtid strict mode is enabled");
call mtr.add_suppression("The binlog on the master is missing the GTID [-0-9]+ requested by the slave");
SET sql_log_bin= 1;
# Create some out-of-order stuff on slave.
INSERT INTO t1 VALUES (5);
--connection server_1
INSERT INTO t1 VALUES (6);
--save_master_pos
--connection server_2
--let $slave_sql_errno=1950
--source include/wait_for_slave_sql_error.inc
STOP SLAVE IO_THREAD;
SET GLOBAL gtid_strict_mode=0;
--source include/start_slave.inc
--sync_with_master
SET GLOBAL gtid_strict_mode=1;
SELECT * FROM t1 ORDER BY a;
INSERT INTO t1 VALUES (7);
--connection server_1
CREATE TABLE t3 (a INT PRIMARY KEY);
--save_master_pos
--connection server_2
--let $slave_sql_errno=1950
--source include/wait_for_slave_sql_error.inc
--error ER_NO_SUCH_TABLE
--query_vertical SHOW CREATE TABLE t3
STOP SLAVE IO_THREAD;
SET GLOBAL gtid_strict_mode=0;
--source include/start_slave.inc
--sync_with_master
SET GLOBAL gtid_strict_mode=1;
--query_vertical SHOW CREATE TABLE t3
INSERT INTO t1 VALUES (8);
--connection server_1
INSERT INTO t2 VALUES (3);
--save_master_pos
--connection server_2
--let $slave_sql_errno=1950
--source include/wait_for_slave_sql_error.inc
SELECT * FROM t2 ORDER BY a;
STOP SLAVE IO_THREAD;
SET GLOBAL gtid_strict_mode=0;
--source include/start_slave.inc
--sync_with_master
SET GLOBAL gtid_strict_mode=1;
SELECT * FROM t2 ORDER BY a;
--echo *** Check slave requests starting from a hole on the master. ***
--connection server_2
--source include/stop_slave.inc
--connection server_1
INSERT INTO t1 VALUES (10);
SET gtid_seq_no= 100;
INSERT INTO t1 VALUES (11);
INSERT INTO t1 VALUES (12);
--save_master_pos
--connection server_2
SET GLOBAL gtid_slave_pos= "0-1-50";
START SLAVE;
--let $slave_io_errno=1236
--source include/wait_for_slave_io_error.inc
STOP SLAVE SQL_THREAD;
SET GLOBAL gtid_strict_mode= 0;
--source include/start_slave.inc
--sync_with_master
SELECT * FROM t1 ORDER BY a;
SET GLOBAL gtid_strict_mode= 1;
# Clean up.
--connection server_1
DROP TABLE t1, t2, t3;
SET GLOBAL gtid_strict_mode= @old_gtid_strict_mode;
--connection server_2
SET GLOBAL gtid_strict_mode= @old_gtid_strict_mode;
--source include/rpl_end.inc
select @@global.gtid_strict_mode;
@@global.gtid_strict_mode
0
select @@session.gtid_strict_mode;
ERROR HY000: Variable 'gtid_strict_mode' is a GLOBAL variable
show global variables like 'gtid_strict_mode';
Variable_name Value
gtid_strict_mode OFF
show session variables like 'gtid_strict_mode';
Variable_name Value
gtid_strict_mode OFF
select * from information_schema.global_variables where variable_name='gtid_strict_mode';
VARIABLE_NAME VARIABLE_VALUE
GTID_STRICT_MODE OFF
select * from information_schema.session_variables where variable_name='gtid_strict_mode';
VARIABLE_NAME VARIABLE_VALUE
GTID_STRICT_MODE OFF
SET @old= @@GLOBAL.gtid_strict_mode;
set global gtid_strict_mode=1;
select @@global.gtid_strict_mode;
@@global.gtid_strict_mode
1
set global gtid_strict_mode=0;
select @@global.gtid_strict_mode;
@@global.gtid_strict_mode
0
set global gtid_strict_mode=@old;
set session gtid_strict_mode=1;
ERROR HY000: Variable 'gtid_strict_mode' is a GLOBAL variable and should be set with SET GLOBAL
--source include/not_embedded.inc
#
# only global
#
select @@global.gtid_strict_mode;
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
select @@session.gtid_strict_mode;
show global variables like 'gtid_strict_mode';
show session variables like 'gtid_strict_mode';
select * from information_schema.global_variables where variable_name='gtid_strict_mode';
select * from information_schema.session_variables where variable_name='gtid_strict_mode';
SET @old= @@GLOBAL.gtid_strict_mode;
set global gtid_strict_mode=1;
select @@global.gtid_strict_mode;
set global gtid_strict_mode=0;
select @@global.gtid_strict_mode;
set global gtid_strict_mode=@old;
--error ER_GLOBAL_VARIABLE
set session gtid_strict_mode=1;
......@@ -3841,9 +3841,6 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log)
if (!is_relay_log)
{
rpl_global_gtid_binlog_state.reset();
mysql_mutex_lock(&LOCK_gtid_counter);
global_gtid_counter= 0;
mysql_mutex_unlock(&LOCK_gtid_counter);
}
/* Start logging with a new file */
......@@ -5367,9 +5364,11 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone,
bool is_transactional)
{
rpl_gtid gtid;
uint64 seq_no;
uint32 domain_id= thd->variables.gtid_domain_id;
uint32 server_id= thd->variables.server_id;
uint64 seq_no= thd->variables.gtid_seq_no;
int err;
seq_no= thd->variables.gtid_seq_no;
/*
Reset the session variable gtid_seq_no, to reduce the risk of accidentally
producing a duplicate GTID.
......@@ -5377,34 +5376,36 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone,
thd->variables.gtid_seq_no= 0;
if (seq_no != 0)
{
/*
If we see a higher sequence number, use that one as the basis of any
later generated sequence numbers.
*/
bump_seq_no_counter_if_needed(seq_no);
/* Use the specified sequence number. */
gtid.domain_id= domain_id;
gtid.server_id= server_id;
gtid.seq_no= seq_no;
mysql_mutex_lock(&LOCK_rpl_gtid_state);
err= rpl_global_gtid_binlog_state.update(&gtid, opt_gtid_strict_mode);
mysql_mutex_unlock(&LOCK_rpl_gtid_state);
if (err && thd->stmt_da->sql_errno()==ER_GTID_STRICT_OUT_OF_ORDER)
errno= ER_GTID_STRICT_OUT_OF_ORDER;
}
else
{
mysql_mutex_lock(&LOCK_gtid_counter);
seq_no= ++global_gtid_counter;
mysql_mutex_unlock(&LOCK_gtid_counter);
/* Allocate the next sequence number for the GTID. */
mysql_mutex_lock(&LOCK_rpl_gtid_state);
err= rpl_global_gtid_binlog_state.update_with_next_gtid(domain_id,
server_id, &gtid);
mysql_mutex_unlock(&LOCK_rpl_gtid_state);
seq_no= gtid.seq_no;
}
gtid.seq_no= seq_no;
gtid.domain_id= thd->variables.gtid_domain_id;
if (err)
return true;
Gtid_log_event gtid_event(thd, gtid.seq_no, gtid.domain_id, standalone,
Gtid_log_event gtid_event(thd, seq_no, domain_id, standalone,
LOG_EVENT_SUPPRESS_USE_F, is_transactional);
gtid.server_id= gtid_event.server_id;
/* Write the event to the binary log. */
if (gtid_event.write(&mysql_bin_log.log_file))
return true;
status_var_add(thd->status_var.binlog_bytes_written, gtid_event.data_written);
/* Update the replication state (last GTID in each replication domain). */
mysql_mutex_lock(&LOCK_rpl_gtid_state);
rpl_global_gtid_binlog_state.update(&gtid);
mysql_mutex_unlock(&LOCK_rpl_gtid_state);
return false;
}
......@@ -5505,9 +5506,6 @@ end:
end_io_cache(&cache);
if (opened)
mysql_file_close(file_no, MYF(0));
/* Pick the next unused seq_no from the loaded binlog state. */
bump_seq_no_counter_if_needed(
rpl_global_gtid_binlog_state.seq_no_from_state());
return err;
}
......@@ -5549,33 +5547,44 @@ bool
MYSQL_BIN_LOG::lookup_domain_in_binlog_state(uint32 domain_id,
rpl_gtid *out_gtid)
{
rpl_binlog_state::element *elem;
bool res;
rpl_gtid *found_gtid;
bool res= false;
mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
elem= (rpl_binlog_state::element *)
my_hash_search(&rpl_global_gtid_binlog_state.hash,
(const uchar *)&domain_id, 0);
if (elem)
if ((found_gtid= rpl_global_gtid_binlog_state.find_most_recent(domain_id)))
{
*out_gtid= *found_gtid;
res= true;
*out_gtid= *elem->last_gtid;
}
else
res= false;
mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
return res;
}
void
MYSQL_BIN_LOG::bump_seq_no_counter_if_needed(uint64 seq_no)
int
MYSQL_BIN_LOG::bump_seq_no_counter_if_needed(uint32 domain_id, uint64 seq_no)
{
int err;
mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
err= rpl_global_gtid_binlog_state.bump_seq_no_if_needed(domain_id, seq_no);
mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
return err;
}
bool
MYSQL_BIN_LOG::check_strict_gtid_sequence(uint32 domain_id, uint32 server_id,
uint64 seq_no)
{
mysql_mutex_lock(&LOCK_gtid_counter);
if (global_gtid_counter < seq_no)
global_gtid_counter= seq_no;
mysql_mutex_unlock(&LOCK_gtid_counter);
bool err;
mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
err= rpl_global_gtid_binlog_state.check_strict_sequence(domain_id, server_id,
seq_no);
mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
return err;
}
......@@ -5648,7 +5657,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
my_org_b_tell= my_b_tell(file);
mysql_mutex_lock(&LOCK_log);
prev_binlog_id= current_binlog_id;
write_gtid_event(thd, true, using_trans);
if (write_gtid_event(thd, true, using_trans))
goto err;
}
else
{
......@@ -6717,8 +6727,8 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
*/
DBUG_ASSERT(!cache_mngr->stmt_cache.empty() || !cache_mngr->trx_cache.empty());
current->error= write_transaction_or_stmt(current);
if ((current->error= write_transaction_or_stmt(current)))
current->commit_errno= errno;
strmake(cache_mngr->last_commit_pos_file, log_file_name,
sizeof(cache_mngr->last_commit_pos_file)-1);
commit_offset= my_b_write_tell(&log_file);
......@@ -8264,9 +8274,6 @@ int TC_LOG_BINLOG::open(const char *opt_name)
error= recover(&log_info, log_name, &log,
(Format_description_log_event *)ev);
state_read= true;
/* Pick the next unused seq_no from the recovered binlog state. */
bump_seq_no_counter_if_needed(
rpl_global_gtid_binlog_state.seq_no_from_state());
}
else
error= read_state_from_file();
......@@ -8748,7 +8755,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
gtid.domain_id= gev->domain_id;
gtid.server_id= gev->server_id;
gtid.seq_no= gev->seq_no;
if (rpl_global_gtid_binlog_state.update(&gtid))
if (rpl_global_gtid_binlog_state.update(&gtid, false))
goto err2;
}
break;
......
......@@ -783,7 +783,9 @@ public:
bool find_in_binlog_state(uint32 domain_id, uint32 server_id,
rpl_gtid *out_gtid);
bool lookup_domain_in_binlog_state(uint32 domain_id, rpl_gtid *out_gtid);
void bump_seq_no_counter_if_needed(uint64 seq_no);
int bump_seq_no_counter_if_needed(uint32 domain_id, uint64 seq_no);
bool check_strict_gtid_sequence(uint32 domain_id, uint32 server_id,
uint64 seq_no);
};
class Log_event_handler
......
......@@ -6214,6 +6214,15 @@ Gtid_log_event::do_apply_event(Relay_log_info const *rli)
thd->variables.gtid_domain_id= this->domain_id;
thd->variables.gtid_seq_no= this->seq_no;
if (opt_gtid_strict_mode && opt_bin_log && opt_log_slave_updates)
{
/* Need to reset prior "ok" status to give an error. */
thd->clear_error();
thd->stmt_da->reset_diagnostics_area();
if (mysql_bin_log.check_strict_gtid_sequence(this->domain_id,
this->server_id, this->seq_no))
return 1;
}
if (flags2 & FL_STANDALONE)
return 0;
......
......@@ -677,7 +677,7 @@ mysql_mutex_t
mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats,
LOCK_global_table_stats, LOCK_global_index_stats;
mysql_mutex_t LOCK_gtid_counter, LOCK_rpl_gtid_state;
mysql_mutex_t LOCK_rpl_gtid_state;
/**
The below lock protects access to two global server variables:
......@@ -776,7 +776,7 @@ PSI_mutex_key key_LOCK_stats,
key_LOCK_global_index_stats,
key_LOCK_wakeup_ready;
PSI_mutex_key key_LOCK_gtid_counter, key_LOCK_rpl_gtid_state;
PSI_mutex_key key_LOCK_rpl_gtid_state;
PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered;
......@@ -821,7 +821,6 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_global_table_stats, "LOCK_global_table_stats", PSI_FLAG_GLOBAL},
{ &key_LOCK_global_index_stats, "LOCK_global_index_stats", PSI_FLAG_GLOBAL},
{ &key_LOCK_wakeup_ready, "THD::LOCK_wakeup_ready", 0},
{ &key_LOCK_gtid_counter, "LOCK_gtid_counter", PSI_FLAG_GLOBAL},
{ &key_LOCK_rpl_gtid_state, "LOCK_rpl_gtid_state", PSI_FLAG_GLOBAL},
{ &key_LOCK_thd_data, "THD::LOCK_thd_data", 0},
{ &key_LOCK_user_conn, "LOCK_user_conn", PSI_FLAG_GLOBAL},
......@@ -1294,10 +1293,7 @@ struct st_VioSSLFd *ssl_acceptor_fd;
*/
uint connection_count= 0, extra_connection_count= 0;
/**
Running counter for generating new GTIDs locally.
*/
uint64 global_gtid_counter= 0;
my_bool opt_gtid_strict_mode= FALSE;
/* Function declarations */
......@@ -1977,7 +1973,6 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_global_user_client_stats);
mysql_mutex_destroy(&LOCK_global_table_stats);
mysql_mutex_destroy(&LOCK_global_index_stats);
mysql_mutex_destroy(&LOCK_gtid_counter);
mysql_mutex_destroy(&LOCK_rpl_gtid_state);
#ifdef HAVE_OPENSSL
mysql_mutex_destroy(&LOCK_des_key_file);
......@@ -4089,8 +4084,6 @@ static int init_thread_environment()
&LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_global_index_stats,
&LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_gtid_counter,
&LOCK_gtid_counter, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_rpl_gtid_state,
&LOCK_rpl_gtid_state, MY_MUTEX_INIT_SLOW);
mysql_mutex_init(key_LOCK_prepare_ordered, &LOCK_prepare_ordered,
......
......@@ -253,7 +253,7 @@ extern PSI_mutex_key key_LOCK_stats,
key_LOCK_global_user_client_stats, key_LOCK_global_table_stats,
key_LOCK_global_index_stats, key_LOCK_wakeup_ready;
extern PSI_mutex_key key_LOCK_gtid_counter, key_LOCK_rpl_gtid_state;
extern PSI_mutex_key key_LOCK_rpl_gtid_state;
extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger,
key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave,
......@@ -345,7 +345,7 @@ extern mysql_mutex_t
LOCK_slave_list, LOCK_active_mi, LOCK_manager,
LOCK_global_system_variables, LOCK_user_conn,
LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count;
extern mysql_mutex_t LOCK_gtid_counter, LOCK_rpl_gtid_state;
extern mysql_mutex_t LOCK_rpl_gtid_state;
extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
#ifdef HAVE_OPENSSL
extern mysql_mutex_t LOCK_des_key_file;
......@@ -541,6 +541,7 @@ extern handlerton *maria_hton;
extern uint extra_connection_count;
extern uint64 global_gtid_counter;
extern my_bool opt_gtid_strict_mode;
extern my_bool opt_userstat_running, debug_assert_if_crashed_table;
extern uint mysqld_extra_port;
extern ulong opt_progress_report_time;
......
......@@ -370,7 +370,10 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
}
table->file->ha_index_end();
mysql_bin_log.bump_seq_no_counter_if_needed(gtid->seq_no);
if(!err && opt_bin_log &&
(err= mysql_bin_log.bump_seq_no_counter_if_needed(gtid->domain_id,
gtid->seq_no)))
my_error(ER_OUT_OF_RESOURCES, MYF(0));
end:
......@@ -719,7 +722,7 @@ rpl_binlog_state::load(struct rpl_gtid *list, uint32 count)
reset();
for (i= 0; i < count; ++i)
{
if (update(&(list[i])))
if (update(&(list[i]), false))
return true;
}
return false;
......@@ -741,48 +744,111 @@ rpl_binlog_state::~rpl_binlog_state()
Returns 0 for ok, 1 for error.
*/
int
rpl_binlog_state::update(const struct rpl_gtid *gtid)
rpl_binlog_state::update(const struct rpl_gtid *gtid, bool strict)
{
rpl_gtid *lookup_gtid;
element *elem;
elem= (element *)my_hash_search(&hash, (const uchar *)(&gtid->domain_id), 0);
if (elem)
if ((elem= (element *)my_hash_search(&hash,
(const uchar *)(&gtid->domain_id), 0)))
{
/*
By far the most common case is that successive events within same
replication domain have the same server id (it changes only when
switching to a new master). So save a hash lookup in this case.
*/
if (likely(elem->last_gtid->server_id == gtid->server_id))
if (strict && elem->last_gtid && elem->last_gtid->seq_no >= gtid->seq_no)
{
elem->last_gtid->seq_no= gtid->seq_no;
return 0;
my_error(ER_GTID_STRICT_OUT_OF_ORDER, MYF(0), gtid->domain_id,
gtid->server_id, gtid->seq_no, elem->last_gtid->domain_id,
elem->last_gtid->server_id, elem->last_gtid->seq_no);
return 1;
}
if (elem->seq_no_counter < gtid->seq_no)
elem->seq_no_counter= gtid->seq_no;
if (!elem->update_element(gtid))
return 0;
}
else if (!alloc_element(gtid))
return 0;
lookup_gtid= (rpl_gtid *)
my_hash_search(&elem->hash, (const uchar *)&gtid->server_id, 0);
if (lookup_gtid)
{
lookup_gtid->seq_no= gtid->seq_no;
elem->last_gtid= lookup_gtid;
my_error(ER_OUT_OF_RESOURCES, MYF(0));
return 1;
}
/*
Fill in a new GTID, allocating next sequence number, and update state
accordingly.
*/
int
rpl_binlog_state::update_with_next_gtid(uint32 domain_id, uint32 server_id,
rpl_gtid *gtid)
{
element *elem;
gtid->domain_id= domain_id;
gtid->server_id= server_id;
if ((elem= (element *)my_hash_search(&hash, (const uchar *)(&domain_id), 0)))
{
gtid->seq_no= ++elem->seq_no_counter;
if (!elem->update_element(gtid))
return 0;
}
}
else
{
gtid->seq_no= 1;
if (!alloc_element(gtid))
return 0;
}
/* Allocate a new GTID and insert it. */
lookup_gtid= (rpl_gtid *)my_malloc(sizeof(*lookup_gtid), MYF(MY_WME));
if (!lookup_gtid)
return 1;
memcpy(lookup_gtid, gtid, sizeof(*lookup_gtid));
if (my_hash_insert(&elem->hash, (const uchar *)lookup_gtid))
{
my_free(lookup_gtid);
return 1;
}
elem->last_gtid= lookup_gtid;
my_error(ER_OUT_OF_RESOURCES, MYF(0));
return 1;
}
/* Helper functions for update. */
int
rpl_binlog_state::element::update_element(const rpl_gtid *gtid)
{
rpl_gtid *lookup_gtid;
/*
By far the most common case is that successive events within same
replication domain have the same server id (it changes only when
switching to a new master). So save a hash lookup in this case.
*/
if (likely(last_gtid && last_gtid->server_id == gtid->server_id))
{
last_gtid->seq_no= gtid->seq_no;
return 0;
}
lookup_gtid= (rpl_gtid *)
my_hash_search(&hash, (const uchar *)&gtid->server_id, 0);
if (lookup_gtid)
{
lookup_gtid->seq_no= gtid->seq_no;
last_gtid= lookup_gtid;
return 0;
}
/* Allocate a new GTID and insert it. */
lookup_gtid= (rpl_gtid *)my_malloc(sizeof(*lookup_gtid), MYF(MY_WME));
if (!lookup_gtid)
return 1;
memcpy(lookup_gtid, gtid, sizeof(*lookup_gtid));
if (my_hash_insert(&hash, (const uchar *)lookup_gtid))
{
my_free(lookup_gtid);
return 1;
}
last_gtid= lookup_gtid;
return 0;
}
int
rpl_binlog_state::alloc_element(const rpl_gtid *gtid)
{
element *elem;
rpl_gtid *lookup_gtid;
/* First time we see this domain_id; allocate a new element. */
elem= (element *)my_malloc(sizeof(*elem), MYF(MY_WME));
lookup_gtid= (rpl_gtid *)my_malloc(sizeof(*lookup_gtid), MYF(MY_WME));
......@@ -793,6 +859,7 @@ rpl_binlog_state::update(const struct rpl_gtid *gtid)
offsetof(rpl_gtid, server_id), sizeof(uint32), NULL, my_free,
HASH_UNIQUE);
elem->last_gtid= lookup_gtid;
elem->seq_no_counter= gtid->seq_no;
memcpy(lookup_gtid, gtid, sizeof(*lookup_gtid));
if (0 == my_hash_insert(&elem->hash, (const uchar *)lookup_gtid))
{
......@@ -812,23 +879,64 @@ rpl_binlog_state::update(const struct rpl_gtid *gtid)
}
uint64
rpl_binlog_state::seq_no_from_state()
/*
Check that a new GTID can be logged without creating an out-of-order
sequence number with existing GTIDs.
*/
bool
rpl_binlog_state::check_strict_sequence(uint32 domain_id, uint32 server_id,
uint64 seq_no)
{
ulong i, j;
uint64 seq_no= 0;
element *elem;
for (i= 0; i < hash.records; ++i)
if ((elem= (element *)my_hash_search(&hash,
(const uchar *)(&domain_id), 0)) &&
elem->last_gtid && elem->last_gtid->seq_no >= seq_no)
{
element *e= (element *)my_hash_element(&hash, i);
for (j= 0; j < e->hash.records; ++j)
{
const rpl_gtid *gtid= (const rpl_gtid *)my_hash_element(&e->hash, j);
if (gtid->seq_no > seq_no)
seq_no= gtid->seq_no;
}
my_error(ER_GTID_STRICT_OUT_OF_ORDER, MYF(0), domain_id, server_id, seq_no,
elem->last_gtid->domain_id, elem->last_gtid->server_id,
elem->last_gtid->seq_no);
return 1;
}
return seq_no;
return 0;
}
/*
When we see a new GTID that will not be binlogged (eg. slave thread
with --log-slave-updates=0), then we need to remember to allocate any
GTID seq_no of our own within that domain starting from there.
Returns 0 if ok, non-zero if out-of-memory.
*/
int
rpl_binlog_state::bump_seq_no_if_needed(uint32 domain_id, uint64 seq_no)
{
element *elem;
if ((elem= (element *)my_hash_search(&hash, (const uchar *)(&domain_id), 0)))
{
if (elem->seq_no_counter < seq_no)
elem->seq_no_counter= seq_no;
return 0;
}
/* We need to allocate a new, empty element to remember the next seq_no. */
if (!(elem= (element *)my_malloc(sizeof(*elem), MYF(MY_WME))))
return 1;
elem->domain_id= domain_id;
my_hash_init(&elem->hash, &my_charset_bin, 32,
offsetof(rpl_gtid, server_id), sizeof(uint32), NULL, my_free,
HASH_UNIQUE);
elem->last_gtid= NULL;
elem->seq_no_counter= seq_no;
if (0 == my_hash_insert(&hash, (const uchar *)elem))
return 0;
my_hash_free(&elem->hash);
my_free(elem);
return 1;
}
......@@ -849,6 +957,11 @@ rpl_binlog_state::write_to_iocache(IO_CACHE *dest)
{
size_t res;
element *e= (element *)my_hash_element(&hash, i);
if (!e->last_gtid)
{
DBUG_ASSERT(e->hash.records == 0);
continue;
}
for (j= 0; j <= e->hash.records; ++j)
{
const rpl_gtid *gtid;
......@@ -890,7 +1003,7 @@ rpl_binlog_state::read_from_iocache(IO_CACHE *src)
end= buf + res;
if (gtid_parser_helper(&p, end, &gtid))
return 1;
if (update(&gtid))
if (update(&gtid, false))
return 1;
}
return 0;
......@@ -906,6 +1019,17 @@ rpl_binlog_state::find(uint32 domain_id, uint32 server_id)
return (rpl_gtid *)my_hash_search(&elem->hash, (const uchar *)&server_id, 0);
}
rpl_gtid *
rpl_binlog_state::find_most_recent(uint32 domain_id)
{
element *elem;
elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0);
if (elem && elem->last_gtid)
return elem->last_gtid;
return NULL;
}
uint32
rpl_binlog_state::count()
......@@ -929,6 +1053,11 @@ rpl_binlog_state::get_gtid_list(rpl_gtid *gtid_list, uint32 list_size)
for (i= 0; i < hash.records; ++i)
{
element *e= (element *)my_hash_element(&hash, i);
if (!e->last_gtid)
{
DBUG_ASSERT(e->hash.records==0);
continue;
}
for (j= 0; j <= e->hash.records; ++j)
{
const rpl_gtid *gtid;
......@@ -965,16 +1094,22 @@ int
rpl_binlog_state::get_most_recent_gtid_list(rpl_gtid **list, uint32 *size)
{
uint32 i;
uint32 alloc_size, out_size;
*size= hash.records;
if (!(*list= (rpl_gtid *)my_malloc(*size * sizeof(rpl_gtid), MYF(MY_WME))))
alloc_size= hash.records;
if (!(*list= (rpl_gtid *)my_malloc(alloc_size * sizeof(rpl_gtid),
MYF(MY_WME))))
return 1;
for (i= 0; i < *size; ++i)
out_size= 0;
for (i= 0; i < alloc_size; ++i)
{
element *e= (element *)my_hash_element(&hash, i);
memcpy(&((*list)[i]), e->last_gtid, sizeof(rpl_gtid));
if (!e->last_gtid)
continue;
memcpy(&((*list)[out_size++]), e->last_gtid, sizeof(rpl_gtid));
}
*size= out_size;
return 0;
}
......@@ -988,7 +1123,8 @@ rpl_binlog_state::append_pos(String *str)
for (i= 0; i < hash.records; ++i)
{
element *e= (element *)my_hash_element(&hash, i);
if (rpl_slave_state_tostring_helper(str, e->last_gtid, &first))
if (e->last_gtid &&
rpl_slave_state_tostring_helper(str, e->last_gtid, &first))
return true;
}
......
......@@ -131,6 +131,10 @@ struct rpl_binlog_state
HASH hash; /* Containing all server_id for one domain_id */
/* The most recent entry in the hash. */
rpl_gtid *last_gtid;
/* Counter to allocate next seq_no for this domain. */
uint64 seq_no_counter;
int update_element(const rpl_gtid *gtid);
};
/* Mapping from domain_id to collection of elements. */
HASH hash;
......@@ -144,8 +148,12 @@ 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 update(const struct rpl_gtid *gtid, bool strict);
int update_with_next_gtid(uint32 domain_id, uint32 server_id,
rpl_gtid *gtid);
int alloc_element(const rpl_gtid *gtid);
bool check_strict_sequence(uint32 domain_id, uint32 server_id, uint64 seq_no);
int bump_seq_no_if_needed(uint32 domain_id, uint64 seq_no);
int write_to_iocache(IO_CACHE *dest);
int read_from_iocache(IO_CACHE *src);
uint32 count();
......@@ -153,6 +161,7 @@ struct rpl_binlog_state
int get_most_recent_gtid_list(rpl_gtid **list, uint32 *size);
bool append_pos(String *str);
rpl_gtid *find(uint32 domain_id, uint32 server_id);
rpl_gtid *find_most_recent(uint32 domain_id);
};
......
......@@ -1397,7 +1397,6 @@ rpl_load_gtid_slave_state(THD *thd)
HASH hash;
int err= 0;
uint32 i;
uint64 highest_seq_no= 0;
DBUG_ENTER("rpl_load_gtid_slave_state");
rpl_global_gtid_slave_state.lock();
......@@ -1450,8 +1449,6 @@ rpl_load_gtid_slave_state(THD *thd)
DBUG_PRINT("info", ("Read slave state row: %u-%u-%lu sub_id=%lu\n",
(unsigned)domain_id, (unsigned)server_id,
(ulong)seq_no, (ulong)sub_id));
if (seq_no > highest_seq_no)
highest_seq_no= seq_no;
if ((rec= my_hash_search(&hash, (const uchar *)&domain_id, 0)))
{
......@@ -1495,6 +1492,14 @@ rpl_load_gtid_slave_state(THD *thd)
rpl_global_gtid_slave_state.unlock();
goto end;
}
if (opt_bin_log &&
mysql_bin_log.bump_seq_no_counter_if_needed(entry->gtid.domain_id,
entry->gtid.seq_no))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
rpl_global_gtid_slave_state.unlock();
goto end;
}
}
rpl_global_gtid_slave_state.loaded= true;
rpl_global_gtid_slave_state.unlock();
......@@ -1514,7 +1519,6 @@ end:
thd->mdl_context.release_transactional_locks();
}
my_hash_free(&hash);
mysql_bin_log.bump_seq_no_counter_if_needed(highest_seq_no);
DBUG_RETURN(err);
}
......
......@@ -6545,3 +6545,7 @@ ER_MASTER_GTID_POS_MISSING_DOMAIN
eng "Specified value for @@gtid_slave_pos contains no value for replication domain %u. This conflicts with the binary log which contains GTID %u-%u-%llu. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos."
ER_UNTIL_REQUIRES_USING_GTID
eng "START SLAVE UNTIL master_gtid_pos requires that slave is using GTID"
ER_GTID_STRICT_OUT_OF_ORDER
eng "An attempt was made to binlog GTID %u-%u-%llu which would create an out-of-order sequence number with existing GTID %u-%u-%llu, and gtid strict mode is enabled."
ER_GTID_START_FROM_BINLOG_HOLE
eng "The binlog on the master is missing the GTID %u-%u-%llu requested by the slave (even though both a prior and a subsequent sequence number does exist), and GTID strict mode is enabled"
......@@ -1824,8 +1824,8 @@ after_set_capability:
{
int rc;
char str_buf[256];
String connect_state(str_buf, sizeof(str_buf), system_charset_info);
connect_state.length(0);
String query_str(str_buf, sizeof(str_buf), system_charset_info);
query_str.length(0);
/*
Read the master @@GLOBAL.gtid_domain_id variable.
......@@ -1848,9 +1848,9 @@ after_set_capability:
mysql_free_result(master_res);
master_res= NULL;
connect_state.append(STRING_WITH_LEN("SET @slave_connect_state='"),
system_charset_info);
if (rpl_append_gtid_state(&connect_state,
query_str.append(STRING_WITH_LEN("SET @slave_connect_state='"),
system_charset_info);
if (rpl_append_gtid_state(&query_str,
mi->using_gtid ==
Master_info::USE_GTID_CURRENT_POS))
{
......@@ -1860,9 +1860,9 @@ after_set_capability:
sprintf(err_buff, "%s Error: Out of memory", errmsg);
goto err;
}
connect_state.append(STRING_WITH_LEN("'"), system_charset_info);
query_str.append(STRING_WITH_LEN("'"), system_charset_info);
rc= mysql_real_query(mysql, connect_state.ptr(), connect_state.length());
rc= mysql_real_query(mysql, query_str.ptr(), query_str.length());
if (rc)
{
err_code= mysql_errno(mysql);
......@@ -1883,12 +1883,45 @@ after_set_capability:
}
}
query_str.length(0);
if (query_str.append(STRING_WITH_LEN("SET @slave_gtid_strict_mode="),
system_charset_info) ||
query_str.append_ulonglong(opt_gtid_strict_mode != false))
{
err_code= ER_OUTOFMEMORY;
errmsg= "The slave I/O thread stops because a fatal out-of-memory "
"error is encountered when it tries to set @slave_gtid_strict_mode.";
sprintf(err_buff, "%s Error: Out of memory", errmsg);
goto err;
}
rc= mysql_real_query(mysql, query_str.ptr(), query_str.length());
if (rc)
{
err_code= mysql_errno(mysql);
if (is_network_error(err_code))
{
mi->report(ERROR_LEVEL, err_code,
"Setting @slave_gtid_strict_mode 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_gtid_strict_mode.";
sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
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))
query_str.length(0);
query_str.append(STRING_WITH_LEN("SET @slave_until_gtid='"),
system_charset_info);
if (mi->rli.until_gtid_pos.append_to_string(&query_str))
{
err_code= ER_OUTOFMEMORY;
errmsg= "The slave I/O thread stops because a fatal out-of-memory "
......@@ -1896,9 +1929,9 @@ after_set_capability:
sprintf(err_buff, "%s Error: Out of memory", errmsg);
goto err;
}
connect_state.append(STRING_WITH_LEN("'"), system_charset_info);
query_str.append(STRING_WITH_LEN("'"), system_charset_info);
rc= mysql_real_query(mysql, connect_state.ptr(), connect_state.length());
rc= mysql_real_query(mysql, query_str.ptr(), query_str.length());
if (rc)
{
err_code= mysql_errno(mysql);
......
This diff is collapsed.
......@@ -1215,6 +1215,26 @@ static Sys_var_uint Sys_gtid_domain_id(
BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(check_has_super));
static bool check_gtid_seq_no(sys_var *self, THD *thd, set_var *var)
{
uint32 domain_id, server_id;
uint64_t seq_no;
if (check_has_super(self, thd, var))
return true;
domain_id= thd->variables.gtid_domain_id;
server_id= thd->variables.server_id;
seq_no= (uint64)var->value->val_uint();
DBUG_EXECUTE_IF("ignore_set_gtid_seq_no_check", return 0;);
if (opt_gtid_strict_mode && opt_bin_log &&
mysql_bin_log.check_strict_gtid_sequence(domain_id, server_id, seq_no))
return true;
return false;
}
static Sys_var_ulonglong Sys_gtid_seq_no(
"gtid_seq_no",
"Internal server usage, for replication with global transaction id. "
......@@ -1224,7 +1244,7 @@ static Sys_var_ulonglong Sys_gtid_seq_no(
SESSION_ONLY(gtid_seq_no),
NO_CMD_LINE, VALID_RANGE(0, ULONGLONG_MAX), DEFAULT(0),
BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(check_has_super));
ON_CHECK(check_gtid_seq_no));
#ifdef HAVE_REPLICATION
......@@ -1364,6 +1384,15 @@ static Sys_var_gtid_slave_pos Sys_gtid_slave_pos(
"The list of global transaction IDs that were last replicated on the "
"server, one for each replication domain.",
GLOBAL_VAR(opt_gtid_slave_pos_dummy), NO_CMD_LINE);
static Sys_var_mybool Sys_gtid_strict_mode(
"gtid_strict_mode",
"Enforce strict seq_no ordering of events in the binary log. Slave "
"stops with an error if it encounters an event that would cause it to "
"generate an out-of-order binlog if executed.",
GLOBAL_VAR(opt_gtid_strict_mode),
CMD_LINE(OPT_ARG), DEFAULT(FALSE));
#endif
......
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