summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrandon Nesterenko <brandon.nesterenko@mariadb.com>2021-07-06 19:08:02 -0600
committerBrandon Nesterenko <brandon.nesterenko@mariadb.com>2021-07-06 19:08:02 -0600
commit170b5b8aaef97225af86e824b2ca39376f4cbb66 (patch)
tree27952d5d64e014d781aa31e249fef8fb164fe51f
parentc262ccac027e71f22fc1329cf295a6bf687e4684 (diff)
downloadmariadb-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.result44
-rw-r--r--mysql-test/suite/rpl/t/rpl_xa_empty_transaction.test63
-rw-r--r--sql/log.cc20
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: