mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
MDEV-33921: Replication breaks when filtering two-phase XA transactions
There are two problems. First, replication fails when XA transactions are used where the slave has replicate_do_db set and the client has touched a different database when running DML such as inserts. This is because XA commands are not treated as keywords, and are thereby not exempt from the replication filter. The effect of this is that during an XA transaction, if its logged “use db” from the master is filtered out by the replication filter, then XA END will be ignored, yet its corresponding XA PREPARE will be executed in an invalid state, thereby breaking replication. Second, if the slave replicates an XA transaction which results in an empty transaction, the XA START through XA PREPARE first phase of the transaction won’t be binlogged, yet the XA COMMIT will be binlogged. This will break replication in chain configurations. The first problem is fixed by treating XA commands in Query_log_event as keywords, thus allowing them to bypass the replication filter. Note that Query_log_event::is_trans_keyword() is changed to accept a new parameter to define its mode, to either check for XA commands or regular transaction commands, but not both. In addition, mysqlbinlog is adapted to use this mode so its --database filter does not remove XA commands from its output. The second problem fixed by overwriting the XA state in the XID cache to be XA_ROLLBACK_ONLY, so at commit time, the server knows to rollback the transaction and skip its binlogging. If the xid cache is cleared before an XA transaction receives its completion command (e.g. on server shutdown), then before reporting ER_XAER_NOTA when the completion command is executed, the filter is first checked if the database is ignored, and if so, the error is ignored. Reviewed By: ============ Kristian Nielsen <knielsen@knielsen-hq.org> Andrei Elkin <andrei.elkin@mariadb.com>
This commit is contained in:
committed by
Brandon Nesterenko
parent
9fdc0e5440
commit
ea9869504d
@@ -1164,6 +1164,84 @@ include/sync_with_master_gtid.inc
|
||||
connection server_1;
|
||||
set @@binlog_format = @sav_binlog_format;
|
||||
set @@global.binlog_format = @sav_binlog_format;
|
||||
#
|
||||
# MDEV-33921.1: If a slave's replication of an XA transaction results in
|
||||
# an empty transaction, e.g. due to replication filters, the slave
|
||||
# should not binlog any part of the XA transaction.
|
||||
connection server_1;
|
||||
create database db1;
|
||||
create database db2;
|
||||
create table db1.t1 (a int) engine=innodb;
|
||||
include/save_master_gtid.inc
|
||||
connection server_3;
|
||||
include/sync_with_master_gtid.inc
|
||||
include/stop_slave.inc
|
||||
connection server_2;
|
||||
include/stop_slave.inc
|
||||
SET @@GLOBAL.replicate_ignore_db= "";
|
||||
SET @@GLOBAL.replicate_do_db= "db2";
|
||||
include/start_slave.inc
|
||||
connection server_1;
|
||||
use db1;
|
||||
XA START "x1";
|
||||
insert into db1.t1 values (1);
|
||||
XA END "x1";
|
||||
XA PREPARE "x1";
|
||||
XA COMMIT "x1";
|
||||
include/save_master_gtid.inc
|
||||
connection server_2;
|
||||
include/sync_with_master_gtid.inc
|
||||
connection server_2;
|
||||
include/save_master_gtid.inc
|
||||
connection server_3;
|
||||
include/start_slave.inc
|
||||
include/sync_with_master_gtid.inc
|
||||
#
|
||||
# 33921.2: If the slave shuts down after "preparing" a filtered-to-empty
|
||||
# XA transaction (and not completing it), then when the respective
|
||||
# XA completion (COMMIT in this test) command is replicated, the slave
|
||||
# should not throw ER_XAER_NOTA. Note that internally, the error is
|
||||
# thrown, but it is ignored because the target db is filtered.
|
||||
connection server_3;
|
||||
include/stop_slave.inc
|
||||
connection server_1;
|
||||
use db1;
|
||||
XA START "x2";
|
||||
insert into db1.t1 values (2);
|
||||
XA END "x2";
|
||||
XA PREPARE "x2";
|
||||
include/save_master_gtid.inc
|
||||
connection server_2;
|
||||
include/sync_with_master_gtid.inc
|
||||
# Connection named slave is needed for reconnection
|
||||
connect slave,localhost,root,,;
|
||||
connect slave1,localhost,root,,;
|
||||
include/rpl_restart_server.inc [server_number=2]
|
||||
connection server_2;
|
||||
include/stop_slave.inc
|
||||
SET @@GLOBAL.replicate_do_db= "db2";
|
||||
include/start_slave.inc
|
||||
connection server_1;
|
||||
XA COMMIT "x2";
|
||||
connection server_2;
|
||||
include/sync_with_master_gtid.inc
|
||||
include/save_master_gtid.inc
|
||||
connection server_3;
|
||||
include/start_slave.inc
|
||||
include/sync_with_master_gtid.inc
|
||||
#
|
||||
# 33921.3: Ensure XA commands are not considered by mysqlbinlog's
|
||||
# --database filter
|
||||
connection server_1;
|
||||
# MYSQL_BINLOG datadir/binlog_file --start-position=pre_xa_pos --database=db2 --result-file=assert_file
|
||||
include/assert_grep.inc [Mysqlbinlog should output all XA commands from the filtered transaction]
|
||||
connection server_2;
|
||||
include/stop_slave.inc
|
||||
SET @@GLOBAL.replicate_do_db="";
|
||||
include/start_slave.inc
|
||||
connection server_1;
|
||||
drop database db1;
|
||||
drop database db2;
|
||||
connection server_1;
|
||||
include/rpl_end.inc
|
||||
# End of rpl_xa_empty_transaction.test
|
||||
|
@@ -32,6 +32,10 @@
|
||||
# MDEV-25616: Binlog event for XA COMMIT is generated without matching
|
||||
# XA START, replication aborts
|
||||
#
|
||||
# MDEV-33921: Replication fails when XA transactions are used where the slave
|
||||
# has replicate_do_db set and the client has touched a different
|
||||
# database when running DML such as inserts.
|
||||
#
|
||||
--source include/have_log_bin.inc
|
||||
|
||||
--let $rpl_server_count= 3
|
||||
@@ -167,6 +171,129 @@ set @@global.binlog_format = row;
|
||||
set @@binlog_format = @sav_binlog_format;
|
||||
set @@global.binlog_format = @sav_binlog_format;
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-33921.1: If a slave's replication of an XA transaction results in
|
||||
--echo # an empty transaction, e.g. due to replication filters, the slave
|
||||
--echo # should not binlog any part of the XA transaction.
|
||||
#
|
||||
# Note that the MDEV-33921 report is actually about that XA END is filtered
|
||||
# out (not executed), and then its corresponding XA PREPARE errors because the
|
||||
# XA state of the transaction is incorrect. This test case inherently tests
|
||||
# both bugs.
|
||||
|
||||
--connection server_1
|
||||
create database db1;
|
||||
create database db2;
|
||||
create table db1.t1 (a int) engine=innodb;
|
||||
--source include/save_master_gtid.inc
|
||||
--connection server_3
|
||||
--source include/sync_with_master_gtid.inc
|
||||
--source include/stop_slave.inc
|
||||
|
||||
--connection server_2
|
||||
--source include/stop_slave.inc
|
||||
SET @@GLOBAL.replicate_ignore_db= "";
|
||||
SET @@GLOBAL.replicate_do_db= "db2";
|
||||
--source include/start_slave.inc
|
||||
|
||||
--connection server_1
|
||||
--let $pre_xa_gtid= `SELECT @@global.gtid_binlog_pos`
|
||||
use db1;
|
||||
XA START "x1";
|
||||
insert into db1.t1 values (1);
|
||||
XA END "x1";
|
||||
XA PREPARE "x1";
|
||||
XA COMMIT "x1";
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection server_2
|
||||
--source include/sync_with_master_gtid.inc
|
||||
--let $slave_binlogged_gtid= `SELECT @@global.gtid_binlog_pos`
|
||||
if (`SELECT strcmp("$slave_binlogged_gtid","$pre_xa_gtid")`)
|
||||
{
|
||||
--die Slave binlogged an empty XA transaction yet should not have
|
||||
}
|
||||
|
||||
--connection server_2
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection server_3
|
||||
--source include/start_slave.inc
|
||||
--source include/sync_with_master_gtid.inc
|
||||
|
||||
--echo #
|
||||
--echo # 33921.2: If the slave shuts down after "preparing" a filtered-to-empty
|
||||
--echo # XA transaction (and not completing it), then when the respective
|
||||
--echo # XA completion (COMMIT in this test) command is replicated, the slave
|
||||
--echo # should not throw ER_XAER_NOTA. Note that internally, the error is
|
||||
--echo # thrown, but it is ignored because the target db is filtered.
|
||||
|
||||
--connection server_3
|
||||
--source include/stop_slave.inc
|
||||
|
||||
--connection server_1
|
||||
--let $pre_xa_gtid= `SELECT @@global.gtid_binlog_pos`
|
||||
|
||||
# Used by mysqlbinlog in part 3
|
||||
--let $pre_xa_pos = query_get_value(SHOW MASTER STATUS, Position, 1)
|
||||
|
||||
use db1;
|
||||
XA START "x2";
|
||||
insert into db1.t1 values (2);
|
||||
XA END "x2";
|
||||
XA PREPARE "x2";
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection server_2
|
||||
--source include/sync_with_master_gtid.inc
|
||||
--let $rpl_server_number= 2
|
||||
--echo # Connection named slave is needed for reconnection
|
||||
--connect(slave,localhost,root,,)
|
||||
--connect(slave1,localhost,root,,)
|
||||
--source include/rpl_restart_server.inc
|
||||
|
||||
--connection server_2
|
||||
--source include/stop_slave.inc
|
||||
SET @@GLOBAL.replicate_do_db= "db2";
|
||||
--source include/start_slave.inc
|
||||
|
||||
--connection server_1
|
||||
XA COMMIT "x2";
|
||||
|
||||
--connection server_2
|
||||
--source include/sync_with_master_gtid.inc
|
||||
--source include/save_master_gtid.inc
|
||||
|
||||
--connection server_3
|
||||
--source include/start_slave.inc
|
||||
--source include/sync_with_master_gtid.inc
|
||||
|
||||
--echo #
|
||||
--echo # 33921.3: Ensure XA commands are not considered by mysqlbinlog's
|
||||
--echo # --database filter
|
||||
--connection server_1
|
||||
--let $datadir= `select @@datadir`
|
||||
--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
|
||||
--let assert_file= $MYSQLTEST_VARDIR/tmp/binlog_decoded.out
|
||||
--echo # MYSQL_BINLOG datadir/binlog_file --start-position=pre_xa_pos --database=db2 --result-file=assert_file
|
||||
--exec $MYSQL_BINLOG $datadir/$binlog_file --start-position=$pre_xa_pos --database=db2 --result-file=$assert_file
|
||||
|
||||
--let assert_text= Mysqlbinlog should output all XA commands from the filtered transaction
|
||||
--let assert_count= 4
|
||||
--let assert_select= XA START|XA END|XA PREPARE|XA COMMIT
|
||||
--source include/assert_grep.inc
|
||||
|
||||
--connection server_2
|
||||
--source include/stop_slave.inc
|
||||
SET @@GLOBAL.replicate_do_db="";
|
||||
--source include/start_slave.inc
|
||||
|
||||
--connection server_1
|
||||
drop database db1;
|
||||
drop database db2;
|
||||
|
||||
|
||||
#
|
||||
# Cleanup
|
||||
--connection server_1
|
||||
|
Reference in New Issue
Block a user