mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-12179: Per-engine mysql.gtid_slave_pos table
Intermediate commit. This commit implements that record_gtid() selects a gtid_slave_posXXX table with a storage engine already in use by current transaction, if any. The default table mysql.gtid_slave_pos is used if no match can be found on storage engine, or for GTID position updates with no specific storage engine. Table discovery of mysql.gtid_slave_pos* happens on initial GTID state load as well as on every START SLAVE. Some effort is made to make this possible without additional locking. New tables are added using lock-free atomics. Removing tables requires stopping all slaves first. A warning is given in the error log when a table is removed but a non-stopped slave still has a reference to it. If multiple mysql.gtid_slave_posXXX tables with same storage engine exist, one is chosen arbitrarily to be used, with a warning in the error log. GTID data from all tables is still read, but only one among redundant tables with same storage engine will be updated.
This commit is contained in:
141
mysql-test/suite/multi_source/gtid_slave_pos.result
Normal file
141
mysql-test/suite/multi_source/gtid_slave_pos.result
Normal file
@ -0,0 +1,141 @@
|
||||
connect slave1,127.0.0.1,root,,,$SERVER_MYPORT_3;
|
||||
connect master1,127.0.0.1,root,,,$SERVER_MYPORT_1;
|
||||
connect master2,127.0.0.1,root,,,$SERVER_MYPORT_2;
|
||||
connection slave1;
|
||||
CHANGE MASTER 'slave1' TO master_port=MYPORT_1, master_host='127.0.0.1', master_user='root', master_use_gtid=slave_pos;
|
||||
CHANGE MASTER 'slave2' TO master_port=MYPORT_2, master_host='127.0.0.1', master_user='root', master_use_gtid=slave_pos;
|
||||
set default_master_connection = 'slave1';
|
||||
START SLAVE;
|
||||
include/wait_for_slave_to_start.inc
|
||||
set default_master_connection = 'slave2';
|
||||
START SLAVE;
|
||||
include/wait_for_slave_to_start.inc
|
||||
set default_master_connection = '';
|
||||
connection master1;
|
||||
SET GLOBAL gtid_domain_id= 1;
|
||||
SET SESSION gtid_domain_id= 1;
|
||||
CREATE TABLE t3 (a INT PRIMARY KEY, b VARCHAR(10)) ENGINE=InnoDB;
|
||||
CREATE TABLE t1 (a INT PRIMARY KEY, b VARCHAR(10));
|
||||
INSERT INTO t1 VALUES (1, "initial");
|
||||
INSERT INTO t3 VALUES (101, "initial 1");
|
||||
include/save_master_gtid.inc
|
||||
connection master2;
|
||||
SET GLOBAL gtid_domain_id= 2;
|
||||
SET SESSION gtid_domain_id= 2;
|
||||
CREATE TABLE t2 (a INT PRIMARY KEY, b VARCHAR(10)) ENGINE=InnoDB;
|
||||
INSERT INTO t2 VALUES (1, "initial");
|
||||
connection slave1;
|
||||
include/sync_with_master_gtid.inc
|
||||
connection master2;
|
||||
include/save_master_gtid.inc
|
||||
connection slave1;
|
||||
include/sync_with_master_gtid.inc
|
||||
*** Add an innodb gtid_slave_pos table. It is not used yet as slaves are already running ***
|
||||
SET sql_log_bin=0;
|
||||
CREATE TABLE mysql.gtid_slave_pos_innodb LIKE mysql.gtid_slave_pos;
|
||||
ALTER TABLE mysql.gtid_slave_pos_innodb ENGINE=InnoDB;
|
||||
SET sql_log_bin=0;
|
||||
connection master1;
|
||||
INSERT INTO t3 VALUES (102, "secondary");
|
||||
include/save_master_gtid.inc
|
||||
connection slave1;
|
||||
include/sync_with_master_gtid.inc
|
||||
SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
|
||||
domain_id max(seq_no)
|
||||
1 5
|
||||
2 2
|
||||
SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos_innodb GROUP BY domain_id;
|
||||
domain_id max(seq_no)
|
||||
*** Restart one slave thread, the other keeps running. Now the new table is used ***
|
||||
connection slave1;
|
||||
set default_master_connection = 'slave1';
|
||||
STOP SLAVE;
|
||||
include/wait_for_slave_to_stop.inc
|
||||
START SLAVE;
|
||||
include/wait_for_slave_to_start.inc
|
||||
connection master2;
|
||||
INSERT INTO t2 VALUES (2, "secondary2");
|
||||
include/save_master_gtid.inc
|
||||
connection slave1;
|
||||
include/sync_with_master_gtid.inc
|
||||
SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
|
||||
domain_id max(seq_no)
|
||||
1 5
|
||||
2 2
|
||||
SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos_innodb GROUP BY domain_id;
|
||||
domain_id max(seq_no)
|
||||
2 3
|
||||
*** Remove a gtid_slave_posXXX table, restart one slave ***
|
||||
*** Get a warning that the change is not yet picked up ***
|
||||
*** See that updates fail due to trying to use the missing table ***
|
||||
connection slave1;
|
||||
SET sql_log_bin=0;
|
||||
DROP TABLE mysql.gtid_slave_pos_innodb;
|
||||
SET sql_log_bin=1;
|
||||
set default_master_connection = 'slave2';
|
||||
STOP SLAVE;
|
||||
include/wait_for_slave_to_stop.inc
|
||||
START SLAVE;
|
||||
include/wait_for_slave_to_start.inc
|
||||
CALL mtr.add_suppression("The table mysql.gtid_slave_pos_innodb was removed.");
|
||||
connection master2;
|
||||
INSERT INTO t2 VALUES (3, "tertiary 2");
|
||||
include/save_master_gtid.inc
|
||||
connection slave1;
|
||||
include/wait_for_slave_sql_error.inc [errno=1942]
|
||||
SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
|
||||
domain_id max(seq_no)
|
||||
1 5
|
||||
2 2
|
||||
*** Stop both slaves, see that the drop of mysql.gtid_slave_pos_innodb is now picked up ***
|
||||
connection slave1;
|
||||
set default_master_connection = 'slave1';
|
||||
STOP SLAVE;
|
||||
include/wait_for_slave_to_stop.inc
|
||||
set default_master_connection = 'slave2';
|
||||
STOP SLAVE;
|
||||
include/wait_for_slave_to_stop.inc
|
||||
set default_master_connection = 'slave1';
|
||||
START SLAVE;
|
||||
include/wait_for_slave_to_start.inc
|
||||
set default_master_connection = 'slave2';
|
||||
START SLAVE;
|
||||
include/wait_for_slave_to_start.inc
|
||||
include/sync_with_master_gtid.inc
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
a b
|
||||
1 initial
|
||||
SELECT * FROM t2 ORDER BY a;
|
||||
a b
|
||||
1 initial
|
||||
2 secondary2
|
||||
3 tertiary 2
|
||||
SELECT * FROM t3 ORDER BY a;
|
||||
a b
|
||||
101 initial 1
|
||||
102 secondary
|
||||
SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
|
||||
domain_id max(seq_no)
|
||||
1 5
|
||||
2 4
|
||||
connection master1;
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t3;
|
||||
connection master2;
|
||||
DROP TABLE t2;
|
||||
connection slave1;
|
||||
SET GLOBAL gtid_domain_id=0;
|
||||
STOP ALL SLAVES;
|
||||
Warnings:
|
||||
Note 1938 SLAVE 'slave1' stopped
|
||||
Note 1938 SLAVE 'slave2' stopped
|
||||
include/reset_master_slave.inc
|
||||
disconnect slave1;
|
||||
connection master1;
|
||||
SET GLOBAL gtid_domain_id=0;
|
||||
include/reset_master_slave.inc
|
||||
disconnect master1;
|
||||
connection master2;
|
||||
SET GLOBAL gtid_domain_id=0;
|
||||
include/reset_master_slave.inc
|
||||
disconnect master2;
|
156
mysql-test/suite/multi_source/gtid_slave_pos.test
Normal file
156
mysql-test/suite/multi_source/gtid_slave_pos.test
Normal file
@ -0,0 +1,156 @@
|
||||
--source include/not_embedded.inc
|
||||
--source include/have_innodb.inc
|
||||
|
||||
#
|
||||
# Test multiple mysql.gtid_slave_posXXX tables with multiple master connections
|
||||
#
|
||||
|
||||
--connect (slave1,127.0.0.1,root,,,$SERVER_MYPORT_3)
|
||||
--connect (master1,127.0.0.1,root,,,$SERVER_MYPORT_1)
|
||||
--connect (master2,127.0.0.1,root,,,$SERVER_MYPORT_2)
|
||||
|
||||
--connection slave1
|
||||
--replace_result $SERVER_MYPORT_1 MYPORT_1
|
||||
eval CHANGE MASTER 'slave1' TO master_port=$SERVER_MYPORT_1, master_host='127.0.0.1', master_user='root', master_use_gtid=slave_pos;
|
||||
--replace_result $SERVER_MYPORT_2 MYPORT_2
|
||||
eval CHANGE MASTER 'slave2' TO master_port=$SERVER_MYPORT_2, master_host='127.0.0.1', master_user='root', master_use_gtid=slave_pos;
|
||||
set default_master_connection = 'slave1';
|
||||
START SLAVE;
|
||||
--source include/wait_for_slave_to_start.inc
|
||||
set default_master_connection = 'slave2';
|
||||
START SLAVE;
|
||||
--source include/wait_for_slave_to_start.inc
|
||||
set default_master_connection = '';
|
||||
|
||||
|
||||
--connection master1
|
||||
SET GLOBAL gtid_domain_id= 1;
|
||||
SET SESSION gtid_domain_id= 1;
|
||||
CREATE TABLE t3 (a INT PRIMARY KEY, b VARCHAR(10)) ENGINE=InnoDB;
|
||||
CREATE TABLE t1 (a INT PRIMARY KEY, b VARCHAR(10));
|
||||
INSERT INTO t1 VALUES (1, "initial");
|
||||
INSERT INTO t3 VALUES (101, "initial 1");
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection master2
|
||||
SET GLOBAL gtid_domain_id= 2;
|
||||
SET SESSION gtid_domain_id= 2;
|
||||
CREATE TABLE t2 (a INT PRIMARY KEY, b VARCHAR(10)) ENGINE=InnoDB;
|
||||
INSERT INTO t2 VALUES (1, "initial");
|
||||
|
||||
|
||||
--connection slave1
|
||||
--source include/sync_with_master_gtid.inc
|
||||
|
||||
--connection master2
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection slave1
|
||||
--source include/sync_with_master_gtid.inc
|
||||
|
||||
|
||||
--echo *** Add an innodb gtid_slave_pos table. It is not used yet as slaves are already running ***
|
||||
|
||||
SET sql_log_bin=0;
|
||||
CREATE TABLE mysql.gtid_slave_pos_innodb LIKE mysql.gtid_slave_pos;
|
||||
ALTER TABLE mysql.gtid_slave_pos_innodb ENGINE=InnoDB;
|
||||
SET sql_log_bin=0;
|
||||
|
||||
--connection master1
|
||||
INSERT INTO t3 VALUES (102, "secondary");
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection slave1
|
||||
--source include/sync_with_master_gtid.inc
|
||||
SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
|
||||
SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos_innodb GROUP BY domain_id;
|
||||
|
||||
--echo *** Restart one slave thread, the other keeps running. Now the new table is used ***
|
||||
--connection slave1
|
||||
set default_master_connection = 'slave1';
|
||||
STOP SLAVE;
|
||||
--source include/wait_for_slave_to_stop.inc
|
||||
START SLAVE;
|
||||
--source include/wait_for_slave_to_start.inc
|
||||
|
||||
--connection master2
|
||||
INSERT INTO t2 VALUES (2, "secondary2");
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection slave1
|
||||
--source include/sync_with_master_gtid.inc
|
||||
SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
|
||||
SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos_innodb GROUP BY domain_id;
|
||||
|
||||
--echo *** Remove a gtid_slave_posXXX table, restart one slave ***
|
||||
--echo *** Get a warning that the change is not yet picked up ***
|
||||
--echo *** See that updates fail due to trying to use the missing table ***
|
||||
--connection slave1
|
||||
SET sql_log_bin=0;
|
||||
DROP TABLE mysql.gtid_slave_pos_innodb;
|
||||
SET sql_log_bin=1;
|
||||
set default_master_connection = 'slave2';
|
||||
STOP SLAVE;
|
||||
--source include/wait_for_slave_to_stop.inc
|
||||
START SLAVE;
|
||||
--source include/wait_for_slave_to_start.inc
|
||||
CALL mtr.add_suppression("The table mysql.gtid_slave_pos_innodb was removed.");
|
||||
|
||||
--connection master2
|
||||
INSERT INTO t2 VALUES (3, "tertiary 2");
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection slave1
|
||||
--let $slave_sql_errno= 1942
|
||||
--source include/wait_for_slave_sql_error.inc
|
||||
SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
|
||||
|
||||
--echo *** Stop both slaves, see that the drop of mysql.gtid_slave_pos_innodb is now picked up ***
|
||||
--connection slave1
|
||||
set default_master_connection = 'slave1';
|
||||
STOP SLAVE;
|
||||
--source include/wait_for_slave_to_stop.inc
|
||||
set default_master_connection = 'slave2';
|
||||
STOP SLAVE;
|
||||
--source include/wait_for_slave_to_stop.inc
|
||||
set default_master_connection = 'slave1';
|
||||
START SLAVE;
|
||||
--source include/wait_for_slave_to_start.inc
|
||||
set default_master_connection = 'slave2';
|
||||
START SLAVE;
|
||||
--source include/wait_for_slave_to_start.inc
|
||||
|
||||
--source include/sync_with_master_gtid.inc
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
SELECT * FROM t2 ORDER BY a;
|
||||
SELECT * FROM t3 ORDER BY a;
|
||||
SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
|
||||
|
||||
|
||||
# Cleanup.
|
||||
--connection master1
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t3;
|
||||
|
||||
--connection master2
|
||||
DROP TABLE t2;
|
||||
|
||||
--connection slave1
|
||||
SET GLOBAL gtid_domain_id=0;
|
||||
--let $wait_condition= SELECT COUNT(*)=0 FROM information_schema.tables WHERE table_name IN ("t1", "t2", "t3") AND table_schema = "test"
|
||||
--source include/wait_condition.inc
|
||||
--sorted_result
|
||||
STOP ALL SLAVES;
|
||||
--source include/reset_master_slave.inc
|
||||
--disconnect slave1
|
||||
|
||||
|
||||
--connection master1
|
||||
SET GLOBAL gtid_domain_id=0;
|
||||
--source include/reset_master_slave.inc
|
||||
--disconnect master1
|
||||
|
||||
--connection master2
|
||||
SET GLOBAL gtid_domain_id=0;
|
||||
--source include/reset_master_slave.inc
|
||||
--disconnect master2
|
Reference in New Issue
Block a user