[Commits] 83a1930: MDEV-7974 XA transactions.

Alexey Botchkov holyfoot at askmonty.org
Fri Apr 5 14:35:12 EEST 2019


revision-id: 83a19306d2581a7e33f4f6697cefb7f43476773d (mariadb-10.4.3-83-g83a1930)
parent(s): dfa24e558ea9fb1ace29e97383577fe4e18d0939
committer: Alexey Botchkov
timestamp: 2019-04-05 15:34:14 +0400
message:

MDEV-7974 XA transactions.

FLUSH TABLES WITH READ LOCK now blocks the XA COMMIT/ROLLBACK/PREPARE
statements as they write to the binlog/Innodb redo.

---
 mysql-test/main/flush_read_lock.result | 159 ++++++++++++++++++++++++++++++++-
 mysql-test/main/flush_read_lock.test   | 112 ++++++++++++++++++++++-
 mysql-test/main/xa.result              |  60 +++++++++++++
 mysql-test/main/xa.test                |  46 +++++++++-
 sql/transaction.cc                     | 101 ++++++++++++++++++---
 5 files changed, 459 insertions(+), 19 deletions(-)

diff --git a/mysql-test/main/flush_read_lock.result b/mysql-test/main/flush_read_lock.result
index 33dc109..9a2f650 100644
--- a/mysql-test/main/flush_read_lock.result
+++ b/mysql-test/main/flush_read_lock.result
@@ -162,7 +162,19 @@ Success: FTWRL is blocked when 'alter event e1 comment 'test'' is active in anot
 #  --read-only for a discussion why.
 #
 Success: Was able to run 'analyze table t1_base' under FTWRL.
-Success: Was able to run 'analyze table t1_base' with FTWRL active in another connection.
+Timeout in wait_condition.inc for select count(*) = 0 from information_schema.processlist
+where info = "analyze table t1_base"
+Id	User	Host	db	Command	Time	State	Info	Progress
+3	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+4	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+1	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+2	system user		NULL	Daemon	NULL	InnoDB purge coordinator	NULL	0.000
+5	system user		NULL	Daemon	NULL	InnoDB shutdown handler	NULL	0.000
+9	root	localhost	test	Query	31	Waiting for backup lock	analyze table t1_base	0.000
+10	root	localhost	test	Query	0	Init	show full processlist	0.000
+11	root	localhost	test	Sleep	31		NULL	0.000
+12	root	localhost	test	Sleep	31		NULL	0.000
+Error: Wasn't able to run 'analyze table t1_base' with FTWRL active in another connection!
 Success: Was able to run FTWRL while 'analyze table t1_base' was active in another connection.
 #
 # 3) BEGIN, ROLLBACK and COMMIT statements.
@@ -1309,6 +1321,8 @@ unlock tables;
 # Check that XA non-COMMIT statements are not and COMMIT is
 # blocked by active FTWRL in another connection
 #
+# XA COMMIT, XA ROLLBACK and XA PREPARE does take COMMIT lock to ensure
+# that nothing is written to bin log and redo log under FTWRL mode.
 connection con1;
 flush tables with read lock;
 connection default;
@@ -1321,11 +1335,51 @@ connection con1;
 flush tables with read lock;
 connection default;
 xa end 'test1';
-xa prepare 'test1';
-xa rollback 'test1';
+xa prepare 'test1';;
+connection con1;
+Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
+where state = "Waiting for commit lock" and
+info = "xa prepare 'test1'"
+Id	User	Host	db	Command	Time	State	Info	Progress
+3	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+4	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+1	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+2	system user		NULL	Daemon	NULL	InnoDB purge coordinator	NULL	0.000
+5	system user		NULL	Daemon	NULL	InnoDB shutdown handler	NULL	0.000
+9	root	localhost	test	Query	30	Waiting for backup lock	xa prepare 'test1'	0.000
+10	root	localhost	test	Query	0	Init	show full processlist	0.000
+11	root	localhost	test	Sleep	30		NULL	0.000
+12	root	localhost	test	Sleep	63		NULL	0.000
+unlock tables;
+# Switching to connection 'default'.
+connection default;
+# Reap XA PREPARE.
+# Switching to connection 'con1'.
 connection con1;
