Skip to content

Commit c8ae357

Browse files
committed
MDEV-742 XA PREPAREd transaction survive disconnect/server restart
Lifted long standing limitation to the XA of rolling it back at the transaction's connection close even if the XA is prepared. Prepared XA-transaction is made to sustain connection close or server restart. The patch consists of - binary logging extension to write prepared XA part of transaction signified with its XID in a new XA_prepare_log_event. The concusion part - with Commit or Rollback decision - is logged separately as Query_log_event. That is in the binlog the XA consists of two separate group of events. That makes the whole XA possibly interweaving in binlog with other XA:s or regular transaction but with no harm to replication and data consistency. Gtid_log_event receives two more flags to identify which of the two XA phases of the transaction it represents. With either flag set also XID info is added to the event. When binlog is ON on the server XID::formatID is constrained to 4 bytes. - engines are made aware of the server policy to keep up user prepared XA:s so they (Innodb, rocksdb) don't roll them back anymore at their disconnect methods. - slave applier is refined to cope with two phase logged XA:s including parallel modes of execution. This patch does not address crash-safe logging of the new events which is being addressed by MDEV-21469. CORNER CASES: read-only, pure myisam, binlog-*, @@skip_log_bin, etc Are addressed along the following policies. 1. The read-only at reconnect marks XID to fail for future completion with ER_XA_RBROLLBACK. 2. binlog-* filtered XA when it changes engine data is regarded as loggable even when nothing got cached for binlog. An empty XA-prepare group is recorded. Consequent Commit-or-Rollback succeeds in the Engine(s) as well as recorded into binlog. 3. The same applies to the non-transactional engine XA. 4. @@skip_log_bin=OFF does not record anything at XA-prepare (obviously), but the completion event is recorded into binlog to admit inconsistency with slave. The following actions are taken by the patch. At XA-prepare: when empty binlog cache - don't do anything to binlog if RO, otherwise write empty XA_prepare (assert(binlog-filter case)). At Disconnect: when Prepared && RO (=> no binlogging was done) set Xid_cache_element::error := ER_XA_RBROLLBACK *keep* XID in the cache, and rollback the transaction. At XA-"complete": Discover the error, if any don't binlog the "complete", return the error to the user. Kudos ----- Alexey Botchkov took to drive this work initially. Sergei Golubchik, Sergei Petrunja, Marko Mäkelä provided a number of good recommendations. Sergei Voitovich made a magnificent review and improvements to the code. They all deserve a bunch of thanks for making this work done!
1 parent 5754ea2 commit c8ae357

File tree

72 files changed

+8867
-220
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+8867
-220
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
if (!$restart_parameters)
2+
{
3+
let $restart_parameters = restart;
4+
}
5+
6+
--let $_server_id= `SELECT @@server_id`
7+
--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect
8+
9+
--echo # Kill and $restart_parameters
10+
--exec echo "$restart_parameters" > $_expect_file_name
11+
--shutdown_server 0
12+
--source include/wait_until_disconnected.inc
13+
--enable_reconnect
14+
--source include/wait_until_connected_again.inc
15+
--disable_reconnect

