Disallow changing @@gtid_domain_id while a temporary table is open in STATEMENT or MIXED binlog mode. Otherwise, a slave may try to replicate events refering to the same temporary table in parallel, using domain-based out-of-order parallel replication. This is not valid, temporary tables are only available for use within a single thread at a time. Use an existing error code that's somewhat close to the real issue (ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO), to not add a new error code in a GA release. When this is merged to the next GA release, we could optionally introduce a new and more precise error code for an attempt to change the domain_id while temporary tables are open. Signed-off-by: Kristian Nielsen <knielsen@knielsen-hq.org> --- .../suite/rpl/r/rpl_gtid_errorhandling.result | 36 ++++++++++++++++--- .../suite/rpl/t/rpl_gtid_errorhandling.test | 31 ++++++++++++++++ sql/sys_vars.cc | 27 ++++++++++++-- 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_gtid_errorhandling.result b/mysql-test/suite/rpl/r/rpl_gtid_errorhandling.result index 54156685806..5e26bdb0032 100644 --- a/mysql-test/suite/rpl/r/rpl_gtid_errorhandling.result +++ b/mysql-test/suite/rpl/r/rpl_gtid_errorhandling.result @@ -60,6 +60,34 @@ ROLLBACK; SELECT * FROM t1 ORDER BY a; a 1 +SET @old_mode= @@SESSION.binlog_format; +SET SESSION binlog_format= row; +SET SESSION gtid_domain_id= 200; +CREATE TEMPORARY TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB; +SET SESSION gtid_domain_id= 0; +BEGIN; +INSERT INTO t2 VALUES (200); +INSERT INTO t1 SELECT * FROM t2; +COMMIT; +SET SESSION gtid_domain_id= 201; +SET SESSION gtid_domain_id= 0; +DELETE FROM t1 WHERE a=200; +SET SESSION gtid_domain_id= 202; +DROP TEMPORARY TABLE t2; +SET SESSION binlog_format= mixed; +SET SESSION gtid_domain_id= 0; +CREATE TEMPORARY TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1); +SET SESSION gtid_domain_id= 0; +SET SESSION gtid_domain_id= 204; +ERROR HY000: Cannot modify @@session.gtid_domain_id or @@session.gtid_seq_no inside a transaction +SET SESSION binlog_format=statement; +INSERT INTO t2 VALUES (2); +SET SESSION gtid_domain_id= 205; +ERROR HY000: Cannot modify @@session.gtid_domain_id or @@session.gtid_seq_no inside a transaction +DROP TEMPORARY TABLE t2; +SET SESSION gtid_domain_id= @old_domain; +SET SESSION binlog_format= @old_mode; *** Test requesting an explicit GTID position that conflicts with newer GTIDs of our own in the binlog. *** connection slave; include/stop_slave.inc @@ -83,16 +111,16 @@ 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 +ERROR HY000: Specified GTID 0-1-1 conflicts with the binary log which contains a more recent GTID 0-2-17. 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 +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-17. 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 +Warning 1947 Specified GTID 0-1-1 conflicts with the binary log which contains a more recent GTID 0-2-17. 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 = ""; Warnings: -Warning 1948 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 +Warning 1948 Specified value for @@gtid_slave_pos contains no value for replication domain 0. This conflicts with the binary log which contains GTID 0-2-17. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos RESET MASTER; SET GLOBAL gtid_slave_pos = "0-1-1"; START SLAVE; diff --git a/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test b/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test index eec7a275e03..e3ca6df1a6e 100644 --- a/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test +++ b/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test @@ -68,6 +68,37 @@ SELECT * FROM t1 ORDER BY a; ROLLBACK; SELECT * FROM t1 ORDER BY a; +# MDEV-34049: Parallel access to temptable in different domain_id in parallel replication +SET @old_mode= @@SESSION.binlog_format; +SET SESSION binlog_format= row; +SET SESSION gtid_domain_id= 200; +CREATE TEMPORARY TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB; +SET SESSION gtid_domain_id= 0; +BEGIN; +INSERT INTO t2 VALUES (200); +INSERT INTO t1 SELECT * FROM t2; +COMMIT; +SET SESSION gtid_domain_id= 201; +SET SESSION gtid_domain_id= 0; +DELETE FROM t1 WHERE a=200; +SET SESSION gtid_domain_id= 202; +DROP TEMPORARY TABLE t2; + +SET SESSION binlog_format= mixed; +SET SESSION gtid_domain_id= 0; +CREATE TEMPORARY TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1); +SET SESSION gtid_domain_id= 0; +--error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO +SET SESSION gtid_domain_id= 204; +SET SESSION binlog_format=statement; +INSERT INTO t2 VALUES (2); +--error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO +SET SESSION gtid_domain_id= 205; +DROP TEMPORARY TABLE t2; +SET SESSION gtid_domain_id= @old_domain; +SET SESSION binlog_format= @old_mode; + --echo *** Test requesting an explicit GTID position that conflicts with newer GTIDs of our own in the binlog. *** --connection slave diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 115bbdf499b..2ef1e404f85 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1808,12 +1808,33 @@ Sys_pseudo_thread_id( static bool check_gtid_domain_id(sys_var *self, THD *thd, set_var *var) { - if (var->type != OPT_GLOBAL && - error_if_in_trans_or_substatement(thd, + if (var->type != OPT_GLOBAL) + { + if (error_if_in_trans_or_substatement(thd, ER_STORED_FUNCTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO, ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO)) return true; - + /* + All binlogged statements on a temporary table must be binlogged in the + same domain_id; it is not safe to run them in parallel in different + domains, temporary table must be exclusive to a single thread. + In row-based binlogging, temporary tables do not end up in the binlog, + so there is no such issue. + + ToDo: When merging to next (non-GA) release, introduce a more specific + error that describes that the problem is changing gtid_domain_id with + open temporary tables in statement/mixed binlogging mode; it is not + really due to doing it inside a "transaction". + */ + if (thd->has_thd_temporary_tables() && + !thd->is_current_stmt_binlog_format_row() && + var->save_result.ulonglong_value != thd->variables.gtid_domain_id) + { + my_error(ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO, + MYF(0)); + return true; + } + } return false; } -- 2.39.5