+flush tables with read lock;
+# Switching to connection 'default'.
+connection default;
+# Send XA ROLLBACK 'test1'
+xa rollback 'test1';
+# Switching to connection 'con1'.
+connection con1;
+# Wait until XA ROLLBACK is blocked.
+Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
+where state = "Waiting for commit lock" and
+info = "xa rollback 'test1'"
+Id	User	Host	db	Command	Time	State	Info	Progress
+3	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+4	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+1	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+2	system user		NULL	Daemon	NULL	InnoDB purge coordinator	NULL	0.000
+5	system user		NULL	Daemon	NULL	InnoDB shutdown handler	NULL	0.000
+9	root	localhost	test	Query	30	Waiting for backup lock	xa rollback 'test1'	0.000
+10	root	localhost	test	Query	0	Init	show full processlist	0.000
+11	root	localhost	test	Sleep	61		NULL	0.000
+12	root	localhost	test	Sleep	94		NULL	0.000
 unlock tables;
 connection default;
+# Reap XA ROLLBACK
 xa start 'test1';
 insert into t3_trans values (1);
 connection con1;
@@ -1333,7 +1387,33 @@ flush tables with read lock;
 connection default;
 connection default;
 xa end 'test1';
+# Send XA PREPARE 'test1'
 xa prepare 'test1';
+# Switching to connection 'con1'.
+connection con1;
+# Wait until XA PREPARE is blocked.
+Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
+where state = "Waiting for commit lock" and
+info = "xa prepare 'test1'"
+Id	User	Host	db	Command	Time	State	Info	Progress
+3	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+4	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+1	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+2	system user		NULL	Daemon	NULL	InnoDB purge coordinator	NULL	0.000
+5	system user		NULL	Daemon	NULL	InnoDB shutdown handler	NULL	0.000
+9	root	localhost	test	Query	31	Waiting for backup lock	xa prepare 'test1'	0.000
+10	root	localhost	test	Query	0	Init	show full processlist	0.000
+11	root	localhost	test	Sleep	92		NULL	0.000
+12	root	localhost	test	Sleep	125		NULL	0.000
+unlock tables;
+# Switching to connection 'default'.
+connection default;
+# Reap XA PREPARE.
+# Switching to connection 'con1'.
+connection con1;
+flush tables with read lock;
+# Switching to connection 'default'.
+connection default;
 # Send:
 xa commit 'test1';;
 connection con1;
@@ -1343,6 +1423,77 @@ connection default;
 # Reap XA COMMIT.
 delete from t3_trans;
 #
