MDEV-34049: Parallel access to temptable in different domain_id in parallel replication

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.

One concrete consequence seen from this bug was a ROLLBACK on an
InnoDB temporary table running in one domain in parallel with DROP
TEMPORARY TABLE in another domain, causing an assertion inside InnoDB:
InnoDB: Failing assertion: table->get_ref_count() == 0 in
dict_sys_t::remove.

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.

Reviewed-by: Brandon Nesterenko <brandon.nesterenko@mariadb.com>
Signed-off-by: Kristian Nielsen <knielsen@knielsen-hq.org>
This commit is contained in:
Kristian Nielsen 2024-10-25 20:20:10 +02:00
parent 0166c89e02
commit d959acbbf8
3 changed files with 95 additions and 6 deletions

View file

@ -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;

View file

@ -68,6 +68,46 @@ 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
#
# Temporary tables must be prevented from being accessed from multiple threads
# at the same time in parallel replication. Withon one domain_id, this is done
# by running wait_for_prior_commit() before accessing a temporary table. To
# prevent the same temporary table from being accessed in parallel from two
# domains in out-of-order parallel replication, an error must be raised on
# attempt to change the gtid_domain_id while temporary tables are in use in
# a session and binlogged. In row-based binlogging, temporary tables are not
# binlogged, so gtid_domain_id can be freely changed.
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

View file

@ -1806,12 +1806,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;
}