Bug #71352 xa start with the exist xid, and then xa commit with the xid will failed

Submitted: 12 Jan 2014 4:03 Modified: 20 May 2014 0:34
Reporter: HongXiang Jiang (OCA) Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: XA transactions Severity:S3 (Non-critical)
Version:5.5.35, 5.6.14 OS:Linux
Assigned to: CPU Architecture:Any
Tags: server crash, xa start

[12 Jan 2014 4:03] HongXiang Jiang
Description:
when a xa transaction is prepared and the server crashed, then restart the MySQL server, first, do xa start with the exist xid, says: XAER_DUPID: The XID already exists, that's OK, but do xa commit with this xid, says that:XAER_RMFAIL: The command cannot be executed when global transaction is in the  NON-EXISTING state.
the prepared transaction can't be committed.
the reason is:
in the code transaction.cc::trans_xa_start
    thd->transaction.xid_state.xid.set(thd->lex->xid);
    if (xid_cache_insert(&thd->transaction.xid_state))
    {
      thd->transaction.xid_state.xa_state= XA_NOTR;
      thd->transaction.xid_state.xid.null();
      trans_rollback(thd);
      DBUG_RETURN(true);
    }
first, set the xid, then, if insert failed, set the xid null, in the function null(), only set the formatID = -1, not clear the data.

but in the transaction.cc::trans_xa_commit
if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
the code above use xid.eq() to compare the two xids, in the function xid.eq(), only compared with gtrid_length, bqual_length and data, do not contain the formatID, so after do trans_xa_start, the gtrid_length, bqual_length and data have been setted with the formatID = -1, and then do trans_xa_commit, the xid.eq() return true, it's wrong!

How to repeat:
mysql> create table t(id int)engine=innodb;
Query OK, 0 rows affected (0.01 sec)
mysql> xa start '111';
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t values(1);
Query OK, 1 row affected (0.00 sec)
mysql> xa end '111';
Query OK, 0 rows affected (0.00 sec)
mysql> xa prepare '111';
Query OK, 0 rows affected (0.00 sec)

kill -9 mysqld
restart mysqld

mysql> xa start '111';
ERROR 1440 (XAE08): XAER_DUPID: The XID already exists
mysql> xa commit '111';
ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the  NON-EXISTING state

Suggested fix:
xid.eq() not only compare with gtrid_length, bqual_length and data, but also with formatID. 
In the trans_xa_start, if insert failed, clear the xid's data
[13 Jan 2014 9:44] MySQL Verification Team
Hello Boyce,

Thank you for the bug report and test case. 
Verified as described.

Thanks,
Umesh
[13 Jan 2014 9:50] MySQL Verification Team
// 5.5.35

mysql> select version();
+------------------+
| version()        |
+------------------+
| 5.5.35-debug-log |
+------------------+
1 row in set (0.00 sec)

mysql> 
mysql> create table t(id int)engine=innodb;
Query OK, 0 rows affected (0.04 sec)

mysql> xa start '111';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values(1);
Query OK, 1 row affected (0.00 sec)

mysql> xa end '111';
Query OK, 0 rows affected (0.00 sec)

mysql> xa prepare '111';
Query OK, 0 rows affected (0.00 sec)

mysql> xa recover;
+----------+--------------+--------------+------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+------+
|        1 |            3 |            0 | 111  |
+----------+--------------+--------------+------+
1 row in set (0.00 sec)

// Killed mysqld from other session

mysql> 
mysql> xa start '111';
ERROR 1440 (XAE08): XAER_DUPID: The XID already exists
mysql> xa recover;
+----------+--------------+--------------+------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+------+
|        1 |            3 |            0 | 111  |
+----------+--------------+--------------+------+
1 row in set (0.00 sec)

mysql> xa commit '111';
ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the  NON-EXISTING state

mysql> xa rollback '111';
ERROR 1399 (XAE07): XAER_RMFAIL: The command cannot be executed when global transaction is in the  NON-EXISTING state
[20 May 2014 0:34] Paul DuBois
Noted in 5.7.5 changelog.

XA START after a server restart with the exising XID followed by XA
COMMIT failed to commit.

Portions of this website are copyright © 2001, 2002 The PHP Group

Page generated in 0.042 sec. using MySQL 8.4.7-u6-cloud

Timestamp references displayed by the system are UTC.
Content reproduced on this site is the property of the respective copyright holders.
It is not reviewed in advance by Oracle and does not necessarily represent the opinion of Oracle or any other party.