+# Check that XA COMMIT / ROLLBACK for prepared transaction from a
+# disconnected session is blocked by active FTWRL in another connection.
+#
+# Create temporary connection for XA transaction.
+connect  con_tmp,localhost,root,,;
+xa start 'test1';
+insert into t3_trans values (1);
+xa end 'test1';
+xa prepare 'test1';
+# Disconnect temporary connection
+disconnect con_tmp;
+# Create temporary connection for XA transaction.
+connect  con_tmp,localhost,root,,;
+xa start 'test2';
+insert into t3_trans values (2);
+xa end 'test2';
+xa prepare 'test2';
+# Disconnect temporary connection
+disconnect con_tmp;
+# Switching to connection 'con1'.
+connection con1;
+flush tables with read lock;
+# Switching to connection 'default'.
+connection default;
+# Send XA ROLLBACK 'test1'
+xa rollback 'test1';
+# Switching to connection 'con1'.
+connection con1;
+# Wait until XA ROLLBACK is blocked.
+Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
+where state = "Waiting for commit lock" and
+info = "xa rollback 'test1'"
+Id	User	Host	db	Command	Time	State	Info	Progress
+3	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+4	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+1	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+2	system user		NULL	Daemon	NULL	InnoDB purge coordinator	NULL	0.000
+5	system user		NULL	Daemon	NULL	InnoDB shutdown handler	NULL	0.000
+9	root	localhost	test	Query	30	Waiting for backup lock	xa rollback 'test1'	0.000
+10	root	localhost	test	Query	0	Init	show full processlist	0.000
+11	root	localhost	test	Sleep	123		NULL	0.000
+12	root	localhost	test	Sleep	157		NULL	0.000
+unlock tables;
+flush tables with read lock;
+# Switching to connection 'default'.
+connection default;
+# Reap XA ROLLBACK
+# Send XA COMMIT
+xa commit 'test2';;
+# Switching to connection 'con1'.
+connection con1;
+# Wait until XA COMMIT is blocked.
+Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
+where state = "Waiting for commit lock" and
+info = "xa commit 'test2'"
+Id	User	Host	db	Command	Time	State	Info	Progress
+3	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+4	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+1	system user		NULL	Daemon	NULL	InnoDB purge worker	NULL	0.000
+2	system user		NULL	Daemon	NULL	InnoDB purge coordinator	NULL	0.000
+5	system user		NULL	Daemon	NULL	InnoDB shutdown handler	NULL	0.000
+9	root	localhost	test	Query	31	Waiting for backup lock	xa commit 'test2'	0.000
+10	root	localhost	test	Query	0	Init	show full processlist	0.000
+11	root	localhost	test	Sleep	154		NULL	0.000
+12	root	localhost	test	Sleep	188		NULL	0.000
+unlock tables;
+# Switching to connection 'default'.
+connection default;
+# Reap XA COMMIT.
+delete from t3_trans;
+#
 # Check that XA COMMIT blocks FTWRL in another connection.
 xa start 'test1';
 insert into t3_trans values (1);
@@ -1412,6 +1563,7 @@ flush tables with read lock;
 # Implicit commits are allowed under FTWRL.
 analyze table t3_trans;
 Table	Op	Msg_type	Msg_text
+test.t3_trans	analyze	status	Engine-independent statistics collected
 test.t3_trans	analyze	status	OK
 unlock tables;
 #
@@ -1425,6 +1577,7 @@ unlock tables;
 connection default;
 # Reap ANALYZE TABLE
 Table	Op	Msg_type	Msg_text
+test.t3_trans	analyze	status	Engine-independent statistics collected
 test.t3_trans	analyze	status	OK
 #
 # 39.1.b) CHECK TABLE for transactional table is compatible with FTWRL.
diff --git a/mysql-test/main/flush_read_lock.test b/mysql-test/main/flush_read_lock.test
index f39dbec..74b7117 100644
--- a/mysql-test/main/flush_read_lock.test
+++ b/mysql-test/main/flush_read_lock.test
@@ -1590,6 +1590,8 @@ unlock tables;
 --echo # Check that XA non-COMMIT statements are not and COMMIT is
 --echo # blocked by active FTWRL in another connection
 --echo #
+--echo # XA COMMIT, XA ROLLBACK and XA PREPARE does take COMMIT lock to ensure
+--echo # that nothing is written to bin log and redo log under FTWRL mode.
 connection $con_aux1;
 flush tables with read lock;
 connection default;
@@ -1602,11 +1604,37 @@ connection $con_aux1;
 flush tables with read lock;
 connection default;
 xa end 'test1';
-xa prepare 'test1';
-xa rollback 'test1';
+--send xa prepare 'test1';
 connection $con_aux1;
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for commit lock" and
+        info = "xa prepare 'test1'";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap XA PREPARE.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Send XA ROLLBACK 'test1'
+--send xa rollback 'test1'
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until XA ROLLBACK is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for commit lock" and
+        info = "xa rollback 'test1'";
+--source include/wait_condition.inc
 unlock tables;
 connection default;
