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.