mysql-test/main/flush_read_lock.result

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,8 @@ unlock tables;
13101310
# Check that XA non-COMMIT statements are not and COMMIT is
13111311
# blocked by active FTWRL in another connection
13121312
#
1313+
# XA COMMIT, XA ROLLBACK and XA PREPARE does take COMMIT lock to ensure
1314+
# that nothing is written to bin log and redo log under FTWRL mode.
13131315
connection con1;
13141316
flush tables with read lock;
13151317
connection default;
@@ -1322,19 +1324,46 @@ connection con1;
13221324
flush tables with read lock;
13231325
connection default;
13241326
xa end 'test1';
1325-
xa prepare 'test1';
1327+
xa prepare 'test1';;
1328+
connection con1;
1329+
unlock tables;
1330+
# Switching to connection 'default'.
1331+
connection default;
1332+
# Reap XA PREPARE.
1333+
# Switching to connection 'con1'.
1334+
connection con1;
1335+
flush tables with read lock;
1336+
# Switching to connection 'default'.
1337+
connection default;
1338+
# Send XA ROLLBACK 'test1'
13261339
xa rollback 'test1';
1340+
# Switching to connection 'con1'.
13271341
connection con1;
1342+
# Wait until XA ROLLBACK is blocked.
13281343
unlock tables;
13291344
connection default;
1345+
# Reap XA ROLLBACK
13301346
xa start 'test1';
13311347
insert into t3_trans values (1);
13321348
connection con1;
13331349
flush tables with read lock;
13341350
connection default;
13351351
connection default;
13361352
xa end 'test1';
1353+
# Send XA PREPARE 'test1'
13371354
xa prepare 'test1';
1355+
# Switching to connection 'con1'.
1356+
connection con1;
1357+
# Wait until XA PREPARE is blocked.
1358+
unlock tables;
1359+
# Switching to connection 'default'.
1360+
connection default;
1361+
# Reap XA PREPARE.
1362+
# Switching to connection 'con1'.
1363+
connection con1;
1364+
flush tables with read lock;
1365+
# Switching to connection 'default'.
1366+
connection default;
13381367
# Send:
13391368
xa commit 'test1';;
13401369
connection con1;
@@ -1344,6 +1373,51 @@ connection default;
13441373
# Reap XA COMMIT.
13451374
delete from t3_trans;
13461375
#
1376+
# Check that XA COMMIT / ROLLBACK for prepared transaction from a
1377+
# disconnected session is blocked by active FTWRL in another connection.
1378+
#
1379+
# Create temporary connection for XA transaction.
1380+
connect con_tmp,localhost,root,,;
1381+
xa start 'test1';
1382+
insert into t3_trans values (1);
1383+
xa end 'test1';
1384+
xa prepare 'test1';
1385+
# Disconnect temporary connection
1386+
disconnect con_tmp;
1387+
# Create temporary connection for XA transaction.
1388+
connect con_tmp,localhost,root,,;
1389+
xa start 'test2';
1390+
insert into t3_trans values (2);
1391+
xa end 'test2';
1392+
xa prepare 'test2';
1393+
# Disconnect temporary connection
1394+
disconnect con_tmp;
1395+
# Switching to connection 'con1'.
1396+
connection con1;
1397+
flush tables with read lock;
1398+
# Switching to connection 'default'.
1399+
connection default;
1400+
# Send XA ROLLBACK 'test1'
1401+
xa rollback 'test1';
1402+
# Switching to connection 'con1'.
1403+
connection con1;
1404+
# Wait until XA ROLLBACK is blocked.
1405+
unlock tables;
1406+
flush tables with read lock;
1407+
# Switching to connection 'default'.
1408+
connection default;
1409+
# Reap XA ROLLBACK
1410+
# Send XA COMMIT
1411+
xa commit 'test2';;
1412+
# Switching to connection 'con1'.
1413+
connection con1;
1414+
# Wait until XA COMMIT is blocked.
1415+
unlock tables;
1416+
# Switching to connection 'default'.
1417+
connection default;
1418+
# Reap XA COMMIT.
1419+
delete from t3_trans;
1420+
#
13471421
# Check that XA COMMIT blocks FTWRL in another connection.
13481422
xa start 'test1';
13491423
insert into t3_trans values (1);