+--echo # Reap XA ROLLBACK
+--reap
 xa start 'test1';
 insert into t3_trans values (1);
 connection $con_aux1;
@@ -1614,7 +1642,27 @@ flush tables with read lock;
 connection default;
 connection default;
 xa end 'test1';
-xa prepare 'test1';
+--echo # Send XA PREPARE 'test1'
+--send xa prepare 'test1'
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until XA PREPARE is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for commit lock" and
+        info = "xa prepare 'test1'";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap XA PREPARE.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+
 --echo # Send:
 --send xa commit 'test1';
 connection $con_aux1;
@@ -1630,6 +1678,64 @@ connection default;
 --reap
 delete from t3_trans;
 --echo #
+--echo # Check that XA COMMIT / ROLLBACK for prepared transaction from a
+--echo # disconnected session is blocked by active FTWRL in another connection.
+--echo #
+--echo # Create temporary connection for XA transaction.
+connect (con_tmp,localhost,root,,);
+xa start 'test1';
+insert into t3_trans values (1);
+xa end 'test1';
+xa prepare 'test1';
+--echo # Disconnect temporary connection
+disconnect con_tmp;
+--echo # Create temporary connection for XA transaction.
+connect (con_tmp,localhost,root,,);
+xa start 'test2';
+insert into t3_trans values (2);
+xa end 'test2';
+xa prepare 'test2';
+--echo # Disconnect temporary connection
+disconnect con_tmp;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Send XA ROLLBACK 'test1'
+--send xa rollback 'test1'
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until XA ROLLBACK is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for commit lock" and
+        info = "xa rollback 'test1'";
+--source include/wait_condition.inc
+unlock tables;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap XA ROLLBACK
+--reap
+--echo # Send XA COMMIT
+--send xa commit 'test2';
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until XA COMMIT is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for commit lock" and
+        info = "xa commit 'test2'";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap XA COMMIT.
+--reap
+delete from t3_trans;
+
+--echo #
 --echo # Check that XA COMMIT blocks FTWRL in another connection.
 xa start 'test1';
 insert into t3_trans values (1);
diff --git a/mysql-test/main/xa.result b/mysql-test/main/xa.result
index a609b68..62e9c9b 100644
--- a/mysql-test/main/xa.result
+++ b/mysql-test/main/xa.result
@@ -355,3 +355,63 @@ DROP TABLE t1, t2, t3;
 xa rollback 'testb',0x2030405060,11;
 XA RECOVER;
 formatID	gtrid_length	bqual_length	data
+# Check XA state when lock_wait_timeout happens
+# More tests added to flush_read_lock.test
+connect  con_tmp,localhost,root,,;
+set session lock_wait_timeout=1;
+create table asd (a int);
+xa start 'test1';
+insert into asd values(1);
+xa end 'test1';
+connection default;
+flush table with read lock;
+connection con_tmp;
+# PREPARE error will do auto rollback.
+xa prepare 'test1';
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+show errors;
+Level	Code	Message
+Error	1205	Lock wait timeout exceeded; try restarting transaction
+Error	1402	XA_RBROLLBACK: Transaction branch was rolled back
+connection default;
+unlock tables;
+connection con_tmp;
+xa start 'test1';
+insert into asd values(1);
+xa end 'test1';
+xa prepare 'test1';
+connection default;
+flush tables with read lock;
+connection con_tmp;
+# LOCK error during ROLLBACK will not alter transaction state.
+xa rollback 'test1';
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+show errors;
+Level	Code	Message
+Error	1205	Lock wait timeout exceeded; try restarting transaction
+Error	1401	XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency
+xa recover;
+formatID	gtrid_length	bqual_length	data
+1	5	0	test1
+# LOCK error during COMMIT will not alter transaction state.
+xa commit 'test1';
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+show errors;
+Level	Code	Message
+Error	1205	Lock wait timeout exceeded; try restarting transaction
+Error	1401	XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency
+xa recover;
+formatID	gtrid_length	bqual_length	data
+1	5	0	test1
+connection default;
+unlock tables;
+connection con_tmp;
+xa rollback 'test1';
+Warnings:
+Warning	1196	Some non-transactional changed tables couldn't be rolled back
+Warning	1196	Some non-transactional changed tables couldn't be rolled back
+xa recover;
+formatID	gtrid_length	bqual_length	data
+drop table asd;
+disconnect con_tmp;
+connection default;
diff --git a/mysql-test/main/xa.test b/mysql-test/main/xa.test
index 7a5cc38..6f56779 100644
--- a/mysql-test/main/xa.test
+++ b/mysql-test/main/xa.test
@@ -468,7 +468,6 @@ REPLACE INTO t2 SELECT * FROM t2;
 INSERT INTO t3 VALUES (1);
 XA BEGIN 'xid3';
 
