diff options
author | Brandon Nesterenko <brandon.nesterenko@mariadb.com> | 2021-07-06 19:08:02 -0600 |
---|---|---|
committer | Brandon Nesterenko <brandon.nesterenko@mariadb.com> | 2021-07-06 19:08:02 -0600 |
commit | 170b5b8aaef97225af86e824b2ca39376f4cbb66 (patch) | |
tree | 27952d5d64e014d781aa31e249fef8fb164fe51f | |
parent | c262ccac027e71f22fc1329cf295a6bf687e4684 (diff) | |
download | mariadb-git-10.5-MDEV-25616.tar.gz |
MDEV-25616: Binlog event for XA COMMIT is generated without matching XA START, replication aborts10.5-MDEV-25616
Problem:
========
If the body of an XA transaction is empty, the XA COMMIT
event will be written into the binlog; however, no other
events will be added that relate to the transaction.
The body of an XA transaction is effectively empty if
all of its statements end in error.
Fix:
====
Ensure that the COMMIT is consistent with the other XA
statements so it is also not written into the binlog
-rw-r--r-- | mysql-test/suite/rpl/r/rpl_xa_empty_transaction.result | 44 | ||||
-rw-r--r-- | mysql-test/suite/rpl/t/rpl_xa_empty_transaction.test | 63 | ||||
-rw-r--r-- | sql/log.cc | 20 |
3 files changed, 125 insertions, 2 deletions
diff --git a/mysql-test/suite/rpl/r/rpl_xa_empty_transaction.result b/mysql-test/suite/rpl/r/rpl_xa_empty_transaction.result new file mode 100644 index 00000000000..2296965d1ad --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_xa_empty_transaction.result @@ -0,0 +1,44 @@ +include/master-slave.inc +[connection master] +CREATE TABLE t1 (a INT PRIMARY KEY) engine=myisam; +#----------------------- +# Case 1: "Empty" XA trx results in no XA statements in binlog +#----------------------- +connection master; +XA START 'x'; +INSERT INTO t1 VALUES (1),(1); +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +XA END 'x'; +XA PREPARE 'x'; +XA COMMIT 'x'; +#----------------------- +# Case 2: "Empty" XA trx with disconnect after prepare results in no XA statements in binlog +#----------------------- +connect con1, 127.0.0.1,root,,test,$MASTER_MYPORT,; +XA START 'x'; +INSERT INTO t1 VALUES (2),(2); +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +XA END 'x'; +XA PREPARE 'x'; +disconnect con1; +connection master; +XA COMMIT 'x'; +ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +******** [master] SHOW BINLOG EVENTS ******** +include/show_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT PRIMARY KEY) engine=myisam +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Annotate_rows # # INSERT INTO t1 VALUES (1),(1) +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Annotate_rows # # INSERT INTO t1 VALUES (2),(2) +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # COMMIT +connection master; +DROP TABLE t1; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_xa_empty_transaction.test b/mysql-test/suite/rpl/t/rpl_xa_empty_transaction.test new file mode 100644 index 00000000000..7be993dbde0 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_xa_empty_transaction.test @@ -0,0 +1,63 @@ +# +# Purpose: +# +# If an XA transaction doesn't result in any changes, this test +# ensures that no XA events are written into the binlog +# +# +# Methodology: +# +# Commit an empty XA transaction and validate that the binlog +# does not have any statements relating to the transaction. To +# create the empty XA transaction, use a body with a single +# statement that results in an error. +# +# +# References: +# MDEV-25616: Binlog event for XA COMMIT is generated without +# matching XA START, replication aborts +# +--source include/master-slave.inc +--source include/have_binlog_format_row.inc + +# --- +# Setup +# --- +CREATE TABLE t1 (a INT PRIMARY KEY) engine=myisam; + +--echo #----------------------- +--echo # Case 1: "Empty" XA trx results in no XA statements in binlog +--echo #----------------------- +--connection master +XA START 'x'; +--error ER_DUP_ENTRY +INSERT INTO t1 VALUES (1),(1); +XA END 'x'; +XA PREPARE 'x'; +XA COMMIT 'x'; + +--echo #----------------------- +--echo # Case 2: "Empty" XA trx with disconnect after prepare results in no XA statements in binlog +--echo #----------------------- +--connect (con1, 127.0.0.1,root,,test,$MASTER_MYPORT,) +XA START 'x'; +--error ER_DUP_ENTRY +INSERT INTO t1 VALUES (2),(2); +XA END 'x'; +XA PREPARE 'x'; + +--disconnect con1 +--connection master + +--error ER_XA_RBROLLBACK +XA COMMIT 'x'; + +--source include/rpl_show_binlog_events.inc + + +# --- +# Cleanup +# --- +--connection master +DROP TABLE t1; +--source include/rpl_end.inc diff --git a/sql/log.cc b/sql/log.cc index d9a271c56bd..28c3f1e78d9 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2194,7 +2194,21 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all) */ if (likely(!error) && ending_trans(thd, all)) { - error= is_preparing_xa(thd) ? + bool is_xa_prepare= is_preparing_xa(thd); + + if (!is_xa_prepare && !thd->transaction->all.is_trx_read_write()) + { + /* + If the transaction is empty, return and do not + log an XA COMMIT, as there will be no XA START + through XA PREPARE binlogged to accompany it. + */ + error= binlog_truncate_trx_cache(thd, cache_mngr, all); + THD_STAGE_INFO(thd, org_stage); + DBUG_RETURN(error); + } + + error= is_xa_prepare ? binlog_commit_flush_xa_prepare(thd, all, cache_mngr) : binlog_commit_flush_trx_cache (thd, all, cache_mngr); } @@ -2280,7 +2294,9 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) } else if (likely(!error)) { - if (ending_trans(thd, all) && trans_cannot_safely_rollback(thd, all)) + if (ending_trans(thd, all) && + trans_cannot_safely_rollback(thd, all) && + thd->transaction->all.is_trx_read_write()) error= binlog_rollback_flush_trx_cache(thd, all, cache_mngr); /* Truncate the cache if: |