mysql-test/main/flush_read_lock.test

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,6 +1592,8 @@ unlock tables;
15921592
--echo # Check that XA non-COMMIT statements are not and COMMIT is
15931593
--echo # blocked by active FTWRL in another connection
15941594
--echo #
1595+
--echo # XA COMMIT, XA ROLLBACK and XA PREPARE does take COMMIT lock to ensure
1596+
--echo # that nothing is written to bin log and redo log under FTWRL mode.
15951597
connection $con_aux1;
15961598
flush tables with read lock;
15971599
connection default;
@@ -1604,19 +1606,65 @@ connection $con_aux1;
16041606
flush tables with read lock;
16051607
connection default;
16061608
xa end 'test1';
1607-
xa prepare 'test1';
1608-
xa rollback 'test1';
1609+
--send xa prepare 'test1';
16091610
connection $con_aux1;
1611+
let $wait_condition=
1612+
select count(*) = 1 from information_schema.processlist
1613+
where state = "Waiting for backup lock" and
1614+
info = "xa prepare 'test1'";
1615+
--source include/wait_condition.inc
16101616
unlock tables;
1617+
--echo # Switching to connection 'default'.
1618+
connection default;
1619+
--echo # Reap XA PREPARE.
1620+
--reap
1621+
--echo # Switching to connection '$con_aux1'.
1622+
connection $con_aux1;
1623+
flush tables with read lock;
1624+
--echo # Switching to connection 'default'.
16111625
connection default;
1626+
--echo # Send XA ROLLBACK 'test1'
1627+
--send xa rollback 'test1'
1628+
--echo # Switching to connection '$con_aux1'.
1629+
connection $con_aux1;
1630+
--echo # Wait until XA ROLLBACK is blocked.
1631+
let $wait_condition=
1632+
select count(*) = 1 from information_schema.processlist
1633+
where state = "Waiting for backup lock" and
1634+
info = "xa rollback 'test1'";
1635+
--source include/wait_condition.inc
1636+
unlock tables;
1637+
connection default;
1638+
--echo # Reap XA ROLLBACK
1639+
--reap
16121640
xa start 'test1';
16131641
insert into t3_trans values (1);
16141642
connection $con_aux1;
16151643
flush tables with read lock;
16161644
connection default;
16171645
connection default;
16181646
xa end 'test1';
1619-
xa prepare 'test1';
1647+
--echo # Send XA PREPARE 'test1'
1648+
--send xa prepare 'test1'
1649+
--echo # Switching to connection '$con_aux1'.
1650+
connection $con_aux1;
1651+
--echo # Wait until XA PREPARE is blocked.
1652+
let $wait_condition=
1653+
select count(*) = 1 from information_schema.processlist
1654+
where state = "Waiting for backup lock" and
1655+
info = "xa prepare 'test1'";
1656+
--source include/wait_condition.inc
1657+
unlock tables;
1658+
--echo # Switching to connection 'default'.
1659+
connection default;
1660+
--echo # Reap XA PREPARE.
1661+
--reap
1662+
--echo # Switching to connection '$con_aux1'.
1663+
connection $con_aux1;
1664+
flush tables with read lock;
1665+
--echo # Switching to connection 'default'.
1666+
connection default;
1667+
16201668
--echo # Send:
16211669
--send xa commit 'test1';
16221670
connection $con_aux1;
@@ -1631,6 +1679,64 @@ connection default;
16311679
--echo # Reap XA COMMIT.
16321680
--reap
16331681
delete from t3_trans;
1682+
--echo #
1683+
--echo # Check that XA COMMIT / ROLLBACK for prepared transaction from a
1684+
--echo # disconnected session is blocked by active FTWRL in another connection.
1685+
--echo #
1686+
--echo # Create temporary connection for XA transaction.
1687+
connect (con_tmp,localhost,root,,);
1688+
xa start 'test1';
1689+
insert into t3_trans values (1);
1690+
xa end 'test1';
1691+
xa prepare 'test1';
1692+
--echo # Disconnect temporary connection
1693+
disconnect con_tmp;
1694+
--echo # Create temporary connection for XA transaction.
1695+
connect (con_tmp,localhost,root,,);
1696+
xa start 'test2';
1697+
insert into t3_trans values (2);
1698+
xa end 'test2';
1699+
xa prepare 'test2';
1700+
--echo # Disconnect temporary connection
1701+
disconnect con_tmp;
1702+
--echo # Switching to connection '$con_aux1'.
1703+
connection $con_aux1;
1704+
flush tables with read lock;
1705+
--echo # Switching to connection 'default'.
1706+
connection default;
1707+
--echo # Send XA ROLLBACK 'test1'
1708+
--send xa rollback 'test1'
1709+
--echo # Switching to connection '$con_aux1'.
1710+
connection $con_aux1;
1711+
--echo # Wait until XA ROLLBACK is blocked.
1712+
let $wait_condition=
1713+
select count(*) = 1 from information_schema.processlist
1714+
where state = "Waiting for backup lock" and
1715+
info = "xa rollback 'test1'";
1716+
--source include/wait_condition.inc
1717+
unlock tables;
1718+
flush tables with read lock;
1719+
--echo # Switching to connection 'default'.
1720+
connection default;
1721+
--echo # Reap XA ROLLBACK
1722+
--reap
1723+
--echo # Send XA COMMIT
1724+
--send xa commit 'test2';
1725+
--echo # Switching to connection '$con_aux1'.
1726+
connection $con_aux1;
1727+
--echo # Wait until XA COMMIT is blocked.
1728+
let $wait_condition=
1729+
select count(*) = 1 from information_schema.processlist
1730+
where state = "Waiting for backup lock" and
1731+
info = "xa commit 'test2'";
1732+
--source include/wait_condition.inc
1733+
unlock tables;
1734+
--echo # Switching to connection 'default'.
1735+
connection default;
1736+
--echo # Reap XA COMMIT.
1737+
--reap
1738+
delete from t3_trans;
1739+
16341740
--echo #
16351741
--echo # Check that XA COMMIT blocks FTWRL in another connection.
16361742
xa start 'test1';