-
 #Cleanup
 --disconnect con2
 --connection default
@@ -481,3 +480,48 @@ XA RECOVER;
 
 --source include/wait_until_count_sessions.inc
 
+--echo # Check XA state when lock_wait_timeout happens
+--echo # More tests added to flush_read_lock.test
+connect (con_tmp,localhost,root,,);
+set session lock_wait_timeout=1;
+create table asd (a int);
+xa start 'test1';
+insert into asd values(1);
+xa end 'test1';
+connection default;
+flush table with read lock;
+connection con_tmp;
+--echo # PREPARE error will do auto rollback.
+--ERROR ER_LOCK_WAIT_TIMEOUT
+xa prepare 'test1';
+show errors;
+connection default;
+unlock tables;
+
+connection con_tmp;
+xa start 'test1';
+insert into asd values(1);
+xa end 'test1';
+xa prepare 'test1';
+connection default;
+flush tables with read lock;
+connection con_tmp;
+--echo # LOCK error during ROLLBACK will not alter transaction state.
+--ERROR ER_LOCK_WAIT_TIMEOUT
+xa rollback 'test1';
+show errors;
+xa recover;
+--echo # LOCK error during COMMIT will not alter transaction state.
+--ERROR ER_LOCK_WAIT_TIMEOUT
+xa commit 'test1';
+show errors;
+xa recover;
+connection default;
+unlock tables;
+connection con_tmp;
+xa rollback 'test1';
+xa recover;
+drop table asd;
+disconnect con_tmp;
+--source include/wait_until_disconnected.inc
+connection default;
diff --git a/sql/transaction.cc b/sql/transaction.cc
index c7b4b80..bef8caa 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -1047,18 +1047,39 @@ bool trans_xa_prepare(THD *thd)
              xa_state_names[thd->transaction.xid_state.xa_state]);
   else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
     my_error(ER_XAER_NOTA, MYF(0));
-  else if (ha_prepare(thd))
-  {
-    xid_cache_delete(thd, &thd->transaction.xid_state);
-    thd->transaction.xid_state.xa_state= XA_NOTR;
-    my_error(ER_XA_RBROLLBACK, MYF(0));
-  }
   else
   {
-    res= 0;
-    thd->transaction.xid_state.xa_state= XA_PREPARED;
-    if (thd->variables.pseudo_slave_mode)
-      res= applier_reset_xa_trans(thd);
+    /*
+      Acquire metadata lock which will ensure that COMMIT is blocked
+      by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
+      progress blocks FTWRL).
+
+      We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
+    */
+    MDL_request mdl_request;
+    mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+                     MDL_STATEMENT);
+    if (thd->mdl_context.acquire_lock(&mdl_request,
+                                      thd->variables.lock_wait_timeout) ||
+        ha_prepare(thd))
+    {
+      if (!mdl_request.ticket)
+        ha_rollback_trans(thd, TRUE);
+      thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+      thd->transaction.all.reset();
+      thd->server_status&=
+        ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+      xid_cache_delete(thd, &thd->transaction.xid_state);
+      thd->transaction.xid_state.xa_state= XA_NOTR;
+      my_error(ER_XA_RBROLLBACK, MYF(0));
+    }
+    else
+    {
+      res= 0;
+      thd->transaction.xid_state.xa_state= XA_PREPARED;
+      if (thd->variables.pseudo_slave_mode)
+        res= applier_reset_xa_trans(thd);
+    }
   }
 
   DBUG_RETURN(res);
