For most people (including me until recently) database XA transactions are a fuzzy concept. In over eight years with Percona, I have never had to deal with XA transactions. Then a few weeks ago I got two customers having issues with XA transactions. That deserves a post.
What are XA transactions? XA transactions are useful when you need to coordinate a transaction between different systems. The simplest example could be simply two storage engines within MySQL. Basically, it follows this sequence:
Once prepared, the XA transaction survives a MySQL crash. Upon restart, you’ll see something like this in the MySQL error log:
|
1 |
2017-08-23T14:53:54.189068Z 0 [Note] Starting crash recovery...<br>2017-08-23T14:53:54.189204Z 0 [Note] InnoDB: Starting recovery for XA transactions...<br>2017-08-23T14:53:54.189225Z 0 [Note] InnoDB: Transaction 45093 in prepared state after recovery<br>2017-08-23T14:53:54.189244Z 0 [Note] InnoDB: Transaction contains changes to 2 rows<br>2017-08-23T14:53:54.189257Z 0 [Note] InnoDB: 1 transactions in prepared state after recovery<br>2017-08-23T14:53:54.189267Z 0 [Note] Found 1 prepared transaction(s) in InnoDB<br>2017-08-23T14:53:54.189312Z 0 [Warning] Found 1 prepared XA transactions<br>2017-08-23T14:53:54.189329Z 0 [Note] Crash recovery finished.<br>2017-08-23T14:53:54.189472Z 0 [Note] InnoDB: Starting recovery for XA transactions...<br>2017-08-23T14:53:54.189489Z 0 [Note] InnoDB: Transaction 45093 in prepared state after recovery<br>2017-08-23T14:53:54.189501Z 0 [Note] InnoDB: Transaction contains changes to 2 rows<br>2017-08-23T14:53:54.189520Z 0 [Note] InnoDB: 1 transactions in prepared state after recovery<br>2017-08-23T14:53:54.189529Z 0 [Note] Found 1 prepared transaction(s) in InnoDB<br>2017-08-23T14:53:54.189539Z 0 [Warning] Found 1 prepared XA transactions<br> |
The command xa recover shows you an output like:
|
1 |
mysql> xa recover;<br>+----------+--------------+--------------+-----------+<br>| formatID | gtrid_length | bqual_length | data |<br>+----------+--------------+--------------+-----------+<br>| 1234 | 4 | 5 | bqual |<br>+----------+--------------+--------------+-----------+<br>1 row in set (0.00 sec)<br> |
There are some binary data that can’t be shown in HTML. The XA Xid is made of three fields: gtrid (global trx id), bqual (branch qualifier) and formatId. Java applications use all three fields. For my example above, I used “X’01020304′,’bqual’,1234”. You can trust Java application servers to be creative with Xid values. With MySQL 5.7, you can output the data part in hex with convert xid :
|
1 |
mysql> xa recover convert xid;<br>+----------+--------------+--------------+----------------------+<br>| formatID | gtrid_length | bqual_length | data |<br>+----------+--------------+--------------+----------------------+<br>| 1234 | 4 | 5 | 0x01020304627175616C |<br>+----------+--------------+--------------+----------------------+<br>1 row in set (0.01 sec)<br> |
If you do nothing, the prepared transaction stays there forever and holds locks and a read view open. As a consequence, the history list grows without bound along with your ibdata1 file, where the undo entries are kept. If you have slaves, they all have the prepared transaction too (at least with 5.7). No fun.
As a consequence, if you are using XA transactions, you MUST check if there are prepared transactions pending after the server or mysqld restarted. If you find such transactions, you need to commit or roll them back, depending on what is involved.
But how do you commit these XA transactions? The problem here is the output of xa recover. As it is, the output is unusable if there is a bqual field or non-default formatID field:
|
1 |
mysql> xa commit 0x01020304627175616C;<br>ERROR 1397 (XAE04): XAER_NOTA: Unknown XID<br> |
Looking back at the xa recover convert xid output above, the gtrid_length and bqual_length are provided. With the use of these values, you can extract the parts of the data field which gives us:
And, of course, the formatID is 1234. Altogether, we have:
|
1 |
mysql> xa commit 0x01020304,0x627175616C,1234;<br>Query OK, 0 rows affected (0.15 sec)<br> |
Which finally works! On 5.6 the convert xid option is not available. You have to be a bit more creative:
|
1 |
root@master57:/var/lib/mysql# mysql -r -e 'xa recoverG' | hexdump -C<br>00000000 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a |****************|<br>00000010 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 20 31 2e 20 72 |*********** 1. r|<br>00000020 6f 77 20 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a |ow *************|<br>00000030 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 2a 0a 20 |**************. |<br>00000040 20 20 20 66 6f 72 6d 61 74 49 44 3a 20 31 32 33 | formatID: 123|<br>00000050 34 0a 67 74 72 69 64 5f 6c 65 6e 67 74 68 3a 20 |4.gtrid_length: |<br>00000060 34 0a 62 71 75 61 6c 5f 6c 65 6e 67 74 68 3a 20 |4.bqual_length: |<br>00000070 35 0a 20 20 20 20 20 20 20 20 64 61 74 61 3a 20 |5. data: |<br>00000080 01 02 03 04 62 71 75 61 6c 0a |....bqual.|<br>0000008a<br> |
But there is a limitation in 5.6: you can only XA commit/rollback transactions that belong to your session. That means after a crash you are out of luck. To get rid of these you need to promote a slave or perform a logical dump and restore. The best plan is to avoid the use of XA transactions with 5.6.
I submitted this bug to Percona Server for MySQL in order to get a usable output out of xa recover convert xid. If you think this is important, vote for it!