mysql-test/main/xa.result

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ select * from t1;
5959
a
6060
20
6161
disconnect con1;
62+
xa rollback 'testb',0x2030405060,11;
63+
xa recover;
64+
formatID gtrid_length bqual_length data
6265
connection default;
6366
xa start 'tr1';
6467
insert t1 values (40);
@@ -400,3 +403,61 @@ XA ROLLBACK 'xid1';
400403
#
401404
XA START 'gtrid', 'bqual', 0x80000000;
402405
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '0x80000000' at line 1
406+
# MDEV-7974 related
407+
# Check XA state when lock_wait_timeout happens
408+
# More tests added to flush_read_lock.test
409+
connect con_tmp,localhost,root,,;
410+
set session lock_wait_timeout=1;
411+
create table asd (a int) engine=innodb;
412+
xa start 'test1';
413+
insert into asd values(1);
414+
xa end 'test1';
415+
connection default;
416+
flush table with read lock;
417+
connection con_tmp;
418+
# PREPARE error will do auto rollback.
419+
xa prepare 'test1';
420+
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
421+
show errors;
422+
Level Code Message
423+
Error 1205 Lock wait timeout exceeded; try restarting transaction
424+
Error 1402 XA_RBROLLBACK: Transaction branch was rolled back
425+
connection default;
426+
unlock tables;
427+
connection con_tmp;
428+
xa start 'test1';
429+
insert into asd values(1);
430+
xa end 'test1';
431+
xa prepare 'test1';
432+
connection default;
433+
flush tables with read lock;
434+
connection con_tmp;
435+
# LOCK error during ROLLBACK will not alter transaction state.
436+
xa rollback 'test1';
437+
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
438+
show errors;
439+
Level Code Message
440+
Error 1205 Lock wait timeout exceeded; try restarting transaction
441+
Error 1401 XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency
442+
xa recover;
443+
formatID gtrid_length bqual_length data
444+
1 5 0 test1
445+
# LOCK error during COMMIT will not alter transaction state.
446+
xa commit 'test1';
447+
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
448+
show errors;
449+
Level Code Message
450+
Error 1205 Lock wait timeout exceeded; try restarting transaction
451+
Error 1401 XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency
452+
xa recover;
453+
formatID gtrid_length bqual_length data
454+
1 5 0 test1
455+
connection default;
456+
unlock tables;
457+
connection con_tmp;
458+
xa rollback 'test1';
459+
xa recover;
460+
formatID gtrid_length bqual_length data
461+
drop table asd;
462+
disconnect con_tmp;
463+
connection default;

mysql-test/main/xa.test

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ xa start 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz';
8282
select * from t1;
8383
disconnect con1;
8484
--source include/wait_until_count_sessions.inc
85+
xa rollback 'testb',0x2030405060,11;
86+
xa recover;
8587

8688
connection default;
8789

@@ -541,3 +543,50 @@ XA ROLLBACK 'xid1';
541543
XA START 'gtrid', 'bqual', 0x80000000;
542544

543545
--source include/wait_until_count_sessions.inc
546+
547+
--echo # MDEV-7974 related
548+
--echo # Check XA state when lock_wait_timeout happens
549+
--echo # More tests added to flush_read_lock.test
550+
connect (con_tmp,localhost,root,,);
551+
set session lock_wait_timeout=1;
552+
create table asd (a int) engine=innodb;
553+
xa start 'test1';
554+
insert into asd values(1);
555+
xa end 'test1';
556+
connection default;
557+
flush table with read lock;
558+
connection con_tmp;
559+
--echo # PREPARE error will do auto rollback.
560+
--ERROR ER_LOCK_WAIT_TIMEOUT
561+
xa prepare 'test1';
562+
show errors;
563+
connection default;
564+
unlock tables;
565+
566+
connection con_tmp;
567+
xa start 'test1';
568+
insert into asd values(1);
569+
xa end 'test1';
570+
xa prepare 'test1';
571+
connection default;
572+
flush tables with read lock;
573+
connection con_tmp;
574+
--echo # LOCK error during ROLLBACK will not alter transaction state.
575+
--ERROR ER_LOCK_WAIT_TIMEOUT
576+
xa rollback 'test1';
577+
show errors;
578+
xa recover;
579+
--echo # LOCK error during COMMIT will not alter transaction state.
580+
--ERROR ER_LOCK_WAIT_TIMEOUT
581+
xa commit 'test1';
582+
show errors;
583+
xa recover;
584+
connection default;
585+
unlock tables;
586+
connection con_tmp;
587+
xa rollback 'test1';
588+
xa recover;
589+
drop table asd;
590+
disconnect con_tmp;
591+
--source include/wait_until_disconnected.inc
592+
connection default;

0 commit comments

Comments
 (0)