@@ -1101,6 +1122,27 @@ bool trans_xa_commit(THD *thd)
     else
     {
       res= xa_trans_rolled_back(xs);
+      /*
+        Acquire metadata lock which will ensure that COMMIT is blocked
+        by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
+        progress blocks FTWRL).
+
+        We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
+      */
+      MDL_request mdl_request;
+      mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+                       MDL_STATEMENT);
+      if (thd->mdl_context.acquire_lock(&mdl_request,
+                                        thd->variables.lock_wait_timeout))
+      {
+        /*
+          We can't rollback an XA transaction on lock failure due to
+          Innodb redo log and bin log update is involved in rollback.
+          Return error to user for a retry.
+        */
+        my_error(ER_XAER_RMERR, MYF(0));
+        DBUG_RETURN(true);
+      }
       ha_commit_or_rollback_by_xid(thd->lex->xid, !res);
       if((WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) &&
           xs->is_binlogged)
@@ -1139,13 +1181,18 @@ bool trans_xa_commit(THD *thd)
       We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
     */
     mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
-                     MDL_TRANSACTION);
+                     MDL_STATEMENT);
 
     if (thd->mdl_context.acquire_lock(&mdl_request,
                                       thd->variables.lock_wait_timeout))
     {
-      ha_rollback_trans(thd, TRUE);
+      /*
+        We can't rollback an XA transaction on lock failure due to
+        Innodb redo log and bin log update is involved in rollback.
+        Return error to user for a retry.
+      */
       my_error(ER_XAER_RMERR, MYF(0));
+      DBUG_RETURN(true);
     }
     else
     {
@@ -1213,6 +1260,21 @@ bool trans_xa_rollback(THD *thd)
       my_error(ER_XAER_NOTA, MYF(0));
     else
     {
+      MDL_request mdl_request;
+      mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+                       MDL_STATEMENT);
+      if (thd->mdl_context.acquire_lock(&mdl_request,
+                                        thd->variables.lock_wait_timeout))
+      {
+        /*
+          We can't rollback an XA transaction on lock failure due to
+          Innodb redo log and bin log update is involved in rollback.
+          Return error to user for a retry.
+        */
+        my_error(ER_XAER_RMERR, MYF(0));
+        DBUG_RETURN(true);
+      }
+
       xa_trans_rolled_back(xs);
       if (ha_commit_or_rollback_by_xid(thd->lex->xid, 0) == 0 &&
           xs->is_binlogged &&
@@ -1232,6 +1294,21 @@ bool trans_xa_rollback(THD *thd)
     DBUG_RETURN(TRUE);
   }
 
+  MDL_request mdl_request;
+  mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+      MDL_STATEMENT);
+  if (thd->mdl_context.acquire_lock(&mdl_request,
+        thd->variables.lock_wait_timeout))
+  {
+    /*
+       We can't rollback an XA transaction on lock failure due to
+       Innodb redo log and bin log update is involved in rollback.
+       Return error to user for a retry.
+       */
+    my_error(ER_XAER_RMERR, MYF(0));
+    DBUG_RETURN(true);
+  }
+
   if(xa_state == XA_PREPARED && thd->transaction.xid_state.is_binlogged &&
      (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()))
   {


More information about the commits mailing list