| 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: | |
| 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 | ||
[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.

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