diff --git a/mysql-test/include/have_spider.inc b/mysql-test/include/have_spider.inc new file mode 100644 index 0000000000000..68056cf9aa939 --- /dev/null +++ b/mysql-test/include/have_spider.inc @@ -0,0 +1,4 @@ +if (`SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.ENGINES WHERE engine = 'SPIDER' AND support IN ('YES', 'DEFAULT', 'ENABLED')`) +{ + --skip Test requires Spider engine +} diff --git a/mysql-test/include/have_spider.opt b/mysql-test/include/have_spider.opt new file mode 100644 index 0000000000000..18d5f6f333b2f --- /dev/null +++ b/mysql-test/include/have_spider.opt @@ -0,0 +1 @@ +--plugin-load-add=$HA_SPIDER_SO diff --git a/mysql-test/include/show_binlog_events2.inc b/mysql-test/include/show_binlog_events2.inc index 69391ff8fa2f0..593965b9133ca 100644 --- a/mysql-test/include/show_binlog_events2.inc +++ b/mysql-test/include/show_binlog_events2.inc @@ -19,6 +19,9 @@ # $regexp_replace # A user's custom addon to standard preexisting list. # +# $filter_cid +# boolean whether to filer out commit id (0) or not (1) +# if ($binlog_start) { @@ -26,6 +29,7 @@ if ($binlog_start) } if (!$binlog_start) { + # consider instead ./binlog_start_pos.inc --let $_binlog_start=256 } if ($binlog_file) @@ -36,4 +40,8 @@ if ($binlog_file) --replace_result "$_from_binlog_start" "from " $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --replace_column 2 # 5 # --replace_regex /\/\* xid=.* \*\//\/* XID *\// /table_id: [0-9]+/table_id: #/ /file_id=[0-9]+/file_id=#/ /GTID [0-9]+-[0-9]+-[0-9]+/GTID #-#-#/ $replace_regexp +if ($filter_cid) +{ +--replace_regex /\/\* xid=.* \*\//\/* XID *\// /table_id: [0-9]+/table_id: #/ /file_id=[0-9]+/file_id=#/ /GTID [0-9]+-[0-9]+-[0-9]+/GTID #-#-#/ / cid=[0-9]+// $replace_regexp +} --eval show binlog events $_in_binlog_file from $_binlog_start diff --git a/mysql-test/include/wait_for_binlog_event.inc b/mysql-test/include/wait_for_binlog_event.inc index b251ae226d3d7..33a8ad8d37149 100644 --- a/mysql-test/include/wait_for_binlog_event.inc +++ b/mysql-test/include/wait_for_binlog_event.inc @@ -6,13 +6,20 @@ # # USAGE # +# let $wait_binlog_file= 'master-bin.000001', defaults to the current log # let $wait_binlog_event= DROP; +# OR +# let $wait_binlog_event= ;pos=4 # as Rotate event signature in the SBE Info + # --source include/wait_for_binlog_event.inc let $_loop_count= 300; let $_last_event= ; let $_event_pos= 1; - +if (!$wait_binlog_file) +{ + let $wait_binlog_file=query_get_value(SHOW MASTER STATUS, File, 1); +} while (`SELECT INSTR("$_last_event","$wait_binlog_event") = 0`) { dec $_loop_count; @@ -22,12 +29,12 @@ while (`SELECT INSTR("$_last_event","$wait_binlog_event") = 0`) --die ERROR: failed while waiting for $wait_binlog_event in binlog } real_sleep 0.1; - let $_event= query_get_value(SHOW BINLOG EVENTS, Info, $_event_pos); + let $_event= query_get_value(SHOW BINLOG EVENTS IN '$wait_binlog_file', Info, $_event_pos); let $_last_event= $_event; while ($_event != "No such row") { inc $_event_pos; let $_last_event= $_event; - let $_event= query_get_value(SHOW BINLOG EVENTS, Info, $_event_pos); + let $_event= query_get_value(SHOW BINLOG EVENTS IN '$wait_binlog_file', Info, $_event_pos); } } diff --git a/mysql-test/main/alter_table_online_debug.result b/mysql-test/main/alter_table_online_debug.result index 1148e178428df..c64ba93c7ff4a 100644 --- a/mysql-test/main/alter_table_online_debug.result +++ b/mysql-test/main/alter_table_online_debug.result @@ -1690,6 +1690,8 @@ update t set a = 0; ERROR 23000: Duplicate entry '0' for key 'PRIMARY' xa end 'xid'; xa prepare 'xid'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back xa commit 'xid'; set debug_sync= 'now signal go'; connection default; @@ -1707,6 +1709,8 @@ update t set a = 0; ERROR 23000: Duplicate entry '0' for key 'PRIMARY' xa end 'xid'; xa prepare 'xid'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back xa rollback 'xid'; set debug_sync= 'now signal go'; connection default; diff --git a/mysql-test/main/binary_to_hex.result b/mysql-test/main/binary_to_hex.result index 3918021f3e65a..697fb4c0b11d1 100644 --- a/mysql-test/main/binary_to_hex.result +++ b/mysql-test/main/binary_to_hex.result @@ -115,7 +115,7 @@ c9: 0x000000000101000000000000000000F03F000000000000F03F #Print the table contents in html format
c1c2c3c4c5c6c7c8c9
0x74696E79626C6F622D74657874207265616461626C650x626C6F622D74657874207265616461626C650x6D656469756D626C6F622D74657874207265616461626C650x6C6F6E67626C6F622D74657874207265616461626C650x74657874207265616461626C650x010x630x7661726961626C650x000000000101000000000000000000F03F000000000000F03F
idcol1col2
10xAB1234000000000000000x123ABC
20xDE1234000000000000000x123DEF
DROP TABLE t1, t2; -create table t1 (a int); +create table t1 (a int) engine=innodb; formatID gtrid_length bqual_length data 1 3 2 0x7472316271 DROP TABLE t1; diff --git a/mysql-test/main/binary_to_hex.test b/mysql-test/main/binary_to_hex.test index be4fb301e4033..98ec88cea3502 100644 --- a/mysql-test/main/binary_to_hex.test +++ b/mysql-test/main/binary_to_hex.test @@ -9,6 +9,7 @@ # # Save the initial number of concurrent sessions +--source include/have_innodb.inc --source include/count_sessions.inc --source include/not_embedded.inc @@ -74,7 +75,7 @@ DROP TABLE t1, t2; # MDEV-14593 human-readable XA RECOVER -create table t1 (a int); +create table t1 (a int) engine=innodb; --write_file $MYSQLTEST_VARDIR/tmp/mdev-14593.sql DELIMITER / diff --git a/mysql-test/main/commit.result b/mysql-test/main/commit.result index 40524829667fa..8da40e259481d 100644 --- a/mysql-test/main/commit.result +++ b/mysql-test/main/commit.result @@ -524,6 +524,8 @@ DELETE FROM t1; ERROR 25006: Cannot execute statement in a READ ONLY transaction XA END 'test1'; XA PREPARE 'test1'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA COMMIT 'test1'; DROP TABLE t1; # diff --git a/mysql-test/main/flush_read_lock.result b/mysql-test/main/flush_read_lock.result index a8f8431e1253b..9b506754a0c34 100644 --- a/mysql-test/main/flush_read_lock.result +++ b/mysql-test/main/flush_read_lock.result @@ -1308,10 +1308,14 @@ flush tables with read lock; xa start 'test1'; xa end 'test1'; xa prepare 'test1'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back xa rollback 'test1'; xa start 'test1'; xa end 'test1'; xa prepare 'test1'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back xa commit 'test1'; xa recover; unlock tables; diff --git a/mysql-test/main/xa.result b/mysql-test/main/xa.result index a692e5ec311b9..0c58b8e87f6bc 100644 --- a/mysql-test/main/xa.result +++ b/mysql-test/main/xa.result @@ -175,10 +175,14 @@ xa rollback 'a'; xa start 'a'; xa end 'a'; xa prepare 'a'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back xa commit 'a'; xa start 'a'; xa end 'a'; xa prepare 'a'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back xa commit 'a'; CREATE TABLE t1(a INT, KEY(a)) ENGINE=InnoDB; INSERT INTO t1 VALUES(1),(2); @@ -222,8 +226,10 @@ XA END 'x'; XA END 'x'; ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA PREPARE 'x'; -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state XA ROLLBACK 'x'; # # Bug#59986 Assert in Diagnostics_area::set_ok_status() for XA COMMIT @@ -324,6 +330,8 @@ connection con2; XA START 'xid1'; XA END 'xid1'; XA PREPARE 'xid1'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA ROLLBACK 'xid1'; connection default; DROP TABLE t1, t2; @@ -396,6 +404,8 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp XA BEGIN 'Я_упaлa_c_сеновала_тормозила_головой'; XA END 'Я_упaлa_c_сеновала_тормозила_головой'; XA PREPARE 'Я_упaлa_c_сеновала_тормозила_головой'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA ROLLBACK 'Я_упaлa_c_сеновала_тормозила_головой'; SET NAMES default; DROP TABLE t1; @@ -422,9 +432,11 @@ XA ROLLBACK 'unknown'; ERROR XAE09: XAER_OUTSIDE: Some work is done outside global transaction XA END 'xid1'; XA PREPARE 'xid1'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA COMMIT 'xid1' ONE PHASE; -ERROR XAE05: XAER_INVAL: Invalid arguments (or unsupported command) XA ROLLBACK 'xid1'; +ERROR XAE04: XAER_NOTA: Unknown XID # # MDEV-21856 - xid_t::formatID has to be constrained to 4 byte size # @@ -463,8 +475,10 @@ SELECT * FROM t; a XA END '0'; XA PREPARE '0'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back HANDLER t READ NEXT; -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state # Cleanup XA COMMIT '0'; DROP TABLE t; @@ -555,6 +569,8 @@ select 0; 0 xa end '1'; xa prepare '1'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back xa commit '1'; xa start '2'; select 0; @@ -562,6 +578,8 @@ select 0; 0 xa end '2'; xa prepare '2'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back xa rollback '2'; # Read-only disconnect case connect con1_ro,localhost,root,,; @@ -571,13 +589,14 @@ select 0; 0 xa end '3'; xa prepare '3'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1_ro; connection default; xa recover; formatID gtrid_length bqual_length data -1 1 0 3 xa commit '3'; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID connect con2_ro,localhost,root,,; xa start '4'; select 0; @@ -585,13 +604,14 @@ select 0; 0 xa end '4'; xa prepare '4'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con2_ro; connection default; xa recover; formatID gtrid_length bqual_length data -1 1 0 4 xa rollback '4'; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID set @@global.read_only=@sav_read_only; # # End of 10.5 tests diff --git a/mysql-test/main/xa.test b/mysql-test/main/xa.test index a4c60ef316d2d..9f8c3bd7a231f 100644 --- a/mysql-test/main/xa.test +++ b/mysql-test/main/xa.test @@ -573,8 +573,10 @@ XA COMMIT 'unknown' ONE PHASE; XA ROLLBACK 'unknown'; XA END 'xid1'; XA PREPARE 'xid1'; ---error ER_XAER_INVAL +# marked to rollback at prepare does not warn (maybe it should) +--error 0 XA COMMIT 'xid1' ONE PHASE; +--error ER_XAER_NOTA XA ROLLBACK 'xid1'; @@ -731,7 +733,7 @@ disconnect con1_ro; connection default; --source include/wait_until_count_sessions.inc xa recover; ---error ER_XA_RBROLLBACK +--error ER_XAER_NOTA xa commit '3'; --source include/count_sessions.inc @@ -745,7 +747,7 @@ disconnect con2_ro; connection default; --source include/wait_until_count_sessions.inc xa recover; ---error ER_XA_RBROLLBACK +--error ER_XAER_NOTA xa rollback '4'; set @@global.read_only=@sav_read_only; diff --git a/mysql-test/main/xa_binlog.result b/mysql-test/main/xa_binlog.result index a272570aac16a..c2334cd8c4599 100644 --- a/mysql-test/main/xa_binlog.result +++ b/mysql-test/main/xa_binlog.result @@ -27,7 +27,10 @@ XA END 'x'; XA PREPARE 'x'; Warnings: Warning 1030 Got error 131 "Command not supported by the engine" from storage engine Aria +Note 4226 Found to be read-only XA transaction is rolled back XA COMMIT 'x'; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back SELECT * from t1; a XA BEGIN 'x'; @@ -36,6 +39,7 @@ XA END 'x'; XA PREPARE 'x'; Warnings: Warning 1030 Got error 131 "Command not supported by the engine" from storage engine Aria +Note 4226 Found to be read-only XA transaction is rolled back XA ROLLBACK 'x'; Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back @@ -68,18 +72,8 @@ master-bin.000001 # Query 1 # COMMIT master-bin.000001 # Gtid 1 # BEGIN GTID #-#-# master-bin.000001 # Query 1 # use `test`; DELETE FROM t1 master-bin.000001 # Query 1 # COMMIT -master-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# -master-bin.000001 # Query 1 # XA END X'78',X'',1 -master-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 -master-bin.000001 # Gtid 1 # GTID #-#-# -master-bin.000001 # Query 1 # XA COMMIT X'78',X'',1 master-bin.000001 # Gtid 1 # BEGIN GTID #-#-# master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 VALUES (3),(4) master-bin.000001 # Query 1 # COMMIT -master-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# -master-bin.000001 # Query 1 # XA END X'78',X'',1 -master-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 -master-bin.000001 # Gtid 1 # GTID #-#-# -master-bin.000001 # Query 1 # XA ROLLBACK X'78',X'',1 master-bin.000001 # Gtid 1 # GTID #-#-# master-bin.000001 # Query 1 # use `test`; DROP TABLE `t1` /* generated by server */ diff --git a/mysql-test/main/xa_prepared_binlog_off.result b/mysql-test/main/xa_prepared_binlog_off.result index ebcb24d3af302..59e033e9c4d9b 100644 --- a/mysql-test/main/xa_prepared_binlog_off.result +++ b/mysql-test/main/xa_prepared_binlog_off.result @@ -4,6 +4,9 @@ CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where typ call mtr.add_suppression("Found 10 prepared XA transactions"); CREATE TABLE t (a INT) ENGINE=innodb; INSERT INTO t VALUES(100); +# +# A. The temp table only prepared XA recovers only formally to +# let post recovery XA COMMIT or XA ROLLBACK with no effect. connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@sql_log_bin = OFF; CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb; @@ -11,6 +14,8 @@ XA START 'trx1tmp'; INSERT INTO tmp1 SET a=1; XA END 'trx1tmp'; XA PREPARE 'trx1tmp'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@sql_log_bin = OFF; CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb; @@ -18,6 +23,8 @@ XA START 'trx2tmp'; INSERT INTO tmp1 SET a=1; XA END 'trx2tmp'; XA PREPARE 'trx2tmp'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@sql_log_bin = OFF; CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb; @@ -25,6 +32,11 @@ XA START 'trx3tmp'; INSERT INTO tmp1 SET a=1; XA END 'trx3tmp'; XA PREPARE 'trx3tmp'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back +# +# Various prohibited XA state changes to test here: +# connection default; XA COMMIT 'trx1tmp'; ERROR XAE04: XAER_NOTA: Unknown XID @@ -33,18 +45,18 @@ ERROR XAE04: XAER_NOTA: Unknown XID XA START 'trx1tmp'; ERROR XAE08: XAER_DUPID: The XID already exists connection default; -*** 3 prepared transactions must be in the list *** +*** 0 transactions must be in the list *** XA RECOVER; formatID gtrid_length bqual_length data -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N connection conn1tmp; disconnect conn1tmp; connection default; XA COMMIT 'trx1tmp'; KILL connection CONN_ID; XA COMMIT 'trx3tmp'; +# +# B. "Read-only" (select) prepared XA recovers only formally to +# let post recovery XA COMMIT or XA ROLLBACK with no effect. connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx1ro'; SELECT * from t ORDER BY a; @@ -52,6 +64,8 @@ a 100 XA END 'trx1ro'; XA PREPARE 'trx1ro'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx2ro'; SELECT * from t ORDER BY a; @@ -59,6 +73,8 @@ a 100 XA END 'trx2ro'; XA PREPARE 'trx2ro'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx3ro'; SELECT * from t ORDER BY a; @@ -66,47 +82,55 @@ a 100 XA END 'trx3ro'; XA PREPARE 'trx3ro'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connection default; -*** 4 prepared transactions must be in the list *** +*** 0 transactions must be in the list *** XA RECOVER; formatID gtrid_length bqual_length data -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N connection conn1ro; disconnect conn1ro; connection default; XA ROLLBACK 'trx1ro'; KILL connection CONN_ID; XA ROLLBACK 'trx3ro'; +# +# C. Empty prepared XA recovers only formally to +# let post recovery XA COMMIT or XA ROLLBACK with no effect. +# connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx1empty'; XA END 'trx1empty'; XA PREPARE 'trx1empty'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx2empty'; XA END 'trx2empty'; XA PREPARE 'trx2empty'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx3empty'; XA END 'trx3empty'; XA PREPARE 'trx3empty'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connection default; -*** 5 prepared transactions must be in the list *** +*** 0 transactions must be in the list *** XA RECOVER; formatID gtrid_length bqual_length data -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N connection conn1empty; disconnect conn1empty; connection default; XA COMMIT 'trx1empty'; KILL connection CONN_ID; XA COMMIT 'trx3empty'; +# +# D. Not prepared XA disconnects to be cleared out, +# no effect on data left as well. +# Few more prohibited XA state transactions is checked out. +# connect conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx1unprepared'; INSERT INTO t set a=0; @@ -121,6 +145,12 @@ disconnect conn1unprepared; connection default; XA COMMIT 'trx1unprepared'; ERROR XAE04: XAER_NOTA: Unknown XID +# +# II. Regular case. +# +# Prepared transactions get disconnected in three ways: +# actively, being killed and by the server shutdown. +# connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@binlog_format = STATEMENT; XA START 'trx_0'; @@ -281,18 +311,27 @@ XA END 'trx_19'; XA PREPARE 'trx_19'; connection default; KILL CONNECTION CONN_ID; +# [0, 5 - 1] are rolled back now connection default; XA ROLLBACK 'trx_0'; XA ROLLBACK 'trx_1'; XA ROLLBACK 'trx_2'; XA ROLLBACK 'trx_3'; XA ROLLBACK 'trx_4'; +# [5, 5 + 5 - 1] get committed XA COMMIT 'trx_5'; XA COMMIT 'trx_6'; XA COMMIT 'trx_7'; XA COMMIT 'trx_8'; XA COMMIT 'trx_9'; # restart +# +# III. Post server-restart verification. +# It concludes survived XA:s with a number of commits and rollbacks +# as configured in the 1st part to check expected results in the end. +# Cleanup section consists of explicit disconnect (for killed, or +# not disconnected before shutdown). +# connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'new_trx_0'; INSERT INTO t SET a=0; @@ -374,6 +413,11 @@ XA COMMIT 'new_trx_6'; XA COMMIT 'new_trx_7'; XA COMMIT 'new_trx_8'; XA COMMIT 'new_trx_9'; +# +# Symmetrically to the pre-restart, the resurrected trx:s are committed +# [10, 10 + 5 - 1] +# and the rest is rolled back. +# XA START 'trx_10'; ERROR XAE08: XAER_DUPID: The XID already exists XA COMMIT 'trx_10'; @@ -404,6 +448,9 @@ XA ROLLBACK 'trx_18'; XA START 'trx_19'; ERROR XAE08: XAER_DUPID: The XID already exists XA ROLLBACK 'trx_19'; +# +# Verification of correct results of recovered XA transaction handling: +# SELECT * FROM t; a 100 @@ -473,6 +520,9 @@ connection default; XA ROLLBACK 'trx_11'; ERROR XAE04: XAER_NOTA: Unknown XID disconnect conn10; +# +# A. The temp table only prepared XA recovers only formally to +# let post recovery XA COMMIT or XA ROLLBACK with no effect. connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@sql_log_bin = OFF; CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb; @@ -480,6 +530,8 @@ XA START 'trx1tmp'; INSERT INTO tmp1 SET a=1; XA END 'trx1tmp'; XA PREPARE 'trx1tmp'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@sql_log_bin = OFF; CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb; @@ -487,6 +539,8 @@ XA START 'trx2tmp'; INSERT INTO tmp1 SET a=1; XA END 'trx2tmp'; XA PREPARE 'trx2tmp'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@sql_log_bin = OFF; CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb; @@ -494,6 +548,11 @@ XA START 'trx3tmp'; INSERT INTO tmp1 SET a=1; XA END 'trx3tmp'; XA PREPARE 'trx3tmp'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back +# +# Various prohibited XA state changes to test here: +# connection default; XA COMMIT 'trx1tmp'; ERROR XAE04: XAER_NOTA: Unknown XID @@ -502,18 +561,18 @@ ERROR XAE04: XAER_NOTA: Unknown XID XA START 'trx1tmp'; ERROR XAE08: XAER_DUPID: The XID already exists connection default; -*** 3 prepared transactions must be in the list *** +*** 0 transactions must be in the list *** XA RECOVER; formatID gtrid_length bqual_length data -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N connection conn1tmp; disconnect conn1tmp; connection default; XA COMMIT 'trx1tmp'; KILL connection CONN_ID; XA COMMIT 'trx3tmp'; +# +# B. "Read-only" (select) prepared XA recovers only formally to +# let post recovery XA COMMIT or XA ROLLBACK with no effect. connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx1ro'; SELECT * from t ORDER BY a; @@ -541,6 +600,8 @@ a 100 XA END 'trx1ro'; XA PREPARE 'trx1ro'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx2ro'; SELECT * from t ORDER BY a; @@ -568,6 +629,8 @@ a 100 XA END 'trx2ro'; XA PREPARE 'trx2ro'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx3ro'; SELECT * from t ORDER BY a; @@ -595,47 +658,55 @@ a 100 XA END 'trx3ro'; XA PREPARE 'trx3ro'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connection default; -*** 4 prepared transactions must be in the list *** +*** 0 transactions must be in the list *** XA RECOVER; formatID gtrid_length bqual_length data -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N connection conn1ro; disconnect conn1ro; connection default; XA ROLLBACK 'trx1ro'; KILL connection CONN_ID; XA ROLLBACK 'trx3ro'; +# +# C. Empty prepared XA recovers only formally to +# let post recovery XA COMMIT or XA ROLLBACK with no effect. +# connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx1empty'; XA END 'trx1empty'; XA PREPARE 'trx1empty'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx2empty'; XA END 'trx2empty'; XA PREPARE 'trx2empty'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx3empty'; XA END 'trx3empty'; XA PREPARE 'trx3empty'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connection default; -*** 5 prepared transactions must be in the list *** +*** 0 transactions must be in the list *** XA RECOVER; formatID gtrid_length bqual_length data -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N connection conn1empty; disconnect conn1empty; connection default; XA COMMIT 'trx1empty'; KILL connection CONN_ID; XA COMMIT 'trx3empty'; +# +# D. Not prepared XA disconnects to be cleared out, +# no effect on data left as well. +# Few more prohibited XA state transactions is checked out. +# connect conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx1unprepared'; INSERT INTO t set a=0; @@ -650,6 +721,12 @@ disconnect conn1unprepared; connection default; XA COMMIT 'trx1unprepared'; ERROR XAE04: XAER_NOTA: Unknown XID +# +# II. Regular case. +# +# Prepared transactions get disconnected in three ways: +# actively, being killed and by the server shutdown. +# connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@binlog_format = STATEMENT; XA START 'trx_0'; @@ -810,18 +887,27 @@ XA END 'trx_19'; XA PREPARE 'trx_19'; connection default; KILL CONNECTION CONN_ID; +# [0, 5 - 1] are rolled back now connection default; XA ROLLBACK 'trx_0'; XA ROLLBACK 'trx_1'; XA ROLLBACK 'trx_2'; XA ROLLBACK 'trx_3'; XA ROLLBACK 'trx_4'; +# [5, 5 + 5 - 1] get committed XA COMMIT 'trx_5'; XA COMMIT 'trx_6'; XA COMMIT 'trx_7'; XA COMMIT 'trx_8'; XA COMMIT 'trx_9'; # Kill and restart +# +# III. Post server-restart verification. +# It concludes survived XA:s with a number of commits and rollbacks +# as configured in the 1st part to check expected results in the end. +# Cleanup section consists of explicit disconnect (for killed, or +# not disconnected before shutdown). +# connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'new_trx_0'; INSERT INTO t SET a=0; @@ -903,6 +989,11 @@ XA COMMIT 'new_trx_6'; XA COMMIT 'new_trx_7'; XA COMMIT 'new_trx_8'; XA COMMIT 'new_trx_9'; +# +# Symmetrically to the pre-restart, the resurrected trx:s are committed +# [10, 10 + 5 - 1] +# and the rest is rolled back. +# XA START 'trx_10'; ERROR XAE08: XAER_DUPID: The XID already exists XA COMMIT 'trx_10'; @@ -933,6 +1024,9 @@ XA ROLLBACK 'trx_18'; XA START 'trx_19'; ERROR XAE08: XAER_DUPID: The XID already exists XA ROLLBACK 'trx_19'; +# +# Verification of correct results of recovered XA transaction handling: +# SELECT * FROM t; a 100 diff --git a/mysql-test/main/xa_sync.result b/mysql-test/main/xa_sync.result index e7dd9b028470c..4baced5108f20 100644 --- a/mysql-test/main/xa_sync.result +++ b/mysql-test/main/xa_sync.result @@ -8,6 +8,8 @@ connection con1; XA START 'xatest'; XA END 'xatest'; XA PREPARE 'xatest'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connection con2; SET debug_sync='xa_after_search SIGNAL parked WAIT_FOR go'; XA COMMIT 'xatest'; @@ -18,11 +20,11 @@ disconnect con1; SET debug_sync='now SIGNAL go'; connection con2; ERROR XAE04: XAER_NOTA: Unknown XID -*** Must have 'xatest' in the list +*** Must not have 'xatest' in the list XA RECOVER; formatID gtrid_length bqual_length data -1 6 0 xatest XA COMMIT 'xatest'; +ERROR XAE04: XAER_NOTA: Unknown XID disconnect con2; connection default; SET debug_sync='RESET'; @@ -32,6 +34,8 @@ connection con1; XA START 'xatest'; XA END 'xatest'; XA PREPARE 'xatest'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connection con2; SET debug_sync='xa_after_search SIGNAL parked WAIT_FOR go'; XA ROLLBACK 'xatest'; @@ -42,11 +46,11 @@ disconnect con1; SET debug_sync='now SIGNAL go'; connection con2; ERROR XAE04: XAER_NOTA: Unknown XID -*** Must have 'xatest' in the list +*** Must not have 'xatest' in the list XA RECOVER; formatID gtrid_length bqual_length data -1 6 0 xatest XA ROLLBACK 'xatest'; +ERROR XAE04: XAER_NOTA: Unknown XID disconnect con2; connection default; SET debug_sync='RESET'; diff --git a/mysql-test/main/xa_sync.test b/mysql-test/main/xa_sync.test index ad1243ce6f80e..d81163affe33d 100644 --- a/mysql-test/main/xa_sync.test +++ b/mysql-test/main/xa_sync.test @@ -35,10 +35,10 @@ while ($i) connection con2; --error ER_XAER_NOTA reap; - --echo *** Must have 'xatest' in the list + --echo *** Must not have 'xatest' in the list XA RECOVER; - # second time yields no error - --error 0,1402 + # attempt to commit non-existing transaction fails + --error ER_XAER_NOTA --eval $op disconnect con2; diff --git a/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc b/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc index 4a83aa5c28241..8a493eb3ac8c2 100644 --- a/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc +++ b/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc @@ -10,7 +10,12 @@ # --connection default ---echo *** $num_trx_prepared prepared transactions must be in the list *** +--let $num_of_recovered = 0 +if (`select '$type' <> 'ro' and '$type' <> 'empty' and '$type' <> 'tmp'`) +{ + --let $num_of_recovered = $num_trx_prepared +} +--echo *** $num_of_recovered transactions must be in the list *** --replace_column 2 LEN1 3 LEN2 4 TRX_N XA RECOVER; @@ -23,7 +28,7 @@ XA RECOVER; --source include/wait_condition.inc # It will conclude now ---error 0,1402 +--error 0,1402,ER_XAER_NOTA --eval $terminate_with 'trx1$type' --replace_result $conn3_id CONN_ID @@ -33,5 +38,5 @@ XA RECOVER; --source include/wait_condition.inc # It will conclude now ---error 0,1402 +--error 0,1402,ER_XAER_NOTA --eval $terminate_with 'trx3$type' diff --git a/mysql-test/suite/binlog/include/binlog_xa_prepared_do_and_restart.inc b/mysql-test/suite/binlog/include/binlog_xa_prepared_do_and_restart.inc index cbd740fdae418..3952972afb3bf 100644 --- a/mysql-test/suite/binlog/include/binlog_xa_prepared_do_and_restart.inc +++ b/mysql-test/suite/binlog/include/binlog_xa_prepared_do_and_restart.inc @@ -45,10 +45,9 @@ # D. Prove that not prepared XA remains to be cleared out by disconnection. # -# -# A. The temp table only prepared XA recovers only formally to -# let post recovery XA COMMIT or XA ROLLBACK with no effect. - +--echo # +--echo # A. The temp table only prepared XA recovers only formally to +--echo # let post recovery XA COMMIT or XA ROLLBACK with no effect. --let $type = tmp --let $index = 1 --let $sql_init1 = SET @@sql_log_bin = OFF @@ -63,9 +62,9 @@ --source suite/binlog/include/binlog_xa_prepare_connection.inc --let $conn3_id=`SELECT connection_id()` -# -# Various prohibited XA state changes to test here: -# +--echo # +--echo # Various prohibited XA state changes to test here: +--echo # --connection default # Stealing is not allowed @@ -84,10 +83,9 @@ --let $num_trx_prepared = $index --source suite/binlog/include/binlog_xa_prepare_disconnect.inc -# -# B. "Read-only" (select) prepared XA recovers only formally to -# let post recovery XA COMMIT or XA ROLLBACK with no effect. -# +--echo # +--echo # B. "Read-only" (select) prepared XA recovers only formally to +--echo # let post recovery XA COMMIT or XA ROLLBACK with no effect. --let $type=ro --let $index = 1 --let $sql_init1 = @@ -107,10 +105,10 @@ --inc $num_trx_prepared --source suite/binlog/include/binlog_xa_prepare_disconnect.inc -# -# C. Empty prepared XA recovers only formally to -# let post recovery XA COMMIT or XA ROLLBACK with no effect. -# +--echo # +--echo # C. Empty prepared XA recovers only formally to +--echo # let post recovery XA COMMIT or XA ROLLBACK with no effect. +--echo # --let $type=empty --let $index = 1 --let $sql_init1 = @@ -129,11 +127,11 @@ --inc $num_trx_prepared --source suite/binlog/include/binlog_xa_prepare_disconnect.inc -# -# D. Not prepared XA disconnects to be cleared out, -# no effect on data left as well. -# Few more prohibited XA state transactions is checked out. -# +--echo # +--echo # D. Not prepared XA disconnects to be cleared out, +--echo # no effect on data left as well. +--echo # Few more prohibited XA state transactions is checked out. +--echo # --let $type=unprepared --let $prev_count=`SELECT count(*) from t` @@ -161,12 +159,12 @@ if (`SELECT count(*) > $prev_count from t`) --die } -# -# II. Regular case. -# -# Prepared transactions get disconnected in three ways: -# actively, being killed and by the server shutdown. -# +--echo # +--echo # II. Regular case. +--echo # +--echo # Prepared transactions get disconnected in three ways: +--echo # actively, being killed and by the server shutdown. +--echo # --let $i=0 while ($i < $conn_number) { @@ -208,7 +206,7 @@ while ($i < $conn_number) --inc $i } -# [0, $rollback_number - 1] are rolled back now +--echo # [0, $rollback_number - 1] are rolled back now --connection default --let $i=0 @@ -219,7 +217,7 @@ while ($i < $rollback_number) --inc $i } -# [$rollback_number, $rollback_number + $commit_number - 1] get committed +--echo # [$rollback_number, $rollback_number + $commit_number - 1] get committed while ($i < $term_number) { --eval XA COMMIT 'trx_$i' @@ -229,13 +227,13 @@ while ($i < $term_number) --source include/$how_to_restart -# -# III. Post server-restart verification. -# It concludes survived XA:s with a number of commits and rollbacks -# as configured in the 1st part to check expected results in the end. -# Cleanup section consists of explicit disconnect (for killed, or -# not disconnected before shutdown). -# +--echo # +--echo # III. Post server-restart verification. +--echo # It concludes survived XA:s with a number of commits and rollbacks +--echo # as configured in the 1st part to check expected results in the end. +--echo # Cleanup section consists of explicit disconnect (for killed, or +--echo # not disconnected before shutdown). +--echo # # New XA can be prepared and committed --let $k = 0 @@ -265,11 +263,11 @@ while ($k < $post_restart_conn_number) --inc $k } -# -# Symmetrically to the pre-restart, the resurrected trx:s are committed -# [$term_number, $term_number + $commit_number - 1] -# and the rest is rolled back. -# +--echo # +--echo # Symmetrically to the pre-restart, the resurrected trx:s are committed +--echo # [$term_number, $term_number + $commit_number - 1] +--echo # and the rest is rolled back. +--echo # --let $i = $term_number while ($i < `SELECT $term_number + $commit_number`) @@ -290,9 +288,9 @@ while ($i < $conn_number) --inc $i } -# -# Verification of correct results of recovered XA transaction handling: -# +--echo # +--echo # Verification of correct results of recovered XA transaction handling: +--echo # SELECT * FROM t; --let $type=tmp diff --git a/mysql-test/suite/binlog/r/binlog_empty_xa_prepared.result b/mysql-test/suite/binlog/r/binlog_empty_xa_prepared.result index c23b0cc30592e..1d5a4020ce043 100644 --- a/mysql-test/suite/binlog/r/binlog_empty_xa_prepared.result +++ b/mysql-test/suite/binlog/r/binlog_empty_xa_prepared.result @@ -4,18 +4,20 @@ XA START '3'; CREATE TEMPORARY TABLE tmp_1(c INT); XA END '3'; XA PREPARE '3'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back DROP TEMPORARY TABLE tmp_1; -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state ALTER TABLE tmp_1 DROP COLUMN c; -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state DROP TEMPORARY SEQUENCE seq_1; -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state ALTER SEQUENCE seq_1 INCREMENT BY 1; -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state CREATE TEMPORARY TABLE tmp_2(c INT); -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state CREATE TEMPORARY SEQUENCE seq_2; -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state XA ROLLBACK '3'; DROP SEQUENCE seq_1; DROP TABLE tmp_1; @@ -28,18 +30,20 @@ XA START '3'; CREATE TEMPORARY TABLE tmp_1(c INT); XA END '3'; XA PREPARE '3'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back DROP TEMPORARY TABLE tmp_1; -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state ALTER TABLE tmp_1 DROP COLUMN c; -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state DROP TEMPORARY SEQUENCE seq_1; -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state ALTER SEQUENCE seq_1 INCREMENT BY 1; -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state CREATE TEMPORARY TABLE tmp_2(c INT); -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state CREATE TEMPORARY SEQUENCE seq_2; -ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state XA ROLLBACK '3'; SET @@binlog_format="mixed"; # Proof of correct logging incl empty XA-PREPARE @@ -50,11 +54,6 @@ master-bin.000001 # Query # # use `test`; CREATE TEMPORARY SEQUENCE seq_1 master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; CREATE TEMPORARY TABLE tmp_1(c INT) master-bin.000001 # Query # # COMMIT -master-bin.000001 # Gtid # # XA START X'33',X'',1 GTID #-#-# -master-bin.000001 # Query # # XA END X'33',X'',1 -master-bin.000001 # XA_prepare # # XA PREPARE X'33',X'',1 -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # XA ROLLBACK X'33',X'',1 RESET MASTER; CREATE TABLE t1 (a INT) ENGINE=MyISAM; CREATE TABLE t2 (id INT PRIMARY KEY) ENGINE=InnoDB; @@ -87,11 +86,6 @@ master-bin.000001 # Annotate_rows # # REPLACE INTO t1 SELECT * FROM t1 master-bin.000001 # Table_map # # table_id: # (test.t1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F master-bin.000001 # Query # # COMMIT -master-bin.000001 # Gtid # # XA START X'31',X'',1 GTID #-#-# -master-bin.000001 # Query # # XA END X'31',X'',1 -master-bin.000001 # XA_prepare # # XA PREPARE X'31',X'',1 -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # XA ROLLBACK X'31',X'',1 master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # use `test`; DROP TABLE `t1`,`t2` /* generated by server */ RESET MASTER; @@ -123,11 +117,6 @@ master-bin.000001 # Annotate_rows # # SELECT NEXT VALUE FOR s master-bin.000001 # Table_map # # table_id: # (test.s) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F master-bin.000001 # Query # # COMMIT -master-bin.000001 # Gtid # # XA START X'32',X'',1 GTID #-#-# -master-bin.000001 # Query # # XA END X'32',X'',1 -master-bin.000001 # XA_prepare # # XA PREPARE X'32',X'',1 -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # XA ROLLBACK X'32',X'',1 master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # use `test`; DROP SEQUENCE `s` /* generated by server */ master-bin.000001 # Gtid # # GTID #-#-# @@ -142,13 +131,27 @@ SELECT * FROM t1 WHERE a = 2; a XA END '1'; XA PREPARE '1'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection default; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 1 XA COMMIT '1'; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID +connect con1_2,localhost,root,,; +XA START '1'; +INSERT INTO t1 VALUES (2),(1); +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * FROM t1 WHERE a = 2; +a +XA END '1'; +XA PREPARE '1'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back +XA COMMIT '1'; +disconnect con1_2; +connection default; Must be no XA PREPARE group nor XA completion one: include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info @@ -164,11 +167,6 @@ master-bin.000001 # Annotate_rows # # SELECT NEXT VALUE FOR s master-bin.000001 # Table_map # # table_id: # (test.s) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F master-bin.000001 # Query # # COMMIT -master-bin.000001 # Gtid # # XA START X'32',X'',1 GTID #-#-# -master-bin.000001 # Query # # XA END X'32',X'',1 -master-bin.000001 # XA_prepare # # XA PREPARE X'32',X'',1 -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # XA ROLLBACK X'32',X'',1 master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # use `test`; DROP SEQUENCE `s` /* generated by server */ master-bin.000001 # Gtid # # GTID #-#-# @@ -179,8 +177,8 @@ master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (1) master-bin.000001 # Xid # # COMMIT /* XID */ DROP TABLE t1; -connect con2,localhost,root,,; CREATE TABLE tm (a INT PRIMARY KEY) ENGINE=MyISAM; +connect con2,localhost,root,,; XA START '1'; INSERT INTO tm VALUES (1),(1); ERROR 23000: Duplicate entry '1' for key 'PRIMARY' @@ -188,13 +186,27 @@ SELECT * FROM tm WHERE a = 2; a XA END '1'; XA PREPARE '1'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con2; connection default; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 1 XA ROLLBACK '1'; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID +connect con2_2,localhost,root,,; +XA START '1'; +INSERT INTO tm VALUES (1),(1); +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * FROM tm WHERE a = 2; +a +XA END '1'; +XA PREPARE '1'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back +XA ROLLBACK '1'; +disconnect con2_2; +connection default; Must be no XA PREPARE group nor XA completion one: include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info @@ -210,11 +222,6 @@ master-bin.000001 # Annotate_rows # # SELECT NEXT VALUE FOR s master-bin.000001 # Table_map # # table_id: # (test.s) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F master-bin.000001 # Query # # COMMIT -master-bin.000001 # Gtid # # XA START X'32',X'',1 GTID #-#-# -master-bin.000001 # Query # # XA END X'32',X'',1 -master-bin.000001 # XA_prepare # # XA PREPARE X'32',X'',1 -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # XA ROLLBACK X'32',X'',1 master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # use `test`; DROP SEQUENCE `s` /* generated by server */ master-bin.000001 # Gtid # # GTID #-#-# @@ -237,6 +244,16 @@ SET pseudo_slave_mode=1; XA START 'a'; XA END 'a'; XA PREPARE 'a'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA ROLLBACK 'a'; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID +include/show_binlog_events.inc +connect con_32852,localhost,root,,; +XA START 'a'; +CREATE TEMPORARY TABLE t1 (a INT) ENGINE=InnoDB; +SELECT * FROM t1; +a +disconnect con_32852; +connection default; include/show_binlog_events.inc diff --git a/mysql-test/suite/binlog/r/binlog_truncate_active_log.result b/mysql-test/suite/binlog/r/binlog_truncate_active_log.result index 53cfb9577d7d3..376186f936e20 100644 --- a/mysql-test/suite/binlog/r/binlog_truncate_active_log.result +++ b/mysql-test/suite/binlog/r/binlog_truncate_active_log.result @@ -1,5 +1,6 @@ call mtr.add_suppression("Can.t init tc log"); call mtr.add_suppression("Aborting"); +call mtr.add_suppression("Found 1 prepared XA transactions"); RESET MASTER; SET @@global.sync_binlog=1; CREATE TABLE t (f INT) ENGINE=INNODB; @@ -133,12 +134,12 @@ SELECT * FROM t4; f DELETE FROM t; # Case C. -CREATE PROCEDURE sp_blank_xa() +CREATE PROCEDURE sp_xa() BEGIN -XA START 'blank'; -DELETE FROM t2 WHERE f = 0 /* no such record */; -XA END 'blank'; -XA PREPARE 'blank'; +XA START 'xid'; +DELETE FROM t WHERE f = 10; +XA END 'xid'; +XA PREPARE 'xid'; END| connect master1,localhost,root,,; connect master2,localhost,root,,; @@ -149,11 +150,11 @@ INSERT INTO t VALUES (10); INSERT INTO tm VALUES (10); connection master1; SET DEBUG_SYNC= "commit_before_get_LOCK_commit_ordered SIGNAL master1_ready WAIT_FOR signal_never_arrives"; -INSERT INTO t VALUES (20); +CALL sp_xa; connection master2; SET DEBUG_SYNC= "now WAIT_FOR master1_ready"; SET DEBUG_SYNC= "commit_before_get_LOCK_after_binlog_sync SIGNAL master2_ready"; -CALL sp_blank_xa; +INSERT INTO t2 VALUES (20); connection master3; SET DEBUG_SYNC= "now WAIT_FOR master2_ready"; SELECT @@global.gtid_binlog_pos as 'Before the crash'; @@ -174,7 +175,7 @@ disconnect master2; disconnect master3; disconnect master4; # restart: --init-rpl-role=SLAVE --sync-binlog=1 --log-warnings=3 -FOUND 1 /Successfully truncated.*to remove transactions starting from GTID 0-1-15/ in mysqld.1.err +FOUND 1 /Successfully truncated.*to remove transactions starting from GTID 0-1-16/ in mysqld.1.err Pre-crash binlog file content: include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info @@ -182,12 +183,12 @@ master-bin.000003 # Gtid # # BEGIN GTID #-#-# master-bin.000003 # Query # # use `test`; DELETE FROM t master-bin.000003 # Xid # # COMMIT /* XID */ master-bin.000003 # Gtid # # GTID #-#-# -master-bin.000003 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_blank_xa`() +master-bin.000003 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_xa`() BEGIN -XA START 'blank'; -DELETE FROM t2 WHERE f = 0 /* no such record */; -XA END 'blank'; -XA PREPARE 'blank'; +XA START 'xid'; +DELETE FROM t WHERE f = 10; +XA END 'xid'; +XA PREPARE 'xid'; END master-bin.000003 # Gtid # # BEGIN GTID #-#-# master-bin.000003 # Query # # use `test`; INSERT INTO t VALUES (10) @@ -195,90 +196,13 @@ master-bin.000003 # Xid # # COMMIT /* XID */ master-bin.000003 # Gtid # # BEGIN GTID #-#-# master-bin.000003 # Query # # use `test`; INSERT INTO tm VALUES (10) master-bin.000003 # Query # # COMMIT +master-bin.000003 # Gtid # # XA START X'786964',X'',1 GTID #-#-# +master-bin.000003 # Query # # use `test`; DELETE FROM t WHERE f = 10 +master-bin.000003 # Query # # XA END X'786964',X'',1 +master-bin.000003 # XA_prepare # # XA PREPARE X'786964',X'',1 SELECT @@global.gtid_binlog_pos as 'After the crash'; After the crash -0-1-14 -"One row should be present in table 't'" -SELECT * FROM t; -f -10 -"No row should be present in table 't4'" -SELECT * FROM t4; -f -DELETE FROM t; -DROP PROCEDURE sp_blank_xa; -# Case D. -CREATE PROCEDURE sp_xa() -BEGIN -XA START 'xid'; -DELETE FROM t WHERE f = 10; -XA END 'xid'; -XA PREPARE 'xid'; -END| -connect master1,localhost,root,,; -connect master2,localhost,root,,; -connect master3,localhost,root,,; -connect master4,localhost,root,,; -connection default; -INSERT INTO t VALUES (10); -INSERT INTO tm VALUES (10); -connection master1; -SET DEBUG_SYNC= "commit_before_get_LOCK_commit_ordered SIGNAL master1_ready WAIT_FOR signal_never_arrives"; -CALL sp_xa; -connection master2; -SET DEBUG_SYNC= "now WAIT_FOR master1_ready"; -SET DEBUG_SYNC= "commit_before_get_LOCK_after_binlog_sync SIGNAL master2_ready"; -INSERT INTO t2 VALUES (20); -connection master3; -SET DEBUG_SYNC= "now WAIT_FOR master2_ready"; -SELECT @@global.gtid_binlog_pos as 'Before the crash'; -Before the crash -0-1-21 -connection master4; -SET DEBUG_SYNC= "ha_commit_trans_before_log_and_order SIGNAL master4_ready WAIT_FOR signal_never_arrives"; -INSERT INTO t4 VALUES (13); -connection master3; -SET DEBUG_SYNC= "now WAIT_FOR master4_ready"; -SELECT @@global.gtid_binlog_pos as 'Before the crash and never logged trx'; -Before the crash and never logged trx -0-1-21 -connection default; -# Kill the server -disconnect master1; -disconnect master2; -disconnect master3; -disconnect master4; -# restart: --init-rpl-role=SLAVE --sync-binlog=1 --log-warnings=3 -FOUND 1 /Successfully truncated.*to remove transactions starting from GTID 0-1-21/ in mysqld.1.err -Pre-crash binlog file content: -include/show_binlog_events.inc -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000004 # Gtid # # BEGIN GTID #-#-# -master-bin.000004 # Query # # use `test`; DELETE FROM t -master-bin.000004 # Xid # # COMMIT /* XID */ -master-bin.000004 # Gtid # # GTID #-#-# -master-bin.000004 # Query # # use `test`; DROP PROCEDURE sp_blank_xa -master-bin.000004 # Gtid # # GTID #-#-# -master-bin.000004 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_xa`() -BEGIN -XA START 'xid'; -DELETE FROM t WHERE f = 10; -XA END 'xid'; -XA PREPARE 'xid'; -END -master-bin.000004 # Gtid # # BEGIN GTID #-#-# -master-bin.000004 # Query # # use `test`; INSERT INTO t VALUES (10) -master-bin.000004 # Xid # # COMMIT /* XID */ -master-bin.000004 # Gtid # # BEGIN GTID #-#-# -master-bin.000004 # Query # # use `test`; INSERT INTO tm VALUES (10) -master-bin.000004 # Query # # COMMIT -master-bin.000004 # Gtid # # XA START X'786964',X'',1 GTID #-#-# -master-bin.000004 # Query # # use `test`; DELETE FROM t WHERE f = 10 -master-bin.000004 # Query # # XA END X'786964',X'',1 -master-bin.000004 # XA_prepare # # XA PREPARE X'786964',X'',1 -SELECT @@global.gtid_binlog_pos as 'After the crash'; -After the crash -0-1-20 +0-1-15 "One row should be present in table 't'" SELECT * FROM t; f @@ -286,6 +210,7 @@ f "No row should be present in table 't4'" SELECT * FROM t4; f +xa rollback 'xid'; DELETE FROM t; DROP PROCEDURE sp_xa; # Cleanup diff --git a/mysql-test/suite/binlog/r/binlog_xa_checkpoint.result b/mysql-test/suite/binlog/r/binlog_xa_checkpoint.result index d8a5818674ff3..fdccb66ac4c28 100644 --- a/mysql-test/suite/binlog/r/binlog_xa_checkpoint.result +++ b/mysql-test/suite/binlog/r/binlog_xa_checkpoint.result @@ -1,33 +1,63 @@ RESET MASTER; CREATE TABLE t1 (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=Innodb; +# case A: Binlog CP after XAP +set @@global.max_binlog_size=4096; connect con1,localhost,root,,; -SET DEBUG_SYNC= "at_unlog_xa_prepare SIGNAL con1_ready WAIT_FOR con1_go"; +SET SESSION binlog_annotate_row_events=OFF; XA START '1'; -INSERT INTO t1 SET a=1; +INSERT INTO t1 SET a=1,b=repeat('b',@@global.max_binlog_size);; XA END '1'; -XA PREPARE '1';; +XA PREPARE '1'; +### Repeatable check block in the cases A,B. connection default; -SET DEBUG_SYNC= "now WAIT_FOR con1_ready"; -FLUSH LOGS; -FLUSH LOGS; -FLUSH LOGS; show binary logs; Log_name File_size master-bin.000001 # master-bin.000002 # -master-bin.000003 # -master-bin.000004 # +*** the old must show the prepared xa include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000004 # Format_desc # # SERVER_VERSION, BINLOG_VERSION -master-bin.000004 # Gtid_list # # [#-#-#] -master-bin.000004 # Binlog_checkpoint # # master-bin.000001 -SET DEBUG_SYNC= "now SIGNAL con1_go"; -connection con1; -*** master-bin.000004 checkpoint must show up now *** +master-bin.000001 # Gtid # # XA START X'31',X'',1 GTID #-#-# +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # XA END X'31',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'31',X'',1 +master-bin.000001 # Rotate # # master-bin.000002;pos=POS +*** the current master-bin.000001 must show the checkpoint event with its name *** +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000002 # Format_desc # # SERVER_VERSION, BINLOG_VERSION +master-bin.000002 # Gtid_list # # [#-#-#] +master-bin.000002 # Binlog_checkpoint # # master-bin.000001 +master-bin.000002 # Binlog_checkpoint # # master-bin.000002 +### end of check block +# case B: Binlog CP after XAC +connection default; +SET STATEMENT binlog_annotate_row_events=OFF FOR INSERT INTO t1 SET a=2,b=repeat('b', 3530); connection con1; -XA ROLLBACK '1'; -SET debug_sync = 'reset'; +XA COMMIT '1'; +### Repeatable check block in the cases A,B. +connection default; +show binary logs; +Log_name File_size +master-bin.000001 # +master-bin.000002 # +master-bin.000003 # +*** the old master-bin.000002 must show the committed xa +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000002 # Gtid # # GTID #-#-# +master-bin.000002 # Query # # XA COMMIT X'31',X'',1 +master-bin.000002 # Rotate # # master-bin.000003;pos=POS +*** the current master-bin.000002 must show the checkpoint event with its name *** +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000003 # Format_desc # # SERVER_VERSION, BINLOG_VERSION +master-bin.000003 # Gtid_list # # [#-#-#] +master-bin.000003 # Binlog_checkpoint # # master-bin.000002 +master-bin.000003 # Binlog_checkpoint # # master-bin.000003 +### end of check block +disconnect con1; connection default; DROP TABLE t1; -SET debug_sync = 'reset'; +set @@global.max_binlog_size=1073741824; diff --git a/mysql-test/suite/binlog/r/binlog_xa_prepared_bugs.result b/mysql-test/suite/binlog/r/binlog_xa_prepared_bugs.result new file mode 100644 index 0000000000000..4a9d37fa3df39 --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_xa_prepared_bugs.result @@ -0,0 +1,50 @@ +RESET MASTER; +CREATE TABLE ta (c INT KEY) engine=Aria; +XA START 'xid_a'; +INSERT INTO ta VALUES (1); +XA END 'xid_a'; +XA PREPARE 'xid_a'; +Warnings: +Warning 1030 Got error 131 "Command not supported by the engine" from storage engine Aria +Note 4226 Found to be read-only XA transaction is rolled back +LOAD INDEX INTO CACHE c KEY(PRIMARY); +Table Op Msg_type Msg_text +test.c preload_keys Error XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state +test.c preload_keys Error XAER_RMFAIL: The command cannot be executed when global transaction is in the ROLLBACK ONLY state +test.c preload_keys error Corrupt +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +XA ROLLBACK 'xid_a'; +CREATE TABLE ti (c INT KEY) engine=Innodb; +XA START 'xid_i'; +INSERT INTO ti VALUES (1); +XA END 'xid_i'; +XA PREPARE 'xid_i'; +LOAD INDEX INTO CACHE c KEY(PRIMARY); +Table Op Msg_type Msg_text +test.c preload_keys Error XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +test.c preload_keys Error XAER_RMFAIL: The command cannot be executed when global transaction is in the PREPARED state +test.c preload_keys error Corrupt +XA COMMIT 'xid_i'; +SELECT * FROM ti; +c +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; CREATE TABLE ta (c INT KEY) engine=Aria +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Annotate_rows # # INSERT INTO ta VALUES (1) +master-bin.000001 # Table_map # # table_id: # (test.ta) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; CREATE TABLE ti (c INT KEY) engine=Innodb +master-bin.000001 # Gtid # # XA START X'7869645f69',X'',1 GTID #-#-# +master-bin.000001 # Annotate_rows # # INSERT INTO ti VALUES (1) +master-bin.000001 # Table_map # # table_id: # (test.ti) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # XA END X'7869645f69',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7869645f69',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'7869645f69',X'',1 +drop table ta,ti; diff --git a/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result b/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result index a84586111bcc7..78e179ff3bbf5 100644 --- a/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result +++ b/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result @@ -4,6 +4,9 @@ CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where typ call mtr.add_suppression("Found 10 prepared XA transactions"); CREATE TABLE t (a INT) ENGINE=innodb; INSERT INTO t VALUES(100); +# +# A. The temp table only prepared XA recovers only formally to +# let post recovery XA COMMIT or XA ROLLBACK with no effect. connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@sql_log_bin = OFF; CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb; @@ -11,6 +14,8 @@ XA START 'trx1tmp'; INSERT INTO tmp1 SET a=1; XA END 'trx1tmp'; XA PREPARE 'trx1tmp'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@sql_log_bin = OFF; CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb; @@ -18,6 +23,8 @@ XA START 'trx2tmp'; INSERT INTO tmp1 SET a=1; XA END 'trx2tmp'; XA PREPARE 'trx2tmp'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@sql_log_bin = OFF; CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb; @@ -25,6 +32,11 @@ XA START 'trx3tmp'; INSERT INTO tmp1 SET a=1; XA END 'trx3tmp'; XA PREPARE 'trx3tmp'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back +# +# Various prohibited XA state changes to test here: +# connection default; XA COMMIT 'trx1tmp'; ERROR XAE04: XAER_NOTA: Unknown XID @@ -33,18 +45,18 @@ ERROR XAE04: XAER_NOTA: Unknown XID XA START 'trx1tmp'; ERROR XAE08: XAER_DUPID: The XID already exists connection default; -*** 3 prepared transactions must be in the list *** +*** 0 transactions must be in the list *** XA RECOVER; formatID gtrid_length bqual_length data -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N connection conn1tmp; disconnect conn1tmp; connection default; XA COMMIT 'trx1tmp'; KILL connection CONN_ID; XA COMMIT 'trx3tmp'; +# +# B. "Read-only" (select) prepared XA recovers only formally to +# let post recovery XA COMMIT or XA ROLLBACK with no effect. connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx1ro'; SELECT * from t ORDER BY a; @@ -52,6 +64,8 @@ a 100 XA END 'trx1ro'; XA PREPARE 'trx1ro'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx2ro'; SELECT * from t ORDER BY a; @@ -59,6 +73,8 @@ a 100 XA END 'trx2ro'; XA PREPARE 'trx2ro'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx3ro'; SELECT * from t ORDER BY a; @@ -66,47 +82,55 @@ a 100 XA END 'trx3ro'; XA PREPARE 'trx3ro'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connection default; -*** 4 prepared transactions must be in the list *** +*** 0 transactions must be in the list *** XA RECOVER; formatID gtrid_length bqual_length data -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N connection conn1ro; disconnect conn1ro; connection default; XA ROLLBACK 'trx1ro'; KILL connection CONN_ID; XA ROLLBACK 'trx3ro'; +# +# C. Empty prepared XA recovers only formally to +# let post recovery XA COMMIT or XA ROLLBACK with no effect. +# connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx1empty'; XA END 'trx1empty'; XA PREPARE 'trx1empty'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx2empty'; XA END 'trx2empty'; XA PREPARE 'trx2empty'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx3empty'; XA END 'trx3empty'; XA PREPARE 'trx3empty'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connection default; -*** 5 prepared transactions must be in the list *** +*** 0 transactions must be in the list *** XA RECOVER; formatID gtrid_length bqual_length data -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N connection conn1empty; disconnect conn1empty; connection default; XA COMMIT 'trx1empty'; KILL connection CONN_ID; XA COMMIT 'trx3empty'; +# +# D. Not prepared XA disconnects to be cleared out, +# no effect on data left as well. +# Few more prohibited XA state transactions is checked out. +# connect conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx1unprepared'; INSERT INTO t set a=0; @@ -121,6 +145,12 @@ disconnect conn1unprepared; connection default; XA COMMIT 'trx1unprepared'; ERROR XAE04: XAER_NOTA: Unknown XID +# +# II. Regular case. +# +# Prepared transactions get disconnected in three ways: +# actively, being killed and by the server shutdown. +# connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@binlog_format = STATEMENT; XA START 'trx_0'; @@ -281,18 +311,27 @@ XA END 'trx_19'; XA PREPARE 'trx_19'; connection default; KILL CONNECTION CONN_ID; +# [0, 5 - 1] are rolled back now connection default; XA ROLLBACK 'trx_0'; XA ROLLBACK 'trx_1'; XA ROLLBACK 'trx_2'; XA ROLLBACK 'trx_3'; XA ROLLBACK 'trx_4'; +# [5, 5 + 5 - 1] get committed XA COMMIT 'trx_5'; XA COMMIT 'trx_6'; XA COMMIT 'trx_7'; XA COMMIT 'trx_8'; XA COMMIT 'trx_9'; # restart +# +# III. Post server-restart verification. +# It concludes survived XA:s with a number of commits and rollbacks +# as configured in the 1st part to check expected results in the end. +# Cleanup section consists of explicit disconnect (for killed, or +# not disconnected before shutdown). +# connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'new_trx_0'; INSERT INTO t SET a=0; @@ -374,6 +413,11 @@ XA COMMIT 'new_trx_6'; XA COMMIT 'new_trx_7'; XA COMMIT 'new_trx_8'; XA COMMIT 'new_trx_9'; +# +# Symmetrically to the pre-restart, the resurrected trx:s are committed +# [10, 10 + 5 - 1] +# and the rest is rolled back. +# XA START 'trx_10'; ERROR XAE08: XAER_DUPID: The XID already exists XA COMMIT 'trx_10'; @@ -404,6 +448,9 @@ XA ROLLBACK 'trx_18'; XA START 'trx_19'; ERROR XAE08: XAER_DUPID: The XID already exists XA ROLLBACK 'trx_19'; +# +# Verification of correct results of recovered XA transaction handling: +# SELECT * FROM t; a 100 @@ -473,6 +520,9 @@ connection default; XA ROLLBACK 'trx_11'; ERROR XAE04: XAER_NOTA: Unknown XID disconnect conn10; +# +# A. The temp table only prepared XA recovers only formally to +# let post recovery XA COMMIT or XA ROLLBACK with no effect. connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@sql_log_bin = OFF; CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb; @@ -480,6 +530,8 @@ XA START 'trx1tmp'; INSERT INTO tmp1 SET a=1; XA END 'trx1tmp'; XA PREPARE 'trx1tmp'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@sql_log_bin = OFF; CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb; @@ -487,6 +539,8 @@ XA START 'trx2tmp'; INSERT INTO tmp1 SET a=1; XA END 'trx2tmp'; XA PREPARE 'trx2tmp'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@sql_log_bin = OFF; CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb; @@ -494,6 +548,11 @@ XA START 'trx3tmp'; INSERT INTO tmp1 SET a=1; XA END 'trx3tmp'; XA PREPARE 'trx3tmp'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back +# +# Various prohibited XA state changes to test here: +# connection default; XA COMMIT 'trx1tmp'; ERROR XAE04: XAER_NOTA: Unknown XID @@ -502,18 +561,18 @@ ERROR XAE04: XAER_NOTA: Unknown XID XA START 'trx1tmp'; ERROR XAE08: XAER_DUPID: The XID already exists connection default; -*** 3 prepared transactions must be in the list *** +*** 0 transactions must be in the list *** XA RECOVER; formatID gtrid_length bqual_length data -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N connection conn1tmp; disconnect conn1tmp; connection default; XA COMMIT 'trx1tmp'; KILL connection CONN_ID; XA COMMIT 'trx3tmp'; +# +# B. "Read-only" (select) prepared XA recovers only formally to +# let post recovery XA COMMIT or XA ROLLBACK with no effect. connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx1ro'; SELECT * from t ORDER BY a; @@ -541,6 +600,8 @@ a 100 XA END 'trx1ro'; XA PREPARE 'trx1ro'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx2ro'; SELECT * from t ORDER BY a; @@ -568,6 +629,8 @@ a 100 XA END 'trx2ro'; XA PREPARE 'trx2ro'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx3ro'; SELECT * from t ORDER BY a; @@ -595,47 +658,55 @@ a 100 XA END 'trx3ro'; XA PREPARE 'trx3ro'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connection default; -*** 4 prepared transactions must be in the list *** +*** 0 transactions must be in the list *** XA RECOVER; formatID gtrid_length bqual_length data -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N connection conn1ro; disconnect conn1ro; connection default; XA ROLLBACK 'trx1ro'; KILL connection CONN_ID; XA ROLLBACK 'trx3ro'; +# +# C. Empty prepared XA recovers only formally to +# let post recovery XA COMMIT or XA ROLLBACK with no effect. +# connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx1empty'; XA END 'trx1empty'; XA PREPARE 'trx1empty'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx2empty'; XA END 'trx2empty'; XA PREPARE 'trx2empty'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx3empty'; XA END 'trx3empty'; XA PREPARE 'trx3empty'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back connection default; -*** 5 prepared transactions must be in the list *** +*** 0 transactions must be in the list *** XA RECOVER; formatID gtrid_length bqual_length data -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N -1 LEN1 LEN2 TRX_N connection conn1empty; disconnect conn1empty; connection default; XA COMMIT 'trx1empty'; KILL connection CONN_ID; XA COMMIT 'trx3empty'; +# +# D. Not prepared XA disconnects to be cleared out, +# no effect on data left as well. +# Few more prohibited XA state transactions is checked out. +# connect conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'trx1unprepared'; INSERT INTO t set a=0; @@ -650,6 +721,12 @@ disconnect conn1unprepared; connection default; XA COMMIT 'trx1unprepared'; ERROR XAE04: XAER_NOTA: Unknown XID +# +# II. Regular case. +# +# Prepared transactions get disconnected in three ways: +# actively, being killed and by the server shutdown. +# connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,; SET @@binlog_format = STATEMENT; XA START 'trx_0'; @@ -810,18 +887,27 @@ XA END 'trx_19'; XA PREPARE 'trx_19'; connection default; KILL CONNECTION CONN_ID; +# [0, 5 - 1] are rolled back now connection default; XA ROLLBACK 'trx_0'; XA ROLLBACK 'trx_1'; XA ROLLBACK 'trx_2'; XA ROLLBACK 'trx_3'; XA ROLLBACK 'trx_4'; +# [5, 5 + 5 - 1] get committed XA COMMIT 'trx_5'; XA COMMIT 'trx_6'; XA COMMIT 'trx_7'; XA COMMIT 'trx_8'; XA COMMIT 'trx_9'; # Kill and restart +# +# III. Post server-restart verification. +# It concludes survived XA:s with a number of commits and rollbacks +# as configured in the 1st part to check expected results in the end. +# Cleanup section consists of explicit disconnect (for killed, or +# not disconnected before shutdown). +# connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'new_trx_0'; INSERT INTO t SET a=0; @@ -903,6 +989,11 @@ XA COMMIT 'new_trx_6'; XA COMMIT 'new_trx_7'; XA COMMIT 'new_trx_8'; XA COMMIT 'new_trx_9'; +# +# Symmetrically to the pre-restart, the resurrected trx:s are committed +# [10, 10 + 5 - 1] +# and the rest is rolled back. +# XA START 'trx_10'; ERROR XAE08: XAER_DUPID: The XID already exists XA COMMIT 'trx_10'; @@ -933,6 +1024,9 @@ XA ROLLBACK 'trx_18'; XA START 'trx_19'; ERROR XAE08: XAER_DUPID: The XID already exists XA ROLLBACK 'trx_19'; +# +# Verification of correct results of recovered XA transaction handling: +# SELECT * FROM t; a 100 diff --git a/mysql-test/suite/binlog/r/mdev-32830_qa_tests.result b/mysql-test/suite/binlog/r/mdev-32830_qa_tests.result new file mode 100644 index 0000000000000..45231cf278577 --- /dev/null +++ b/mysql-test/suite/binlog/r/mdev-32830_qa_tests.result @@ -0,0 +1,37 @@ +set @@session.binlog_format=row; +CREATE TABLE t (c INT) ENGINE=InnoDB; +XA START 'a'; +SAVEPOINT s; +INSERT INTO t VALUES (1); +CREATE TEMPORARY TABLE t (c INT) ENGINE=InnoDB; +SELECT * FROM mysql.proc; +ROLLBACK WORK TO s; +XA END 'a'; +XA ROLLBACK 'a'; +# Expected binlog events: +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; CREATE TABLE t (c INT) ENGINE=InnoDB +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Annotate_rows # # INSERT INTO t VALUES (1) +master-bin.000001 # Table_map # # table_id: # (test.t) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # ROLLBACK +Cleanup +DROP TEMPORARY TABLE t; +DROP TABLE t; +set @@session.binlog_format=statement; +CREATE TABLE t1 (c CHAR(1),c2 CHAR(1)) ENGINE=MyISAM; +CREATE TEMPORARY TABLE t (a INT) ENGINE=InnoDB SELECT+1 a; +XA START 'a'; +INSERT t SELECT+1 seq_1_to_1; +SET pseudo_slave_mode=1; +INSERT INTO t1 (c) VALUES (1); +XA END 'a'; +XA PREPARE 'a'; +# cleanup +XA ROLLBACK 'a'; +DROP TABLE t; +DROP TABLE t1; +End of tests diff --git a/mysql-test/suite/binlog/r/mdev-36802_multiple_xa_engine.result b/mysql-test/suite/binlog/r/mdev-36802_multiple_xa_engine.result new file mode 100644 index 0000000000000..8d7f2338c7bf2 --- /dev/null +++ b/mysql-test/suite/binlog/r/mdev-36802_multiple_xa_engine.result @@ -0,0 +1,23 @@ +CREATE TABLE t (c INT) ENGINE=InnoDB; +SET pseudo_slave_mode=1; +XA START 'a'; +INSERT INTO t VALUES (1),(2),(3); +XA END 'a'; +XA PREPARE 'a'; +XA COMMIT 'a'; +ERROR HY000: This xid does not exist +DROP TABLE t; +SET max_session_mem_used=32768; +CREATE TABLE t (c INT) ENGINE=InnoDB; +XA START 'b'; +INSERT INTO t VALUES (); +ERROR HY000: The MariaDB server is running with the --max-session-mem-used=32768 option so it cannot execute this statement +INSERT INTO t VALUES (); +SET pseudo_slave_mode=1; +XA END 'b'; +XA PREPARE 'b'; +Warnings: +Warning 4205 Pseudo thread id should not be modified by the client as it will be overwritten +XA ROLLBACK 'b'; +# cleanup +DROP TABLE t; diff --git a/mysql-test/suite/binlog/t/binlog_empty_xa_prepared.test b/mysql-test/suite/binlog/t/binlog_empty_xa_prepared.test index a34b77195e7f8..e59784b51335d 100644 --- a/mysql-test/suite/binlog/t/binlog_empty_xa_prepared.test +++ b/mysql-test/suite/binlog/t/binlog_empty_xa_prepared.test @@ -142,31 +142,60 @@ XA PREPARE '1'; --source include/wait_until_count_sessions.inc XA RECOVER; ---error ER_XA_RBROLLBACK +# the read-only xa transaction completed by disconnect +--error ER_XAER_NOTA XA COMMIT '1'; + +--connect(con1_2,localhost,root,,) +# The same as above without diconnect +XA START '1'; + --error ER_DUP_ENTRY + INSERT INTO t1 VALUES (2),(1); + SELECT * FROM t1 WHERE a = 2; +XA END '1'; +XA PREPARE '1'; +# the read-only xa transaction completes seemlessly in the native connect +--error 0 +XA COMMIT '1'; +--disconnect con1_2 + +--connection default --echo Must be no XA PREPARE group nor XA completion one: --source include/show_binlog_events.inc DROP TABLE t1; ---source include/count_sessions.inc ---connect(con2,localhost,root,,) CREATE TABLE tm (a INT PRIMARY KEY) ENGINE=MyISAM; +--source include/count_sessions.inc +--connect(con2,localhost,root,,) XA START '1'; --error ER_DUP_ENTRY INSERT INTO tm VALUES (1),(1); SELECT * FROM tm WHERE a = 2; XA END '1'; XA PREPARE '1'; - --disconnect con2 --connection default --source include/wait_until_count_sessions.inc XA RECOVER; ---error ER_XA_RBROLLBACK +# ditto to the commit +--error ER_XAER_NOTA +XA ROLLBACK '1'; + +--connect(con2_2,localhost,root,,) +XA START '1'; + --error ER_DUP_ENTRY + INSERT INTO tm VALUES (1),(1); + SELECT * FROM tm WHERE a = 2; +XA END '1'; +XA PREPARE '1'; +--error 0 XA ROLLBACK '1'; +--disconnect con2_2 + +--connection default --echo Must be no XA PREPARE group nor XA completion one: --source include/show_binlog_events.inc DROP TABLE tm; @@ -180,6 +209,21 @@ SET pseudo_slave_mode=1; XA START 'a'; XA END 'a'; XA PREPARE 'a'; ---error ER_XA_RBROLLBACK +--error ER_XAER_NOTA XA ROLLBACK 'a'; + +--source include/show_binlog_events.inc + +# MDEV-31949/MDEV-32830/MDEV-32852 +# Prove exceptional empty transaction binlogging in the following case: +--connect(con_32852,localhost,root,,) + +--let $binlog_start = query_get_value(SHOW MASTER STATUS, Position, 1) +--let $binlog_file = query_get_value(SHOW MASTER STATUS, File, 1) +XA START 'a'; +CREATE TEMPORARY TABLE t1 (a INT) ENGINE=InnoDB; +SELECT * FROM t1; +--disconnect con_32852 + +--connection default --source include/show_binlog_events.inc diff --git a/mysql-test/suite/binlog/t/binlog_truncate_active_log.inc b/mysql-test/suite/binlog/t/binlog_truncate_active_log.inc index 050e4ad8665aa..890628a30d818 100644 --- a/mysql-test/suite/binlog/t/binlog_truncate_active_log.inc +++ b/mysql-test/suite/binlog/t/binlog_truncate_active_log.inc @@ -67,4 +67,8 @@ SELECT * FROM t4; --inc $binlog_file_index # Local cleanup +if (`select '$query1' = 'CALL sp_xa'`) +{ + xa rollback 'xid'; +} DELETE FROM t; diff --git a/mysql-test/suite/binlog/t/binlog_truncate_active_log.test b/mysql-test/suite/binlog/t/binlog_truncate_active_log.test index e288aa476600f..00c1e13a4978c 100644 --- a/mysql-test/suite/binlog/t/binlog_truncate_active_log.test +++ b/mysql-test/suite/binlog/t/binlog_truncate_active_log.test @@ -15,6 +15,7 @@ call mtr.add_suppression("Can.t init tc log"); call mtr.add_suppression("Aborting"); +call mtr.add_suppression("Found 1 prepared XA transactions"); # The following cases are tested: # A. 2pc transaction is followed by a blank "zero-engines" one @@ -55,30 +56,8 @@ CREATE TABLE tm (f INT) ENGINE=Aria; --let $query2 = INSERT INTO t VALUES (20) --source binlog_truncate_active_log.inc - --echo # Case C. delimiter |; -CREATE PROCEDURE sp_blank_xa() -BEGIN - XA START 'blank'; - DELETE FROM t2 WHERE f = 0 /* no such record */; - XA END 'blank'; - XA PREPARE 'blank'; -END| -delimiter ;| - -# The same as in A with $query2 being the zero-engine XA transaction. -# Both $query1 and $query2 are going to be truncated. ---let $truncate_gtid_pos = 0-1-15 ---let $query1 = INSERT INTO t VALUES (20) ---let $query2 = CALL sp_blank_xa ---source binlog_truncate_active_log.inc - -DROP PROCEDURE sp_blank_xa; - - ---echo # Case D. -delimiter |; CREATE PROCEDURE sp_xa() BEGIN XA START 'xid'; @@ -90,7 +69,7 @@ delimiter ;| # The same as in B with $query1 being the prepared XA transaction. # Truncation must occurs at $query2. ---let $truncate_gtid_pos = 0-1-21 +--let $truncate_gtid_pos = 0-1-16 --let $query1 = CALL sp_xa --let $query2 = INSERT INTO t2 VALUES (20) --source binlog_truncate_active_log.inc diff --git a/mysql-test/suite/binlog/t/binlog_xa_checkpoint.test b/mysql-test/suite/binlog/t/binlog_xa_checkpoint.test index b208d02cf2a53..c57360d1b7cc1 100644 --- a/mysql-test/suite/binlog/t/binlog_xa_checkpoint.test +++ b/mysql-test/suite/binlog/t/binlog_xa_checkpoint.test @@ -1,57 +1,146 @@ +# +# MDEV-32830/MDEV-31949 +# The test proves Binlog-Checkpoint events are written upon binlog rotation +# caused by XA-PREPARE (the case A) or XA-COMMIT (the case B). +# --source include/have_innodb.inc ---source include/have_debug.inc ---source include/have_debug_sync.inc --source include/have_binlog_format_row.inc RESET MASTER; CREATE TABLE t1 (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=Innodb; -# Test that -# 1. XA PREPARE is binlogged before the XA has been prepared in Engine -# 2. While XA PREPARE already binlogged in an old binlog file which has been rotated, -# Binlog checkpoint is not generated for the latest log until -# XA PREPARE returns, e.g OK to the client. +--echo # case A: Binlog CP after XAP +# Test that XA PREPARE is binlogged to cause binlog rotation (Proof 1) +# followed by Binlog checkpoint event (Proof 2) for the old log file, +# to comply with the normal transaction commit pattern. +# When the server crashes after that the prepared XA will be found +# at restart in the engine as well. - -# con1 will hang before doing commit checkpoint, blocking RESET MASTER. +--let $save_max_binlog_size= `SELECT @@global.max_binlog_size` +set @@global.max_binlog_size=4096; +--let $pk_ins= 1 +--let $binlog_0= query_get_value(SHOW MASTER STATUS, File, 1) +--let $pre_xa_pos= query_get_value(SHOW MASTER STATUS, Position, 1) connect(con1,localhost,root,,); -SET DEBUG_SYNC= "at_unlog_xa_prepare SIGNAL con1_ready WAIT_FOR con1_go"; +SET SESSION binlog_annotate_row_events=OFF; XA START '1'; -INSERT INTO t1 SET a=1; +--eval INSERT INTO t1 SET a=1,b=repeat('b',@@global.max_binlog_size); XA END '1'; ---send XA PREPARE '1'; +XA PREPARE '1'; +--echo ### Repeatable check block in the cases A,B. +# +# Proof 1: +# connection default; -SET DEBUG_SYNC= "now WAIT_FOR con1_ready"; -FLUSH LOGS; -FLUSH LOGS; -FLUSH LOGS; +# the Rotate event signature in the SBE Info field +--let $wait_binlog_event= ;pos=4 +--let $wait_binlog_file=$binlog_0 +--source include/wait_for_binlog_event.inc +# +# Proof 2: +# +# looping is very unlikely but treat it conservatively that is safe +--let $_max_wait_count= 300 +let $binlog_1= $binlog_0; +while (`select STRCMP("$binlog_0", "$binlog_1") = 0`) +{ + --dec $_max_wait_count + if (!$_max_wait_count) + { + --die Got $binlog_1 SHOW MASTER STATUS which is the old $binlog_0 + } + --real_sleep 0.1 + --let $binlog_1= query_get_value(SHOW MASTER STATUS, File, 1) +} +# new binlog reigns, prove/wait for its predessor's irrelevance for recovery +--source include/wait_for_binlog_checkpoint.inc +# .. so the server can crash-restart with the prepared XA: +# list of the logs +# events in the pre-ultimate and the current one follow. --source include/show_binary_logs.inc ---let $binlog_file= master-bin.000004 +--echo *** the old $binlog_file must show the prepared xa +--let $binlog_file= $binlog_0 +--let $binlog_start= $pre_xa_pos +--source include/show_binlog_events.inc +--echo *** the current $binlog_file must show the checkpoint event with its name *** --let $binlog_start= 4 +--let $binlog_file= $binlog_1 --source include/show_binlog_events.inc +--echo ### end of check block -SET DEBUG_SYNC= "now SIGNAL con1_go"; +--echo # case B: Binlog CP after XAC +# Similar proof to XA COMMIT. +# *estimate* the table map size and the row event header, +# fill the rest of the following Rows_log_event with data, approaching +# the end-of-binlog offset to its max, not overstepping though (!) (see -19). +# This test is (practially) future-prove if when the estimate will turn out +# too low later. +--let $curr_pos= query_get_value(SHOW MASTER STATUS, Position, 1) +--let $gtid_size=40 +--let $tm_size= 60 +--let $rows_hdr= 10 +--let $xid_size= 50 +--let $insert_length= `select @@max_binlog_size - ($curr_pos + $gtid_size + $tm_size + $rows_hdr + 0 + $xid_size) - 19` +connection default; +--inc $pk_ins +--eval SET STATEMENT binlog_annotate_row_events=OFF FOR INSERT INTO t1 SET a=$pk_ins,b=repeat('b', $insert_length) +--let $pre_xa_commit_file= query_get_value(SHOW MASTER STATUS, File, 1) +if (`select strcmp("$binlog_1", "$pre_xa_commit_file")`) +{ + --die unxpected (rotated?) $pre_xa_commit_file end-of-binlog while the correct must be $binlog_1 +} +--let $pre_xa_pos= query_get_value(SHOW MASTER STATUS, Position, 1) connection con1; -reap; ---echo *** master-bin.000004 checkpoint must show up now *** ---source include/wait_for_binlog_checkpoint.inc +XA COMMIT '1'; -# Todo: think about the error code returned, move to an appropriate test, or remove -# connection default; -#--error 1399 -# DROP TABLE t1; +--echo ### Repeatable check block in the cases A,B. +# +# Proof 1: +# +connection default; +--let $wait_binlog_event= ;pos=4 +--let $wait_binlog_file= $binlog_1 +--source include/wait_for_binlog_event.inc -connection con1; -XA ROLLBACK '1'; -SET debug_sync = 'reset'; +# +# Proof 2 (ditto looping) +# +--let $_max_wait_count= 300 +--let $binlog_2= $binlog_1 +while (`select STRCMP("$binlog_1", "$binlog_2") = 0`) +{ + --dec $_max_wait_count + if (!$_max_wait_count) + { + --die Got $binlog_2 SHOW MASTER STATUS which is the old $binlog_1 + } + --real_sleep 0.1 + --let $binlog_2= query_get_value(SHOW MASTER STATUS, File, 1) +} +# another new binlog reigns, prove/wait for its predessor's irrelevance for recovery.. +--source include/wait_for_binlog_checkpoint.inc + +# .. so the server can crash-restart with the committed XA +# events in the pre-ultimate log. Display that. +--source include/show_binary_logs.inc +--echo *** the old $binlog_file must show the committed xa +--let $binlog_file= $binlog_1 +--let $binlog_start= $pre_xa_pos +--source include/show_binlog_events.inc +--echo *** the current $binlog_file must show the checkpoint event with its name *** +--let $binlog_start= 4 +--let $binlog_file= $binlog_2 +--source include/show_binlog_events.inc +--echo ### end of check block # Clean up. +--disconnect con1 connection default; DROP TABLE t1; -SET debug_sync = 'reset'; +--eval set @@global.max_binlog_size=$save_max_binlog_size diff --git a/mysql-test/suite/binlog/t/binlog_xa_prepared_bugs.test b/mysql-test/suite/binlog/t/binlog_xa_prepared_bugs.test new file mode 100644 index 0000000000000..5d49e6ed77b2c --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_xa_prepared_bugs.test @@ -0,0 +1,36 @@ +--source include/have_binlog_format_row.inc +--source include/have_innodb.inc + +RESET MASTER; +--let $binlog_start = query_get_value(SHOW MASTER STATUS, Position, 1) +--let $binlog_file = query_get_value(SHOW MASTER STATUS, File, 1) + +CREATE TABLE ta (c INT KEY) engine=Aria; +XA START 'xid_a'; +INSERT INTO ta VALUES (1); +XA END 'xid_a'; +XA PREPARE 'xid_a'; + +# sort out the correct code by MDEV-32455 fixes +--error 0,ER_XAER_RMFAIL +LOAD INDEX INTO CACHE c KEY(PRIMARY); +--error 0 +XA ROLLBACK 'xid_a'; + +CREATE TABLE ti (c INT KEY) engine=Innodb; +XA START 'xid_i'; +INSERT INTO ti VALUES (1); +XA END 'xid_i'; +XA PREPARE 'xid_i'; + +--error 0,ER_XAER_RMFAIL +LOAD INDEX INTO CACHE c KEY(PRIMARY); + +--error 0 +XA COMMIT 'xid_i'; +SELECT * FROM ti; + +# +--source include/show_binlog_events.inc + +drop table ta,ti; diff --git a/mysql-test/suite/binlog/t/mdev-32830_qa_tests.test b/mysql-test/suite/binlog/t/mdev-32830_qa_tests.test new file mode 100644 index 0000000000000..aaf19f88c97bc --- /dev/null +++ b/mysql-test/suite/binlog/t/mdev-32830_qa_tests.test @@ -0,0 +1,47 @@ +--source include/have_log_bin.inc +--source include/have_innodb.inc + +# MDEV-36804 +set @@session.binlog_format=row; + +CREATE TABLE t (c INT) ENGINE=InnoDB; +XA START 'a'; +SAVEPOINT s; +INSERT INTO t VALUES (1); +CREATE TEMPORARY TABLE t (c INT) ENGINE=InnoDB; +--disable_result_log +SELECT * FROM mysql.proc; +--enable_query_log +ROLLBACK WORK TO s; + +XA END 'a'; +XA ROLLBACK 'a'; + +--echo # Expected binlog events: +source include/show_binlog_events.inc; + +--echo Cleanup +DROP TEMPORARY TABLE t; +DROP TABLE t; + +# MDEV-36800 +set @@session.binlog_format=statement; + +CREATE TABLE t1 (c CHAR(1),c2 CHAR(1)) ENGINE=MyISAM; +CREATE TEMPORARY TABLE t (a INT) ENGINE=InnoDB SELECT+1 a; +XA START 'a'; +INSERT t SELECT+1 seq_1_to_1; +SET pseudo_slave_mode=1; +INSERT INTO t1 (c) VALUES (1); +XA END 'a'; +XA PREPARE 'a'; + +--echo # cleanup +--error ER_XAER_NOTA +XA ROLLBACK 'a'; +--error ER_BAD_TABLE_ERROR +DROP TABLE t; +DROP TABLE t1; + + +--echo End of tests diff --git a/mysql-test/suite/binlog/t/mdev-36802_multiple_xa_engine.test b/mysql-test/suite/binlog/t/mdev-36802_multiple_xa_engine.test new file mode 100644 index 0000000000000..f9f7f7f61d8fe --- /dev/null +++ b/mysql-test/suite/binlog/t/mdev-36802_multiple_xa_engine.test @@ -0,0 +1,35 @@ +--source include/have_binlog_format_statement.inc +--source include/have_innodb.inc +--source include/have_spider.inc + +CREATE TABLE t (c INT) ENGINE=InnoDB; + +SET pseudo_slave_mode=1; +XA START 'a'; +INSERT INTO t VALUES (1),(2),(3); +XA END 'a'; +XA PREPARE 'a'; +--error 12607 +XA COMMIT 'a'; + +DROP TABLE t; + +# MDEV-36825 +# The mtr test passes anyway never noticing the assert. +# Its core fails as in the description with an explict +# INSTALL PLUGIN Spider SONAME 'ha_spider.so'; +# instead of source include/have_spider.inc. +SET max_session_mem_used=32768; +CREATE TABLE t (c INT) ENGINE=InnoDB; + +XA START 'b'; +--error ER_OPTION_PREVENTS_STATEMENT +INSERT INTO t VALUES (); +INSERT INTO t VALUES (); +SET pseudo_slave_mode=1; +XA END 'b'; +XA PREPARE 'b'; +XA ROLLBACK 'b'; + +--echo # cleanup +DROP TABLE t; diff --git a/mysql-test/suite/rpl/include/rpl_xa_concurrent_2pc.inc b/mysql-test/suite/rpl/include/rpl_xa_concurrent_2pc.inc new file mode 100644 index 0000000000000..5a4126a196dec --- /dev/null +++ b/mysql-test/suite/rpl/include/rpl_xa_concurrent_2pc.inc @@ -0,0 +1,341 @@ +# +# Helper file to run the 1-4(a,b) test cases for rpl_xa_concurrent_2pc, +# with either XA COMMIT or XA ROLLBACK used to complete XA transactions. +# +# Parameters +# $xa_complete_sym (string) : COMMIT or ROLLBACK, the action used to complete +# a prepared XA transaction +# + +if (!$xa_complete_sym) +{ + die MTR variable xa_complete_sym not specified, must be either COMMIT or ROLLBACK; +} + +--let $is_xac= 0 +--let $is_xar= 0 + +if (`SELECT strcmp("COMMIT", "$xa_complete_sym") = 0`) +{ + --let $is_xac= 1 +} + +if (`SELECT strcmp("ROLLBACK", "$xa_complete_sym") = 0`) +{ + --let $is_xar= 1 +} + +if (`SELECT !$is_xar && !$is_xac`) +{ + die MTR variable xa_complete_sym invalid, must be either COMMIT or ROLLBACK; +} + + +--echo # +--echo # Initialize test data +--connection slave +--source include/stop_slave.inc + +--connection master +create table t1 (a int primary key, b int) engine=innodb; + +# Slave locks this row before updates to pause transaction progress +--let $hold_row= -1 +--let $t1_ctr= 0 +--eval insert into t1 values ($hold_row, 0) +--source include/save_master_gtid.inc + +--connection slave +--source include/start_slave.inc +--source include/sync_with_master_gtid.inc +--source include/stop_slave.inc +set @save_debug= @@GLOBAL.debug_dbug; +set @save_par_thds= @@GLOBAL.slave_parallel_threads; +set @save_par_mode= @@GLOBAL.slave_parallel_mode; +set @@GLOBAL.slave_parallel_threads= 4; +set @@GLOBAL.slave_parallel_mode= optimistic; + +set statement sql_log_bin=0 for call mtr.add_suppression("Commit failed due to failure of an earlier commit on which this one depends"); + +--echo # +--echo # Test Case 1-$xa_complete_sym: +--echo # If two XA $xa_complete_sym transactions have different +--echo # XIDs, ensure both phases of both transactions all execute concurrently. +--echo # + +--connection slave +# Stop both XAP after their binlogging and before their engine changing +set @@global.debug_dbug= "+d,stop_before_binlog_prepare,stop_after_binlog_prepare"; + +--connection master +--eval set @@session.gtid_seq_no= 200 + ($is_xar * 1000) +XA START 'x1'; +--eval insert into t1 values ($t1_ctr, 0) +--inc $t1_ctr +XA END 'x1'; +XA PREPARE 'x1'; +--eval XA $xa_complete_sym 'x1' + +XA START 'x2'; +--eval insert into t1 values ($t1_ctr, 0) +--inc $t1_ctr +XA END 'x2'; +XA PREPARE 'x2'; +--eval XA $xa_complete_sym 'x2' + +--connection slave +--source include/start_slave.inc + +# Prove $count_wait number of the workers in W4PT2C. +# It's just 1 with MDEV-33668 that schedules XAC(xid) to a XAP(xid) worker +--let $count_wait= 1 +--let $wait_condition=SELECT count(*) = $count_wait FROM information_schema.processlist WHERE command = 'Slave_worker' AND state LIKE "Waiting for prior transaction to commit" +--source include/wait_condition.inc +# .. while the seq_no 200 XAP is dsync-delayed +--let $count_wait= 1 +--let $wait_condition=SELECT count(*) = $count_wait FROM information_schema.processlist WHERE command = 'Slave_worker' AND state LIKE "debug sync point: now" +--source include/wait_condition.inc +--echo must be \Empty +XA RECOVER; +set debug_sync= "now signal binlog_xap"; + +--echo # Make a stop at a point where XA PREPARE (both) have completed (binlogged): +set debug_sync= "now wait_for xa_prepare_binlogged"; + +--echo # prove XA-$xa_complete_sym 'x1' has completed too +--let $xid = query_get_value("XA RECOVER", data, 1) +--eval SELECT '$xid' = 'x1' + +--echo # cleanup: release the seq_no 200 XAP +--let $wait_condition=SELECT count(*) = 1 FROM information_schema.processlist WHERE command = 'Slave_worker' AND state LIKE "debug sync point: now" +--source include/wait_condition.inc +set debug_sync= "now signal continue_xap"; + +--echo # Proof of the full master-slave sync +--source include/sync_with_master_gtid.inc + +--let $diff_tables=master:test.t1, slave:test.t1 +--source include/diff_tables.inc + +--connection slave +--source include/stop_slave.inc +set @@global.debug_dbug= @save_debug; + + +--echo # +--echo # Test Case 2-$xa_complete_sym: +--echo # Two concurrent 2-phase XA transactions with matching XIDs +--echo # should run one after the other. +--echo # +--connection slave +set @@global.debug_dbug= "+d,stop_before_binlog_prepare,stop_after_binlog_prepare"; + +--connection master +--eval set @@session.gtid_seq_no= 300 + ($is_xar * 1000) +XA START 'x'; +--eval insert into t1 values ($t1_ctr, 1) +--inc $t1_ctr +XA END 'x'; +XA PREPARE 'x'; +--eval XA $xa_complete_sym 'x' + +XA START 'x'; +--eval insert into t1 values ($t1_ctr, 2) +--inc $t1_ctr +XA END 'x'; +XA PREPARE 'x'; +--eval XA $xa_complete_sym 'x' +--source include/save_master_gtid.inc + +--connection slave +# remember the slave's initial binlog offset +--let $binlog_pos=query_get_value(SHOW MASTER STATUS, Position, 1) +--let $binlog_file=query_get_value(SHOW MASTER STATUS, File, 1) +--source include/start_slave.inc + +--echo # states of workers at the first XAP near binlogging are expected to be as the following +--let $count_wait= 3 +--let $wait_condition=SELECT count(*) = $count_wait FROM information_schema.processlist WHERE command = 'Slave_worker' AND state LIKE "Waiting for prior transaction to commit" +set debug_sync= "now signal binlog_xap"; + +--echo # Once the first XA PREPARE has binlogged.. +set debug_sync= "now wait_for xa_prepare_binlogged"; + +# with MDEV-33668 the first of the two XAP:s is done now +--let $xid = query_get_value("XA RECOVER", data, 1) +--eval SELECT '$xid' = 'x' + +--echo # cleanup: signal XA PREPARE to leave the stage +set debug_sync= "now signal continue_xap"; + +--echo # Proof of the full master-slave sync +--source include/sync_with_master_gtid.inc +--let $diff_tables=master:test.t1, slave:test.t1 +--source include/diff_tables.inc + +--connection slave +--source include/stop_slave.inc +set @@global.debug_dbug= @save_debug; + + +--echo # +--echo # Test Case 3-$xa_complete_sym (Error Case): +--echo # If an XA PREPARE errors while its +--echo # XA $xa_complete_sym is waiting on it, both phases should rollback +--echo # successfully. Note this tests both: +--echo # a) XA $xa_complete_sym is waiting in group commit (first phase +--echo # times out in DMLs) +--echo # b) XA $xa_complete_sym is waiting in group commit, with another XAP +--echo # with a duplicate XID waiting on it. + +--echo # Case a) +--echo # Ensure slave is stopped +--connection slave +--source include/wait_for_slave_to_stop.inc +set @save_lock_wait_timeout= @@GLOBAL.innodb_lock_wait_timeout; +set @save_trans_retries= @@GLOBAL.slave_transaction_retries; +set @@global.innodb_lock_wait_timeout= 1; +set @@global.slave_transaction_retries= 0; + +--connection master +XA START 'x'; +--eval update t1 set b=b+1 where a=$hold_row +XA END 'x'; +XA PREPARE 'x'; +--eval XA $xa_complete_sym 'x' +--source include/save_master_gtid.inc + +--connection slave1 +BEGIN; +--eval select * from t1 where a=$hold_row for update; + +--connection slave +--source include/start_slave.inc + +--let $slave_sql_errno= 1205 +--source include/wait_for_slave_sql_error.inc + +--connection slave1 +ROLLBACK; + +--connection slave +# Stop the IO thread too +--source include/stop_slave_io.inc +set @@global.innodb_lock_wait_timeout= @save_lock_wait_timeout; +set @@global.slave_transaction_retries= @save_trans_retries; + +select @@global.gtid_slave_pos; + +--echo # Ensure on slave restart, we can re-execute the XA transaction +--source include/start_slave.inc +--source include/sync_with_master_gtid.inc +let $diff_tables= master:t1, slave:t1; +--source include/diff_tables.inc +select * from t1; + +--source include/stop_slave.inc +set @@global.debug_dbug= @save_debug; + + +--echo # Case b) +--echo # Ensure slave is stopped +--connection slave +--source include/wait_for_slave_to_stop.inc +set @save_lock_wait_timeout= @@GLOBAL.innodb_lock_wait_timeout; +set @save_trans_retries= @@GLOBAL.slave_transaction_retries; +set @@global.innodb_lock_wait_timeout= 1; +set @@global.slave_transaction_retries= 0; + +--connection master +XA START 'x'; +--eval update t1 set b=b+1 where a=$hold_row +--eval select * from t1 where a=$hold_row +XA END 'x'; +XA PREPARE 'x'; +--eval XA $xa_complete_sym 'x' + +XA START 'x'; +--eval insert into t1 values ($t1_ctr, 0) +--let $new_row_idx= $t1_ctr +--inc $t1_ctr +XA END 'x'; +XA PREPARE 'x'; +--source include/save_master_gtid.inc +--eval XA $xa_complete_sym 'x' + +--connection slave1 +BEGIN; +--eval select * from t1 where a=$hold_row for update; + +--connection slave +--source include/start_slave.inc + +--let $slave_sql_errno= 1205 +--source include/wait_for_slave_sql_error.inc + +--connection slave1 +ROLLBACK; + +--echo # There should not be any prepared rows seen by XA RECOVER +XA RECOVER; + +--echo # Ensuring data from second XAP *isn't* visible.. +if (`select count(*) from t1 where a=$new_row_idx`) +{ + --die Failed, row exists +} +--echo # ..done + +--connection slave +--source include/stop_slave_io.inc +set @@global.innodb_lock_wait_timeout= @save_lock_wait_timeout; +set @@global.slave_transaction_retries= @save_trans_retries; + +--echo # Ensure on slave restart, we can re-execute the XA transaction +--source include/start_slave.inc +--source include/sync_with_master_gtid.inc +let $diff_tables= master:t1, slave:t1; +--source include/diff_tables.inc +select * from t1; + +--echo # Ensuring data from second XAP *is* visible.. +if ($is_xac) +{ + --let $expected_row_count= 1 +} +if ($is_xar) +{ + --let $expected_row_count= 0 +} +if (`select count(*) != $expected_row_count from t1 where a=$new_row_idx`) +{ + --die Failed, XA $xa_complete_sym was not observed +} +--echo # ..done + +# +--echo # Cleanup +# + + +--connection slave +set debug_sync='RESET'; +set @@global.debug_dbug= @save_debug; +--source include/start_slave.inc + +--connection master +DROP TABLE t1; +--source include/save_master_gtid.inc +--let $binlog_file=query_get_value(SHOW MASTER STATUS, File, 1) +--source include/show_binlog_events.inc + +--connection slave +--source include/sync_with_master_gtid.inc +--let $binlog_file=query_get_value(SHOW MASTER STATUS, File, 1) +--let $filter_cid=1 +--source include/show_binlog_events2.inc + +--source include/stop_slave.inc +set @@GLOBAL.slave_parallel_threads= @save_par_thds; +set @@GLOBAL.slave_parallel_mode= @save_par_mode; +--source include/start_slave.inc diff --git a/mysql-test/suite/rpl/include/rpl_xa_empty_transaction_test_case.inc b/mysql-test/suite/rpl/include/rpl_xa_empty_transaction_test_case.inc index 6368336b8e36c..b718bbdaf345d 100644 --- a/mysql-test/suite/rpl/include/rpl_xa_empty_transaction_test_case.inc +++ b/mysql-test/suite/rpl/include/rpl_xa_empty_transaction_test_case.inc @@ -78,7 +78,7 @@ if ($use_disconnect) --source include/wait_until_count_sessions.inc XA RECOVER; - --error ER_XA_RBROLLBACK + --error ER_XAER_NOTA --eval XA $xa_completion_action 'x'; } if (!$use_disconnect) diff --git a/mysql-test/suite/rpl/include/rpl_xa_mixed_engines.inc b/mysql-test/suite/rpl/include/rpl_xa_mixed_engines.inc index 0707a04090acf..aae9324818d12 100644 --- a/mysql-test/suite/rpl/include/rpl_xa_mixed_engines.inc +++ b/mysql-test/suite/rpl/include/rpl_xa_mixed_engines.inc @@ -169,6 +169,7 @@ if ($command == run) --eval XA START '$xid' UPDATE t SET a = 99 where a = -1; + SELECT ROW_COUNT(); --eval XA END '$xid' if ($xa_prepare_opt) { diff --git a/mysql-test/suite/rpl/r/rpl_xa.result b/mysql-test/suite/rpl/r/rpl_xa.result index 5ed7ab5e27957..5b8355cd3b974 100644 --- a/mysql-test/suite/rpl/r/rpl_xa.result +++ b/mysql-test/suite/rpl/r/rpl_xa.result @@ -59,6 +59,8 @@ xa start 'ro_2'; select count(*) into @s2 from t1; xa end 'ro_2'; xa prepare 'ro_2';; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master_ro_2; connection master; connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; @@ -68,6 +70,8 @@ select 1; 1 xa end 'ro_1'; xa prepare 'ro_1';; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master_ro_1; connection master; connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; @@ -75,6 +79,8 @@ xa start 'ro_2'; select count(*) into @s2 from t1; xa end 'ro_2'; xa prepare 'ro_2';; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master_ro_2; connection master; connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; @@ -84,17 +90,17 @@ select 1; 1 xa end 'ro_1'; xa prepare 'ro_1';; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master_ro_1; -*** 2 prepared xa:s must be in the list: +*** Zero prepared xa:s must be in the list: connection master; xa recover; formatID gtrid_length bqual_length data -1 4 0 ro_1 -1 4 0 ro_2 *** Zero prepared xa:s must be in the list: xa recover; formatID gtrid_length bqual_length data -*** At the end of read-only section gtid list has 0 more compare with previous check: +*** At the end of the above read-only section gtid list must not change from the previous check: flush logs; show binlog events in 'master-bin.000003' limit 1,1; Log_name Pos Event_type Server_id End_log_pos Info @@ -114,15 +120,18 @@ connection master; xa recover; formatID gtrid_length bqual_length data 1 12 0 rw_no_binlog +set @@session.sql_log_bin = 0; +xa $complete '$xid'; +set @@session.sql_log_bin = 1; *** Zero must be in the list: connection master; xa recover; formatID gtrid_length bqual_length data -*** At the end of --binlog-ignore-db section gtid list has 2 more: +*** At the end of --binlog-ignore-db section gtid list remains the same. flush logs; show binlog events in 'master-bin.000004' limit 1,1; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000004 # Gtid_list 1 # [0-1-13] +master-bin.000004 # Gtid_list 1 # [0-1-11] connection master; create table t3 (a int) engine=innodb; *** the disconnected prepare case @@ -162,7 +171,7 @@ formatID gtrid_length bqual_length data flush logs; show binlog events in 'master-bin.000005' limit 1,1; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000005 # Gtid_list 1 # [0-1-18] +master-bin.000005 # Gtid_list 1 # [0-1-16] create table tm (a int) engine=myisam; connection master; connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; @@ -170,6 +179,8 @@ xa start 'rw_myisam'; insert into tm set a=1; xa end 'rw_myisam'; xa prepare 'rw_myisam';; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master_rw_myisam; connection master; connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; @@ -177,20 +188,30 @@ xa start 'rw_myisam'; insert into tm set a=1; xa end 'rw_myisam'; xa prepare 'rw_myisam';; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master_rw_myisam; *** rw_myisam prepared must be in the list: connection master; xa recover; formatID gtrid_length bqual_length data -1 9 0 rw_myisam *** Zero prepared xa:s must be in the list: xa recover; formatID gtrid_length bqual_length data -*** At the end of MyISAM "xa" section gtid list has 7 more compare with previous check: +*** At the end of MyISAM "xa" section gtid list has 3 more compare with previous check: flush logs; show binlog events in 'master-bin.000006' limit 1,1; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000006 # Gtid_list 1 # [0-1-25] +master-bin.000006 # Gtid_list 1 # [0-1-19] +set @@session.sql_log_bin = OFF; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'skip_binlog'; +insert into t2 values(1); +xa end 'skip_binlog'; +xa prepare 'skip_binlog';; +xa $complete '$xid'; +set @@session.sql_log_bin = ON; +disconnect master_skip_binlog; connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; set @@session.sql_log_bin = OFF; xa start 'skip_binlog'; @@ -215,7 +236,7 @@ formatID gtrid_length bqual_length data flush logs; show binlog events in 'master-bin.000007' limit 1,1; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000007 # Gtid_list 1 # [0-1-25] +master-bin.000007 # Gtid_list 1 # [0-1-21] connection slave; include/sync_with_master_gtid.inc connection master; diff --git a/mysql-test/suite/rpl/r/rpl_xa_concurrent_2pc.result b/mysql-test/suite/rpl/r/rpl_xa_concurrent_2pc.result new file mode 100644 index 0000000000000..a9b017edf49d9 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_xa_concurrent_2pc.result @@ -0,0 +1,811 @@ +include/master-slave.inc +[connection master] +# +# Initialize test data +connection slave; +include/stop_slave.inc +connection master; +create table t1 (a int primary key, b int) engine=innodb; +insert into t1 values (-1, 0); +include/save_master_gtid.inc +connection slave; +include/start_slave.inc +include/sync_with_master_gtid.inc +include/stop_slave.inc +set @save_debug= @@GLOBAL.debug_dbug; +set @save_par_thds= @@GLOBAL.slave_parallel_threads; +set @save_par_mode= @@GLOBAL.slave_parallel_mode; +set @@GLOBAL.slave_parallel_threads= 4; +set @@GLOBAL.slave_parallel_mode= optimistic; +set statement sql_log_bin=0 for call mtr.add_suppression("Commit failed due to failure of an earlier commit on which this one depends"); +# +# Test Case 1-COMMIT: +# If two XA COMMIT transactions have different +# XIDs, ensure both phases of both transactions all execute concurrently. +# +connection slave; +set @@global.debug_dbug= "+d,stop_before_binlog_prepare,stop_after_binlog_prepare"; +connection master; +set @@session.gtid_seq_no= 200 + (0 * 1000); +XA START 'x1'; +insert into t1 values (0, 0); +XA END 'x1'; +XA PREPARE 'x1'; +XA COMMIT 'x1'; +XA START 'x2'; +insert into t1 values (1, 0); +XA END 'x2'; +XA PREPARE 'x2'; +XA COMMIT 'x2'; +connection slave; +include/start_slave.inc +must be \Empty +XA RECOVER; +formatID gtrid_length bqual_length data +set debug_sync= "now signal binlog_xap"; +# Make a stop at a point where XA PREPARE (both) have completed (binlogged): +set debug_sync= "now wait_for xa_prepare_binlogged"; +# prove XA-COMMIT 'x1' has completed too +SELECT 'x1' = 'x1'; +'x1' = 'x1' +1 +# cleanup: release the seq_no 200 XAP +set debug_sync= "now signal continue_xap"; +# Proof of the full master-slave sync +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:test.t1, slave:test.t1] +connection slave; +include/stop_slave.inc +set @@global.debug_dbug= @save_debug; +# +# Test Case 2-COMMIT: +# Two concurrent 2-phase XA transactions with matching XIDs +# should run one after the other. +# +connection slave; +set @@global.debug_dbug= "+d,stop_before_binlog_prepare,stop_after_binlog_prepare"; +connection master; +set @@session.gtid_seq_no= 300 + (0 * 1000); +XA START 'x'; +insert into t1 values (2, 1); +XA END 'x'; +XA PREPARE 'x'; +XA COMMIT 'x'; +XA START 'x'; +insert into t1 values (3, 2); +XA END 'x'; +XA PREPARE 'x'; +XA COMMIT 'x'; +include/save_master_gtid.inc +connection slave; +include/start_slave.inc +# states of workers at the first XAP near binlogging are expected to be as the following +set debug_sync= "now signal binlog_xap"; +# Once the first XA PREPARE has binlogged.. +set debug_sync= "now wait_for xa_prepare_binlogged"; +SELECT 'x' = 'x'; +'x' = 'x' +1 +# cleanup: signal XA PREPARE to leave the stage +set debug_sync= "now signal continue_xap"; +# Proof of the full master-slave sync +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:test.t1, slave:test.t1] +connection slave; +include/stop_slave.inc +set @@global.debug_dbug= @save_debug; +# +# Test Case 3-COMMIT (Error Case): +# If an XA PREPARE errors while its +# XA COMMIT is waiting on it, both phases should rollback +# successfully. Note this tests both: +# a) XA COMMIT is waiting in group commit (first phase +# times out in DMLs) +# b) XA COMMIT is waiting in group commit, with another XAP +# with a duplicate XID waiting on it. +# Case a) +# Ensure slave is stopped +connection slave; +include/wait_for_slave_to_stop.inc +set @save_lock_wait_timeout= @@GLOBAL.innodb_lock_wait_timeout; +set @save_trans_retries= @@GLOBAL.slave_transaction_retries; +set @@global.innodb_lock_wait_timeout= 1; +set @@global.slave_transaction_retries= 0; +connection master; +XA START 'x'; +update t1 set b=b+1 where a=-1; +XA END 'x'; +XA PREPARE 'x'; +XA COMMIT 'x'; +include/save_master_gtid.inc +connection slave1; +BEGIN; +select * from t1 where a=-1 for update;; +a b +-1 0 +connection slave; +include/start_slave.inc +include/wait_for_slave_sql_error.inc [errno=1205] +connection slave1; +ROLLBACK; +connection slave; +include/stop_slave_io.inc +set @@global.innodb_lock_wait_timeout= @save_lock_wait_timeout; +set @@global.slave_transaction_retries= @save_trans_retries; +select @@global.gtid_slave_pos; +@@global.gtid_slave_pos +0-1-303 +# Ensure on slave restart, we can re-execute the XA transaction +include/start_slave.inc +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:t1, slave:t1] +select * from t1; +a b +-1 1 +0 0 +1 0 +2 1 +3 2 +include/stop_slave.inc +set @@global.debug_dbug= @save_debug; +# Case b) +# Ensure slave is stopped +connection slave; +include/wait_for_slave_to_stop.inc +set @save_lock_wait_timeout= @@GLOBAL.innodb_lock_wait_timeout; +set @save_trans_retries= @@GLOBAL.slave_transaction_retries; +set @@global.innodb_lock_wait_timeout= 1; +set @@global.slave_transaction_retries= 0; +connection master; +XA START 'x'; +update t1 set b=b+1 where a=-1; +select * from t1 where a=-1; +a b +-1 2 +XA END 'x'; +XA PREPARE 'x'; +XA COMMIT 'x'; +XA START 'x'; +insert into t1 values (4, 0); +XA END 'x'; +XA PREPARE 'x'; +include/save_master_gtid.inc +XA COMMIT 'x'; +connection slave1; +BEGIN; +select * from t1 where a=-1 for update;; +a b +-1 1 +connection slave; +include/start_slave.inc +include/wait_for_slave_sql_error.inc [errno=1205] +connection slave1; +ROLLBACK; +# There should not be any prepared rows seen by XA RECOVER +XA RECOVER; +formatID gtrid_length bqual_length data +# Ensuring data from second XAP *isn't* visible.. +# ..done +connection slave; +include/stop_slave_io.inc +set @@global.innodb_lock_wait_timeout= @save_lock_wait_timeout; +set @@global.slave_transaction_retries= @save_trans_retries; +# Ensure on slave restart, we can re-execute the XA transaction +include/start_slave.inc +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:t1, slave:t1] +select * from t1; +a b +-1 2 +0 0 +1 0 +2 1 +3 2 +4 0 +# Ensuring data from second XAP *is* visible.. +# ..done +# Cleanup +connection slave; +set debug_sync='RESET'; +set @@global.debug_dbug= @save_debug; +include/start_slave.inc +Warnings: +Note 1254 Slave is already running +connection master; +DROP TABLE t1; +include/save_master_gtid.inc +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; create table t1 (a int primary key, b int) engine=innodb +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (-1, 0) +master-bin.000001 # Xid # # COMMIT /* XID */ +master-bin.000001 # Gtid # # XA START X'7831',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (0, 0) +master-bin.000001 # Query # # XA END X'7831',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7831',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'7831',X'',1 +master-bin.000001 # Gtid # # XA START X'7832',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (1, 0) +master-bin.000001 # Query # # XA END X'7832',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7832',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'7832',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (2, 1) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (3, 2) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; update t1 set b=b+1 where a=-1 +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; update t1 set b=b+1 where a=-1 +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (4, 0) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ +connection slave; +include/sync_with_master_gtid.inc +show binlog events in 'slave-bin.000001' from ; +Log_name Pos Event_type Server_id End_log_pos Info +slave-bin.000001 # Gtid_list 2 # [] +slave-bin.000001 # Binlog_checkpoint 2 # slave-bin.000001 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; create table t1 (a int primary key, b int) engine=innodb +slave-bin.000001 # Gtid 1 # BEGIN GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (-1, 0) +slave-bin.000001 # Xid 1 # COMMIT /* XID */ +slave-bin.000001 # Gtid 1 # XA START X'7831',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (0, 0) +slave-bin.000001 # Query 1 # XA END X'7831',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'7831',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'7831',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'7832',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (1, 0) +slave-bin.000001 # Query 1 # XA END X'7832',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'7832',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'7832',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (2, 1) +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'78',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (3, 2) +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'78',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; update t1 set b=b+1 where a=-1 +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'78',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; update t1 set b=b+1 where a=-1 +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'78',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (4, 0) +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; DROP TABLE IF EXISTS `t1` /* generated by server */ +include/stop_slave.inc +set @@GLOBAL.slave_parallel_threads= @save_par_thds; +set @@GLOBAL.slave_parallel_mode= @save_par_mode; +include/start_slave.inc +# +# Initialize test data +connection slave; +include/stop_slave.inc +connection master; +create table t1 (a int primary key, b int) engine=innodb; +insert into t1 values (-1, 0); +include/save_master_gtid.inc +connection slave; +include/start_slave.inc +include/sync_with_master_gtid.inc +include/stop_slave.inc +set @save_debug= @@GLOBAL.debug_dbug; +set @save_par_thds= @@GLOBAL.slave_parallel_threads; +set @save_par_mode= @@GLOBAL.slave_parallel_mode; +set @@GLOBAL.slave_parallel_threads= 4; +set @@GLOBAL.slave_parallel_mode= optimistic; +set statement sql_log_bin=0 for call mtr.add_suppression("Commit failed due to failure of an earlier commit on which this one depends"); +# +# Test Case 1-ROLLBACK: +# If two XA ROLLBACK transactions have different +# XIDs, ensure both phases of both transactions all execute concurrently. +# +connection slave; +set @@global.debug_dbug= "+d,stop_before_binlog_prepare,stop_after_binlog_prepare"; +connection master; +set @@session.gtid_seq_no= 200 + (1 * 1000); +XA START 'x1'; +insert into t1 values (0, 0); +XA END 'x1'; +XA PREPARE 'x1'; +XA ROLLBACK 'x1'; +XA START 'x2'; +insert into t1 values (1, 0); +XA END 'x2'; +XA PREPARE 'x2'; +XA ROLLBACK 'x2'; +connection slave; +include/start_slave.inc +must be \Empty +XA RECOVER; +formatID gtrid_length bqual_length data +set debug_sync= "now signal binlog_xap"; +# Make a stop at a point where XA PREPARE (both) have completed (binlogged): +set debug_sync= "now wait_for xa_prepare_binlogged"; +# prove XA-ROLLBACK 'x1' has completed too +SELECT 'x1' = 'x1'; +'x1' = 'x1' +1 +# cleanup: release the seq_no 200 XAP +set debug_sync= "now signal continue_xap"; +# Proof of the full master-slave sync +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:test.t1, slave:test.t1] +connection slave; +include/stop_slave.inc +set @@global.debug_dbug= @save_debug; +# +# Test Case 2-ROLLBACK: +# Two concurrent 2-phase XA transactions with matching XIDs +# should run one after the other. +# +connection slave; +set @@global.debug_dbug= "+d,stop_before_binlog_prepare,stop_after_binlog_prepare"; +connection master; +set @@session.gtid_seq_no= 300 + (1 * 1000); +XA START 'x'; +insert into t1 values (2, 1); +XA END 'x'; +XA PREPARE 'x'; +XA ROLLBACK 'x'; +XA START 'x'; +insert into t1 values (3, 2); +XA END 'x'; +XA PREPARE 'x'; +XA ROLLBACK 'x'; +include/save_master_gtid.inc +connection slave; +include/start_slave.inc +# states of workers at the first XAP near binlogging are expected to be as the following +set debug_sync= "now signal binlog_xap"; +# Once the first XA PREPARE has binlogged.. +set debug_sync= "now wait_for xa_prepare_binlogged"; +SELECT 'x' = 'x'; +'x' = 'x' +1 +# cleanup: signal XA PREPARE to leave the stage +set debug_sync= "now signal continue_xap"; +# Proof of the full master-slave sync +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:test.t1, slave:test.t1] +connection slave; +include/stop_slave.inc +set @@global.debug_dbug= @save_debug; +# +# Test Case 3-ROLLBACK (Error Case): +# If an XA PREPARE errors while its +# XA ROLLBACK is waiting on it, both phases should rollback +# successfully. Note this tests both: +# a) XA ROLLBACK is waiting in group commit (first phase +# times out in DMLs) +# b) XA ROLLBACK is waiting in group commit, with another XAP +# with a duplicate XID waiting on it. +# Case a) +# Ensure slave is stopped +connection slave; +include/wait_for_slave_to_stop.inc +set @save_lock_wait_timeout= @@GLOBAL.innodb_lock_wait_timeout; +set @save_trans_retries= @@GLOBAL.slave_transaction_retries; +set @@global.innodb_lock_wait_timeout= 1; +set @@global.slave_transaction_retries= 0; +connection master; +XA START 'x'; +update t1 set b=b+1 where a=-1; +XA END 'x'; +XA PREPARE 'x'; +XA ROLLBACK 'x'; +include/save_master_gtid.inc +connection slave1; +BEGIN; +select * from t1 where a=-1 for update;; +a b +-1 0 +connection slave; +include/start_slave.inc +include/wait_for_slave_sql_error.inc [errno=1205] +connection slave1; +ROLLBACK; +connection slave; +include/stop_slave_io.inc +set @@global.innodb_lock_wait_timeout= @save_lock_wait_timeout; +set @@global.slave_transaction_retries= @save_trans_retries; +select @@global.gtid_slave_pos; +@@global.gtid_slave_pos +0-1-1303 +# Ensure on slave restart, we can re-execute the XA transaction +include/start_slave.inc +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:t1, slave:t1] +select * from t1; +a b +-1 0 +include/stop_slave.inc +set @@global.debug_dbug= @save_debug; +# Case b) +# Ensure slave is stopped +connection slave; +include/wait_for_slave_to_stop.inc +set @save_lock_wait_timeout= @@GLOBAL.innodb_lock_wait_timeout; +set @save_trans_retries= @@GLOBAL.slave_transaction_retries; +set @@global.innodb_lock_wait_timeout= 1; +set @@global.slave_transaction_retries= 0; +connection master; +XA START 'x'; +update t1 set b=b+1 where a=-1; +select * from t1 where a=-1; +a b +-1 1 +XA END 'x'; +XA PREPARE 'x'; +XA ROLLBACK 'x'; +XA START 'x'; +insert into t1 values (4, 0); +XA END 'x'; +XA PREPARE 'x'; +include/save_master_gtid.inc +XA ROLLBACK 'x'; +connection slave1; +BEGIN; +select * from t1 where a=-1 for update;; +a b +-1 0 +connection slave; +include/start_slave.inc +include/wait_for_slave_sql_error.inc [errno=1205] +connection slave1; +ROLLBACK; +# There should not be any prepared rows seen by XA RECOVER +XA RECOVER; +formatID gtrid_length bqual_length data +# Ensuring data from second XAP *isn't* visible.. +# ..done +connection slave; +include/stop_slave_io.inc +set @@global.innodb_lock_wait_timeout= @save_lock_wait_timeout; +set @@global.slave_transaction_retries= @save_trans_retries; +# Ensure on slave restart, we can re-execute the XA transaction +include/start_slave.inc +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:t1, slave:t1] +select * from t1; +a b +-1 0 +# Ensuring data from second XAP *is* visible.. +# ..done +# Cleanup +connection slave; +set debug_sync='RESET'; +set @@global.debug_dbug= @save_debug; +include/start_slave.inc +Warnings: +Note 1254 Slave is already running +connection master; +DROP TABLE t1; +include/save_master_gtid.inc +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; create table t1 (a int primary key, b int) engine=innodb +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (-1, 0) +master-bin.000001 # Xid # # COMMIT /* XID */ +master-bin.000001 # Gtid # # XA START X'7831',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (0, 0) +master-bin.000001 # Query # # XA END X'7831',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7831',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'7831',X'',1 +master-bin.000001 # Gtid # # XA START X'7832',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (1, 0) +master-bin.000001 # Query # # XA END X'7832',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7832',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'7832',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (2, 1) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (3, 2) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; update t1 set b=b+1 where a=-1 +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; update t1 set b=b+1 where a=-1 +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (4, 0) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; create table t1 (a int primary key, b int) engine=innodb +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (-1, 0) +master-bin.000001 # Xid # # COMMIT /* XID */ +master-bin.000001 # Gtid # # XA START X'7831',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (0, 0) +master-bin.000001 # Query # # XA END X'7831',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7831',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'7831',X'',1 +master-bin.000001 # Gtid # # XA START X'7832',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (1, 0) +master-bin.000001 # Query # # XA END X'7832',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7832',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'7832',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (2, 1) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (3, 2) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; update t1 set b=b+1 where a=-1 +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; update t1 set b=b+1 where a=-1 +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (4, 0) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ +connection slave; +include/sync_with_master_gtid.inc +show binlog events in 'slave-bin.000001' from ; +Log_name Pos Event_type Server_id End_log_pos Info +slave-bin.000001 # Gtid_list 2 # [] +slave-bin.000001 # Binlog_checkpoint 2 # slave-bin.000001 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; create table t1 (a int primary key, b int) engine=innodb +slave-bin.000001 # Gtid 1 # BEGIN GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (-1, 0) +slave-bin.000001 # Xid 1 # COMMIT /* XID */ +slave-bin.000001 # Gtid 1 # XA START X'7831',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (0, 0) +slave-bin.000001 # Query 1 # XA END X'7831',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'7831',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'7831',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'7832',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (1, 0) +slave-bin.000001 # Query 1 # XA END X'7832',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'7832',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'7832',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (2, 1) +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'78',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (3, 2) +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'78',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; update t1 set b=b+1 where a=-1 +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'78',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; update t1 set b=b+1 where a=-1 +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'78',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (4, 0) +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA COMMIT X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; DROP TABLE IF EXISTS `t1` /* generated by server */ +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; create table t1 (a int primary key, b int) engine=innodb +slave-bin.000001 # Gtid 1 # BEGIN GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (-1, 0) +slave-bin.000001 # Xid 1 # COMMIT /* XID */ +slave-bin.000001 # Gtid 1 # XA START X'7831',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (0, 0) +slave-bin.000001 # Query 1 # XA END X'7831',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'7831',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA ROLLBACK X'7831',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'7832',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (1, 0) +slave-bin.000001 # Query 1 # XA END X'7832',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'7832',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA ROLLBACK X'7832',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (2, 1) +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA ROLLBACK X'78',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (3, 2) +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA ROLLBACK X'78',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; update t1 set b=b+1 where a=-1 +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA ROLLBACK X'78',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; update t1 set b=b+1 where a=-1 +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA ROLLBACK X'78',X'',1 +slave-bin.000001 # Gtid 1 # XA START X'78',X'',1 GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; insert into t1 values (4, 0) +slave-bin.000001 # Query 1 # XA END X'78',X'',1 +slave-bin.000001 # XA_prepare 1 # XA PREPARE X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # XA ROLLBACK X'78',X'',1 +slave-bin.000001 # Gtid 1 # GTID #-#-# +slave-bin.000001 # Query 1 # use `test`; DROP TABLE IF EXISTS `t1` /* generated by server */ +include/stop_slave.inc +set @@GLOBAL.slave_parallel_threads= @save_par_thds; +set @@GLOBAL.slave_parallel_mode= @save_par_mode; +include/start_slave.inc +# +# Test Case 5: If an XAP is skipped by the replica (e.g. by incorrectly +# setting gtid_slave_pos), and only its XAC/XAR is tried to execute, the +# replica should report ER_XAER_NOTA. +connection master; +create table t1 (a int) engine=innodb; +include/save_master_gtid.inc +connection slave; +include/sync_with_master_gtid.inc +call mtr.add_suppression("XAER_NOTA: Unknown XID"); +include/stop_slave.inc +change master to master_use_gtid = slave_pos; +connection master; +xa start '1'; +insert into t1 set a=1; +xa end '1'; +xa prepare '1'; +xa rollback '1'; +insert into t1 set a=2; +include/save_master_gtid.inc +connection slave; +set @save_gtid_slave_pos= @@global.gtid_slave_pos; +SELECT CONCAT(domain_id,"-",server_id,"-", seq_no + 1) +into @gtid_skip +FROM mysql.gtid_slave_pos +WHERE seq_no = (SELECT DISTINCT max(seq_no) FROM mysql.gtid_slave_pos) limit 1; +set @@global.gtid_slave_pos = @gtid_skip; +start slave; +include/wait_for_slave_sql_error.inc [errno=1397] +select count(*) = 2 % 2 as 'must be true' from t1;; +must be true +1 +include/stop_slave.inc +set @@global.gtid_slave_pos = @save_gtid_slave_pos; +show warnings; +Level Code Message +Warning 1947 Specified GTID conflicts with the binary log which contains a more recent GTID . If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos +include/start_slave.inc +include/sync_with_master_gtid.inc +include/stop_slave.inc +change master to master_use_gtid = slave_pos; +connection master; +xa start '1'; +insert into t1 set a=1; +xa end '1'; +xa prepare '1'; +xa commit '1'; +insert into t1 set a=2; +include/save_master_gtid.inc +connection slave; +set @save_gtid_slave_pos= @@global.gtid_slave_pos; +SELECT CONCAT(domain_id,"-",server_id,"-", seq_no + 1) +into @gtid_skip +FROM mysql.gtid_slave_pos +WHERE seq_no = (SELECT DISTINCT max(seq_no) FROM mysql.gtid_slave_pos) limit 1; +set @@global.gtid_slave_pos = @gtid_skip; +start slave; +include/wait_for_slave_sql_error.inc [errno=1397] +select count(*) = 1 % 2 as 'must be true' from t1;; +must be true +1 +include/stop_slave.inc +set @@global.gtid_slave_pos = @save_gtid_slave_pos; +show warnings; +Level Code Message +include/start_slave.inc +include/sync_with_master_gtid.inc +connection master; +drop table t1; +connection slave; +include/rpl_end.inc +# End of rpl_xa_concurrent_2pc.test diff --git a/mysql-test/suite/rpl/r/rpl_xa_concurrent_2pc_lsu_off.result b/mysql-test/suite/rpl/r/rpl_xa_concurrent_2pc_lsu_off.result new file mode 100644 index 0000000000000..434985f581657 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_xa_concurrent_2pc_lsu_off.result @@ -0,0 +1,664 @@ +include/master-slave.inc +[connection master] +# +# Initialize test data +connection slave; +include/stop_slave.inc +connection master; +create table t1 (a int primary key, b int) engine=innodb; +insert into t1 values (-1, 0); +include/save_master_gtid.inc +connection slave; +include/start_slave.inc +include/sync_with_master_gtid.inc +include/stop_slave.inc +set @save_debug= @@GLOBAL.debug_dbug; +set @save_par_thds= @@GLOBAL.slave_parallel_threads; +set @save_par_mode= @@GLOBAL.slave_parallel_mode; +set @@GLOBAL.slave_parallel_threads= 4; +set @@GLOBAL.slave_parallel_mode= optimistic; +set statement sql_log_bin=0 for call mtr.add_suppression("Commit failed due to failure of an earlier commit on which this one depends"); +# +# Test Case 1-COMMIT: +# If two XA COMMIT transactions have different +# XIDs, ensure both phases of both transactions all execute concurrently. +# +connection slave; +set @@global.debug_dbug= "+d,stop_before_binlog_prepare,stop_after_binlog_prepare"; +connection master; +set @@session.gtid_seq_no= 200 + (0 * 1000); +XA START 'x1'; +insert into t1 values (0, 0); +XA END 'x1'; +XA PREPARE 'x1'; +XA COMMIT 'x1'; +XA START 'x2'; +insert into t1 values (1, 0); +XA END 'x2'; +XA PREPARE 'x2'; +XA COMMIT 'x2'; +connection slave; +include/start_slave.inc +must be \Empty +XA RECOVER; +formatID gtrid_length bqual_length data +set debug_sync= "now signal binlog_xap"; +# Make a stop at a point where XA PREPARE (both) have completed (binlogged): +set debug_sync= "now wait_for xa_prepare_binlogged"; +# prove XA-COMMIT 'x1' has completed too +SELECT 'x1' = 'x1'; +'x1' = 'x1' +1 +# cleanup: release the seq_no 200 XAP +set debug_sync= "now signal continue_xap"; +# Proof of the full master-slave sync +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:test.t1, slave:test.t1] +connection slave; +include/stop_slave.inc +set @@global.debug_dbug= @save_debug; +# +# Test Case 2-COMMIT: +# Two concurrent 2-phase XA transactions with matching XIDs +# should run one after the other. +# +connection slave; +set @@global.debug_dbug= "+d,stop_before_binlog_prepare,stop_after_binlog_prepare"; +connection master; +set @@session.gtid_seq_no= 300 + (0 * 1000); +XA START 'x'; +insert into t1 values (2, 1); +XA END 'x'; +XA PREPARE 'x'; +XA COMMIT 'x'; +XA START 'x'; +insert into t1 values (3, 2); +XA END 'x'; +XA PREPARE 'x'; +XA COMMIT 'x'; +include/save_master_gtid.inc +connection slave; +include/start_slave.inc +# states of workers at the first XAP near binlogging are expected to be as the following +set debug_sync= "now signal binlog_xap"; +# Once the first XA PREPARE has binlogged.. +set debug_sync= "now wait_for xa_prepare_binlogged"; +SELECT 'x' = 'x'; +'x' = 'x' +1 +# cleanup: signal XA PREPARE to leave the stage +set debug_sync= "now signal continue_xap"; +# Proof of the full master-slave sync +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:test.t1, slave:test.t1] +connection slave; +include/stop_slave.inc +set @@global.debug_dbug= @save_debug; +# +# Test Case 3-COMMIT (Error Case): +# If an XA PREPARE errors while its +# XA COMMIT is waiting on it, both phases should rollback +# successfully. Note this tests both: +# a) XA COMMIT is waiting in group commit (first phase +# times out in DMLs) +# b) XA COMMIT is waiting in group commit, with another XAP +# with a duplicate XID waiting on it. +# Case a) +# Ensure slave is stopped +connection slave; +include/wait_for_slave_to_stop.inc +set @save_lock_wait_timeout= @@GLOBAL.innodb_lock_wait_timeout; +set @save_trans_retries= @@GLOBAL.slave_transaction_retries; +set @@global.innodb_lock_wait_timeout= 1; +set @@global.slave_transaction_retries= 0; +connection master; +XA START 'x'; +update t1 set b=b+1 where a=-1; +XA END 'x'; +XA PREPARE 'x'; +XA COMMIT 'x'; +include/save_master_gtid.inc +connection slave1; +BEGIN; +select * from t1 where a=-1 for update;; +a b +-1 0 +connection slave; +include/start_slave.inc +include/wait_for_slave_sql_error.inc [errno=1205] +connection slave1; +ROLLBACK; +connection slave; +include/stop_slave_io.inc +set @@global.innodb_lock_wait_timeout= @save_lock_wait_timeout; +set @@global.slave_transaction_retries= @save_trans_retries; +select @@global.gtid_slave_pos; +@@global.gtid_slave_pos +0-1-303 +# Ensure on slave restart, we can re-execute the XA transaction +include/start_slave.inc +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:t1, slave:t1] +select * from t1; +a b +-1 1 +0 0 +1 0 +2 1 +3 2 +include/stop_slave.inc +set @@global.debug_dbug= @save_debug; +# Case b) +# Ensure slave is stopped +connection slave; +include/wait_for_slave_to_stop.inc +set @save_lock_wait_timeout= @@GLOBAL.innodb_lock_wait_timeout; +set @save_trans_retries= @@GLOBAL.slave_transaction_retries; +set @@global.innodb_lock_wait_timeout= 1; +set @@global.slave_transaction_retries= 0; +connection master; +XA START 'x'; +update t1 set b=b+1 where a=-1; +select * from t1 where a=-1; +a b +-1 2 +XA END 'x'; +XA PREPARE 'x'; +XA COMMIT 'x'; +XA START 'x'; +insert into t1 values (4, 0); +XA END 'x'; +XA PREPARE 'x'; +include/save_master_gtid.inc +XA COMMIT 'x'; +connection slave1; +BEGIN; +select * from t1 where a=-1 for update;; +a b +-1 1 +connection slave; +include/start_slave.inc +include/wait_for_slave_sql_error.inc [errno=1205] +connection slave1; +ROLLBACK; +# There should not be any prepared rows seen by XA RECOVER +XA RECOVER; +formatID gtrid_length bqual_length data +# Ensuring data from second XAP *isn't* visible.. +# ..done +connection slave; +include/stop_slave_io.inc +set @@global.innodb_lock_wait_timeout= @save_lock_wait_timeout; +set @@global.slave_transaction_retries= @save_trans_retries; +# Ensure on slave restart, we can re-execute the XA transaction +include/start_slave.inc +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:t1, slave:t1] +select * from t1; +a b +-1 2 +0 0 +1 0 +2 1 +3 2 +4 0 +# Ensuring data from second XAP *is* visible.. +# ..done +# Cleanup +connection slave; +set debug_sync='RESET'; +set @@global.debug_dbug= @save_debug; +include/start_slave.inc +Warnings: +Note 1254 Slave is already running +connection master; +DROP TABLE t1; +include/save_master_gtid.inc +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; create table t1 (a int primary key, b int) engine=innodb +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (-1, 0) +master-bin.000001 # Xid # # COMMIT /* XID */ +master-bin.000001 # Gtid # # XA START X'7831',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (0, 0) +master-bin.000001 # Query # # XA END X'7831',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7831',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'7831',X'',1 +master-bin.000001 # Gtid # # XA START X'7832',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (1, 0) +master-bin.000001 # Query # # XA END X'7832',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7832',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'7832',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (2, 1) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (3, 2) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; update t1 set b=b+1 where a=-1 +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; update t1 set b=b+1 where a=-1 +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (4, 0) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ +connection slave; +include/sync_with_master_gtid.inc +show binlog events in 'slave-bin.000001' from ; +Log_name Pos Event_type Server_id End_log_pos Info +slave-bin.000001 # Gtid_list 2 # [] +slave-bin.000001 # Binlog_checkpoint 2 # slave-bin.000001 +include/stop_slave.inc +set @@GLOBAL.slave_parallel_threads= @save_par_thds; +set @@GLOBAL.slave_parallel_mode= @save_par_mode; +include/start_slave.inc +# +# Initialize test data +connection slave; +include/stop_slave.inc +connection master; +create table t1 (a int primary key, b int) engine=innodb; +insert into t1 values (-1, 0); +include/save_master_gtid.inc +connection slave; +include/start_slave.inc +include/sync_with_master_gtid.inc +include/stop_slave.inc +set @save_debug= @@GLOBAL.debug_dbug; +set @save_par_thds= @@GLOBAL.slave_parallel_threads; +set @save_par_mode= @@GLOBAL.slave_parallel_mode; +set @@GLOBAL.slave_parallel_threads= 4; +set @@GLOBAL.slave_parallel_mode= optimistic; +set statement sql_log_bin=0 for call mtr.add_suppression("Commit failed due to failure of an earlier commit on which this one depends"); +# +# Test Case 1-ROLLBACK: +# If two XA ROLLBACK transactions have different +# XIDs, ensure both phases of both transactions all execute concurrently. +# +connection slave; +set @@global.debug_dbug= "+d,stop_before_binlog_prepare,stop_after_binlog_prepare"; +connection master; +set @@session.gtid_seq_no= 200 + (1 * 1000); +XA START 'x1'; +insert into t1 values (0, 0); +XA END 'x1'; +XA PREPARE 'x1'; +XA ROLLBACK 'x1'; +XA START 'x2'; +insert into t1 values (1, 0); +XA END 'x2'; +XA PREPARE 'x2'; +XA ROLLBACK 'x2'; +connection slave; +include/start_slave.inc +must be \Empty +XA RECOVER; +formatID gtrid_length bqual_length data +set debug_sync= "now signal binlog_xap"; +# Make a stop at a point where XA PREPARE (both) have completed (binlogged): +set debug_sync= "now wait_for xa_prepare_binlogged"; +# prove XA-ROLLBACK 'x1' has completed too +SELECT 'x1' = 'x1'; +'x1' = 'x1' +1 +# cleanup: release the seq_no 200 XAP +set debug_sync= "now signal continue_xap"; +# Proof of the full master-slave sync +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:test.t1, slave:test.t1] +connection slave; +include/stop_slave.inc +set @@global.debug_dbug= @save_debug; +# +# Test Case 2-ROLLBACK: +# Two concurrent 2-phase XA transactions with matching XIDs +# should run one after the other. +# +connection slave; +set @@global.debug_dbug= "+d,stop_before_binlog_prepare,stop_after_binlog_prepare"; +connection master; +set @@session.gtid_seq_no= 300 + (1 * 1000); +XA START 'x'; +insert into t1 values (2, 1); +XA END 'x'; +XA PREPARE 'x'; +XA ROLLBACK 'x'; +XA START 'x'; +insert into t1 values (3, 2); +XA END 'x'; +XA PREPARE 'x'; +XA ROLLBACK 'x'; +include/save_master_gtid.inc +connection slave; +include/start_slave.inc +# states of workers at the first XAP near binlogging are expected to be as the following +set debug_sync= "now signal binlog_xap"; +# Once the first XA PREPARE has binlogged.. +set debug_sync= "now wait_for xa_prepare_binlogged"; +SELECT 'x' = 'x'; +'x' = 'x' +1 +# cleanup: signal XA PREPARE to leave the stage +set debug_sync= "now signal continue_xap"; +# Proof of the full master-slave sync +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:test.t1, slave:test.t1] +connection slave; +include/stop_slave.inc +set @@global.debug_dbug= @save_debug; +# +# Test Case 3-ROLLBACK (Error Case): +# If an XA PREPARE errors while its +# XA ROLLBACK is waiting on it, both phases should rollback +# successfully. Note this tests both: +# a) XA ROLLBACK is waiting in group commit (first phase +# times out in DMLs) +# b) XA ROLLBACK is waiting in group commit, with another XAP +# with a duplicate XID waiting on it. +# Case a) +# Ensure slave is stopped +connection slave; +include/wait_for_slave_to_stop.inc +set @save_lock_wait_timeout= @@GLOBAL.innodb_lock_wait_timeout; +set @save_trans_retries= @@GLOBAL.slave_transaction_retries; +set @@global.innodb_lock_wait_timeout= 1; +set @@global.slave_transaction_retries= 0; +connection master; +XA START 'x'; +update t1 set b=b+1 where a=-1; +XA END 'x'; +XA PREPARE 'x'; +XA ROLLBACK 'x'; +include/save_master_gtid.inc +connection slave1; +BEGIN; +select * from t1 where a=-1 for update;; +a b +-1 0 +connection slave; +include/start_slave.inc +include/wait_for_slave_sql_error.inc [errno=1205] +connection slave1; +ROLLBACK; +connection slave; +include/stop_slave_io.inc +set @@global.innodb_lock_wait_timeout= @save_lock_wait_timeout; +set @@global.slave_transaction_retries= @save_trans_retries; +select @@global.gtid_slave_pos; +@@global.gtid_slave_pos +0-1-1303 +# Ensure on slave restart, we can re-execute the XA transaction +include/start_slave.inc +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:t1, slave:t1] +select * from t1; +a b +-1 0 +include/stop_slave.inc +set @@global.debug_dbug= @save_debug; +# Case b) +# Ensure slave is stopped +connection slave; +include/wait_for_slave_to_stop.inc +set @save_lock_wait_timeout= @@GLOBAL.innodb_lock_wait_timeout; +set @save_trans_retries= @@GLOBAL.slave_transaction_retries; +set @@global.innodb_lock_wait_timeout= 1; +set @@global.slave_transaction_retries= 0; +connection master; +XA START 'x'; +update t1 set b=b+1 where a=-1; +select * from t1 where a=-1; +a b +-1 1 +XA END 'x'; +XA PREPARE 'x'; +XA ROLLBACK 'x'; +XA START 'x'; +insert into t1 values (4, 0); +XA END 'x'; +XA PREPARE 'x'; +include/save_master_gtid.inc +XA ROLLBACK 'x'; +connection slave1; +BEGIN; +select * from t1 where a=-1 for update;; +a b +-1 0 +connection slave; +include/start_slave.inc +include/wait_for_slave_sql_error.inc [errno=1205] +connection slave1; +ROLLBACK; +# There should not be any prepared rows seen by XA RECOVER +XA RECOVER; +formatID gtrid_length bqual_length data +# Ensuring data from second XAP *isn't* visible.. +# ..done +connection slave; +include/stop_slave_io.inc +set @@global.innodb_lock_wait_timeout= @save_lock_wait_timeout; +set @@global.slave_transaction_retries= @save_trans_retries; +# Ensure on slave restart, we can re-execute the XA transaction +include/start_slave.inc +include/sync_with_master_gtid.inc +include/diff_tables.inc [master:t1, slave:t1] +select * from t1; +a b +-1 0 +# Ensuring data from second XAP *is* visible.. +# ..done +# Cleanup +connection slave; +set debug_sync='RESET'; +set @@global.debug_dbug= @save_debug; +include/start_slave.inc +Warnings: +Note 1254 Slave is already running +connection master; +DROP TABLE t1; +include/save_master_gtid.inc +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; create table t1 (a int primary key, b int) engine=innodb +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (-1, 0) +master-bin.000001 # Xid # # COMMIT /* XID */ +master-bin.000001 # Gtid # # XA START X'7831',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (0, 0) +master-bin.000001 # Query # # XA END X'7831',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7831',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'7831',X'',1 +master-bin.000001 # Gtid # # XA START X'7832',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (1, 0) +master-bin.000001 # Query # # XA END X'7832',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7832',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'7832',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (2, 1) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (3, 2) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; update t1 set b=b+1 where a=-1 +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; update t1 set b=b+1 where a=-1 +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (4, 0) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA COMMIT X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; create table t1 (a int primary key, b int) engine=innodb +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (-1, 0) +master-bin.000001 # Xid # # COMMIT /* XID */ +master-bin.000001 # Gtid # # XA START X'7831',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (0, 0) +master-bin.000001 # Query # # XA END X'7831',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7831',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'7831',X'',1 +master-bin.000001 # Gtid # # XA START X'7832',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (1, 0) +master-bin.000001 # Query # # XA END X'7832',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'7832',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'7832',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (2, 1) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (3, 2) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; update t1 set b=b+1 where a=-1 +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; update t1 set b=b+1 where a=-1 +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'78',X'',1 +master-bin.000001 # Gtid # # XA START X'78',X'',1 GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (4, 0) +master-bin.000001 # Query # # XA END X'78',X'',1 +master-bin.000001 # XA_prepare # # XA PREPARE X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # XA ROLLBACK X'78',X'',1 +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ +connection slave; +include/sync_with_master_gtid.inc +show binlog events in 'slave-bin.000001' from ; +Log_name Pos Event_type Server_id End_log_pos Info +slave-bin.000001 # Gtid_list 2 # [] +slave-bin.000001 # Binlog_checkpoint 2 # slave-bin.000001 +include/stop_slave.inc +set @@GLOBAL.slave_parallel_threads= @save_par_thds; +set @@GLOBAL.slave_parallel_mode= @save_par_mode; +include/start_slave.inc +# +# Test Case 5: If an XAP is skipped by the replica (e.g. by incorrectly +# setting gtid_slave_pos), and only its XAC/XAR is tried to execute, the +# replica should report ER_XAER_NOTA. +connection master; +create table t1 (a int) engine=innodb; +include/save_master_gtid.inc +connection slave; +include/sync_with_master_gtid.inc +call mtr.add_suppression("XAER_NOTA: Unknown XID"); +include/stop_slave.inc +change master to master_use_gtid = slave_pos; +connection master; +xa start '1'; +insert into t1 set a=1; +xa end '1'; +xa prepare '1'; +xa rollback '1'; +insert into t1 set a=2; +include/save_master_gtid.inc +connection slave; +set @save_gtid_slave_pos= @@global.gtid_slave_pos; +SELECT CONCAT(domain_id,"-",server_id,"-", seq_no + 1) +into @gtid_skip +FROM mysql.gtid_slave_pos +WHERE seq_no = (SELECT DISTINCT max(seq_no) FROM mysql.gtid_slave_pos) limit 1; +set @@global.gtid_slave_pos = @gtid_skip; +start slave; +include/wait_for_slave_sql_error.inc [errno=1397] +select count(*) = 2 % 2 as 'must be true' from t1;; +must be true +1 +include/stop_slave.inc +set @@global.gtid_slave_pos = @save_gtid_slave_pos; +show warnings; +Level Code Message +Warning 1947 Specified GTID conflicts with the binary log which contains a more recent GTID . If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos +include/start_slave.inc +include/sync_with_master_gtid.inc +include/stop_slave.inc +change master to master_use_gtid = slave_pos; +connection master; +xa start '1'; +insert into t1 set a=1; +xa end '1'; +xa prepare '1'; +xa commit '1'; +insert into t1 set a=2; +include/save_master_gtid.inc +connection slave; +set @save_gtid_slave_pos= @@global.gtid_slave_pos; +SELECT CONCAT(domain_id,"-",server_id,"-", seq_no + 1) +into @gtid_skip +FROM mysql.gtid_slave_pos +WHERE seq_no = (SELECT DISTINCT max(seq_no) FROM mysql.gtid_slave_pos) limit 1; +set @@global.gtid_slave_pos = @gtid_skip; +start slave; +include/wait_for_slave_sql_error.inc [errno=1397] +select count(*) = 1 % 2 as 'must be true' from t1;; +must be true +1 +include/stop_slave.inc +set @@global.gtid_slave_pos = @save_gtid_slave_pos; +show warnings; +Level Code Message +include/start_slave.inc +include/sync_with_master_gtid.inc +connection master; +drop table t1; +connection slave; +include/rpl_end.inc +# End of rpl_xa_concurrent_2pc.test diff --git a/mysql-test/suite/rpl/r/rpl_xa_empty_transaction.result b/mysql-test/suite/rpl/r/rpl_xa_empty_transaction.result index 917041c148d23..5f0a2adabbcc7 100644 --- a/mysql-test/suite/rpl/r/rpl_xa_empty_transaction.result +++ b/mysql-test/suite/rpl/r/rpl_xa_empty_transaction.result @@ -19,6 +19,8 @@ connection server_1; XA START 'x'; XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA COMMIT 'x';; include/save_master_gtid.inc FLUSH LOGS; @@ -53,13 +55,14 @@ connect con1,localhost,root,,; XA START 'x'; XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA COMMIT 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -92,6 +95,8 @@ connection server_1; XA START 'x'; XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA ROLLBACK 'x';; include/save_master_gtid.inc FLUSH LOGS; @@ -126,13 +131,14 @@ connect con1,localhost,root,,; XA START 'x'; XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA ROLLBACK 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -170,6 +176,8 @@ INSERT INTO ti VALUES (1),(1);; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA COMMIT 'x';; include/save_master_gtid.inc FLUSH LOGS; @@ -206,13 +214,14 @@ INSERT INTO ti VALUES (1),(1);; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA COMMIT 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -247,6 +256,8 @@ INSERT INTO ti VALUES (1),(1);; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA ROLLBACK 'x';; include/save_master_gtid.inc FLUSH LOGS; @@ -283,13 +294,14 @@ INSERT INTO ti VALUES (1),(1);; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA ROLLBACK 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -331,7 +343,11 @@ INSERT INTO tm VALUES (1),(1);; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA COMMIT 'x';; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -367,13 +383,14 @@ INSERT INTO tm VALUES (1),(1);; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA COMMIT 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -408,6 +425,8 @@ INSERT INTO tm VALUES (1),(1);; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA ROLLBACK 'x';; Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back @@ -446,13 +465,14 @@ INSERT INTO tm VALUES (1),(1);; ERROR 23000: Duplicate entry '1' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA ROLLBACK 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -495,6 +515,8 @@ INSERT INTO ti VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA COMMIT 'x';; include/save_master_gtid.inc FLUSH LOGS; @@ -533,13 +555,14 @@ INSERT INTO ti VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA COMMIT 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -576,6 +599,8 @@ INSERT INTO ti VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA ROLLBACK 'x';; include/save_master_gtid.inc FLUSH LOGS; @@ -614,13 +639,14 @@ INSERT INTO ti VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA ROLLBACK 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -643,7 +669,8 @@ include/sync_with_master_gtid.inc # # Test Case 5: A mixed XA transaction consisting of one statement that # can successfully be rolled back (first statement), and another that -# can not (second statement) should be binlogged +# can not (second statement) should be binlogged similarly to the normal +# transaction to reflect the 2nd statement. connection server_1; set @sav_binlog_format = @@binlog_format; set @@binlog_format = row; @@ -665,7 +692,11 @@ INSERT INTO tm VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA COMMIT 'x';; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -703,13 +734,14 @@ INSERT INTO tm VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA COMMIT 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -746,6 +778,8 @@ INSERT INTO tm VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA ROLLBACK 'x';; Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back @@ -786,13 +820,14 @@ INSERT INTO tm VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA ROLLBACK 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -818,7 +853,8 @@ set @@global.binlog_format = @sav_binlog_format; # # Test Case 6: A mixed XA transaction consisting of one statement that # cannot successfully be rolled back (first statement), and another that -# can (second statement) should be binlogged +# can (second statement) should be binlogged similarly to the normal +# transaction to reflect the 1st statement. connection server_1; set @sav_binlog_format = @@binlog_format; set @@binlog_format = row; @@ -840,7 +876,11 @@ INSERT INTO ti VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA COMMIT 'x';; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -878,13 +918,14 @@ INSERT INTO ti VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA COMMIT 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -921,6 +962,8 @@ INSERT INTO ti VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA ROLLBACK 'x';; Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back @@ -961,13 +1004,14 @@ INSERT INTO ti VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA ROLLBACK 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -992,7 +1036,7 @@ set @@binlog_format = @sav_binlog_format; set @@global.binlog_format = @sav_binlog_format; # # Test Case 7: An XA transaction consisting of two failed -# non-transactional statements should be binlogged +# non-transactional statements should be binlogged akin to 5,6. connection server_1; set @sav_binlog_format = @@binlog_format; set @@binlog_format = row; @@ -1014,7 +1058,11 @@ INSERT INTO tm VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA COMMIT 'x';; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -1052,13 +1100,14 @@ INSERT INTO tm VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA COMMIT 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -1095,6 +1144,8 @@ INSERT INTO tm VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA ROLLBACK 'x';; Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back @@ -1135,13 +1186,14 @@ INSERT INTO tm VALUES (2),(2);; ERROR 23000: Duplicate entry '2' for key 'PRIMARY' XA END 'x'; XA PREPARE 'x'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect con1; connection server_1; XA RECOVER; formatID gtrid_length bqual_length data -1 1 0 x XA ROLLBACK 'x';; -ERROR XA100: XA_RBROLLBACK: Transaction branch was rolled back +ERROR XAE04: XAER_NOTA: Unknown XID include/save_master_gtid.inc FLUSH LOGS; # MYSQL_BINLOG server_1_datadir/binlog_filename --result-file=assert_file @@ -1182,6 +1234,7 @@ include/stop_slave.inc SET @@GLOBAL.replicate_ignore_db= ""; SET @@GLOBAL.replicate_do_db= "db2"; change master to master_use_gtid=slave_pos; +SET @@GLOBAL.slave_parallel_workers=2; include/start_slave.inc connection server_1; use db1; @@ -1246,6 +1299,30 @@ include/start_slave.inc connection server_1; drop database db1; drop database db2; +use test; +connection server_1; +create table t_not_in_binlog (a int) engine=innodb; +flush logs; +include/save_master_gtid.inc +connect con1,localhost,root,,; +call mtr.add_suppression("XAER_NOTA: Unknown XID"); +SET sql_log_bin=0; +XA START 'a'; +insert into t_not_in_binlog set a=1; +XA END 'a'; +XA PREPARE 'a'; +disconnect con1; +connection server_1; +xa recover; +formatID gtrid_length bqual_length data +1 1 0 a +XA ROLLBACK 'a'; +drop table t_not_in_binlog; +include/save_master_gtid.inc +connection server_2; +include/sync_with_master_gtid.inc +connection server_3; +include/sync_with_master_gtid.inc connection server_1; include/rpl_end.inc # End of rpl_xa_empty_transaction.test diff --git a/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result b/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result index f10e3e578bdb1..cb8edc38afcaf 100644 --- a/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result +++ b/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result @@ -68,6 +68,8 @@ xa start 'ro_2'; select count(*) into @s2 from t1; xa end 'ro_2'; xa prepare 'ro_2';; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master_ro_2; connection master; connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; @@ -77,6 +79,8 @@ select 1; 1 xa end 'ro_1'; xa prepare 'ro_1';; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master_ro_1; connection master; connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; @@ -84,6 +88,8 @@ xa start 'ro_2'; select count(*) into @s2 from t1; xa end 'ro_2'; xa prepare 'ro_2';; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master_ro_2; connection master; connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; @@ -93,17 +99,17 @@ select 1; 1 xa end 'ro_1'; xa prepare 'ro_1';; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master_ro_1; -*** 2 prepared xa:s must be in the list: +*** Zero prepared xa:s must be in the list: connection master; xa recover; formatID gtrid_length bqual_length data -1 4 0 ro_1 -1 4 0 ro_2 *** Zero prepared xa:s must be in the list: xa recover; formatID gtrid_length bqual_length data -*** At the end of read-only section gtid list has 0 more compare with previous check: +*** At the end of the above read-only section gtid list must not change from the previous check: flush logs; show binlog events in 'master-bin.000003' limit 1,1; Log_name Pos Event_type Server_id End_log_pos Info @@ -123,15 +129,18 @@ connection master; xa recover; formatID gtrid_length bqual_length data 1 12 0 rw_no_binlog +set @@session.sql_log_bin = 0; +xa $complete '$xid'; +set @@session.sql_log_bin = 1; *** Zero must be in the list: connection master; xa recover; formatID gtrid_length bqual_length data -*** At the end of --binlog-ignore-db section gtid list has 2 more: +*** At the end of --binlog-ignore-db section gtid list remains the same. flush logs; show binlog events in 'master-bin.000004' limit 1,1; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000004 # Gtid_list 1 # [0-1-13] +master-bin.000004 # Gtid_list 1 # [0-1-11] connection master; create table t3 (a int) engine=innodb; *** the disconnected prepare case @@ -171,7 +180,7 @@ formatID gtrid_length bqual_length data flush logs; show binlog events in 'master-bin.000005' limit 1,1; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000005 # Gtid_list 1 # [0-1-18] +master-bin.000005 # Gtid_list 1 # [0-1-16] create table tm (a int) engine=myisam; connection master; connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; @@ -179,6 +188,8 @@ xa start 'rw_myisam'; insert into tm set a=1; xa end 'rw_myisam'; xa prepare 'rw_myisam';; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master_rw_myisam; connection master; connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; @@ -186,20 +197,30 @@ xa start 'rw_myisam'; insert into tm set a=1; xa end 'rw_myisam'; xa prepare 'rw_myisam';; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master_rw_myisam; *** rw_myisam prepared must be in the list: connection master; xa recover; formatID gtrid_length bqual_length data -1 9 0 rw_myisam *** Zero prepared xa:s must be in the list: xa recover; formatID gtrid_length bqual_length data -*** At the end of MyISAM "xa" section gtid list has 7 more compare with previous check: +*** At the end of MyISAM "xa" section gtid list has 3 more compare with previous check: flush logs; show binlog events in 'master-bin.000006' limit 1,1; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000006 # Gtid_list 1 # [0-1-25] +master-bin.000006 # Gtid_list 1 # [0-1-19] +set @@session.sql_log_bin = OFF; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'skip_binlog'; +insert into t2 values(1); +xa end 'skip_binlog'; +xa prepare 'skip_binlog';; +xa $complete '$xid'; +set @@session.sql_log_bin = ON; +disconnect master_skip_binlog; connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; set @@session.sql_log_bin = OFF; xa start 'skip_binlog'; @@ -224,7 +245,7 @@ formatID gtrid_length bqual_length data flush logs; show binlog events in 'master-bin.000007' limit 1,1; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000007 # Gtid_list 1 # [0-1-25] +master-bin.000007 # Gtid_list 1 # [0-1-21] connection slave; include/sync_with_master_gtid.inc connection master; diff --git a/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect.result b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect.result index a295a5d4dc2d9..9488ee522a1ce 100644 --- a/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect.result +++ b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect.result @@ -99,6 +99,8 @@ a 2 XA END '4'; XA PREPARE '4'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master2; connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'bulk_trx_10'; diff --git a/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_lsu_off.result b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_lsu_off.result index a295a5d4dc2d9..9488ee522a1ce 100644 --- a/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_lsu_off.result +++ b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_lsu_off.result @@ -99,6 +99,8 @@ a 2 XA END '4'; XA PREPARE '4'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back disconnect master2; connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,; XA START 'bulk_trx_10'; diff --git a/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_mixed_engines.result b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_mixed_engines.result index 09bfffc0da452..e1f32b1254b80 100644 --- a/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_mixed_engines.result +++ b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_mixed_engines.result @@ -22,7 +22,11 @@ XA START 'xa_trx'; INSERT INTO tm VALUES (3); XA END 'xa_trx'; XA PREPARE 'xa_trx'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA COMMIT 'xa_trx' ; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back CREATE TEMPORARY TABLE tmp_i LIKE t; CREATE TEMPORARY TABLE tmp_m LIKE tm; XA START 'xa_trx'; @@ -87,8 +91,13 @@ XA PREPARE 'xa_trx'; XA COMMIT 'xa_trx' ; XA START 'xa_trx'; UPDATE t SET a = 99 where a = -1; +SELECT ROW_COUNT(); +ROW_COUNT() +0 XA END 'xa_trx'; XA PREPARE 'xa_trx'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back XA COMMIT 'xa_trx' ; include/sync_slave_sql_with_master.inc connection master; @@ -166,6 +175,9 @@ XA END 'xa_trx'; XA COMMIT 'xa_trx' ONE PHASE; XA START 'xa_trx'; UPDATE t SET a = 99 where a = -1; +SELECT ROW_COUNT(); +ROW_COUNT() +0 XA END 'xa_trx'; XA COMMIT 'xa_trx' ONE PHASE; include/sync_slave_sql_with_master.inc @@ -191,6 +203,8 @@ XA START 'xa_trx'; INSERT INTO tm VALUES (3); XA END 'xa_trx'; XA PREPARE 'xa_trx'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back xa rollback 'xa_trx' ; Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back @@ -268,8 +282,13 @@ Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back XA START 'xa_trx'; UPDATE t SET a = 99 where a = -1; +SELECT ROW_COUNT(); +ROW_COUNT() +0 XA END 'xa_trx'; XA PREPARE 'xa_trx'; +Warnings: +Note 4226 Found to be read-only XA transaction is rolled back xa rollback 'xa_trx' ; include/sync_slave_sql_with_master.inc connection master; @@ -363,6 +382,9 @@ Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back XA START 'xa_trx'; UPDATE t SET a = 99 where a = -1; +SELECT ROW_COUNT(); +ROW_COUNT() +0 XA END 'xa_trx'; xa rollback 'xa_trx' ; include/sync_slave_sql_with_master.inc diff --git a/mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc b/mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc index b823ebf62ee9a..05adc1682b9ac 100644 --- a/mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc +++ b/mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc @@ -1,6 +1,6 @@ # param $xid to name xa and take part in the connection name # param $query to execute as the xa body -# param $db_ign the default database +# param $db the default database --connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,) --eval xa start '$xid' diff --git a/mysql-test/suite/rpl/t/rpl_parallel_multi_domain_xa.test b/mysql-test/suite/rpl/t/rpl_parallel_multi_domain_xa.test index f17634d4df816..b1c0b38eb6f9d 100644 --- a/mysql-test/suite/rpl/t/rpl_parallel_multi_domain_xa.test +++ b/mysql-test/suite/rpl/t/rpl_parallel_multi_domain_xa.test @@ -69,7 +69,9 @@ while ($mode) --eval XA START '$conn$trx$i' --eval UPDATE t1 SET b = 1 - 2 * $decision WHERE a = 1 --eval XA END '$conn$trx$i' + set @@session.note_verbosity=""; --eval XA PREPARE '$conn$trx$i' + set @@session.note_verbosity=default; --let $term = COMMIT if ($decision) { diff --git a/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa.test b/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa.test index 6e5cf4367a725..2ce20e1cc7c27 100644 --- a/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa.test +++ b/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa.test @@ -56,13 +56,26 @@ while($i > 0) { # 'decision' to commit 0, or rollback 1 --let $decision = `SELECT $i % 2` + --let $b = `SELECT 1 - 2 * $decision` --eval XA START '$conn$trx$i' - --eval UPDATE t1 SET b = 1 - 2 * $decision WHERE a = 1 + --let $ignore_note = `select @@session.binlog_format = 'ROW' AND b = $b from t1 where a = 1` + --eval UPDATE t1 SET b = $b WHERE a = 1 --eval XA END '$conn$trx$i' - --let $one_phase = `SELECT IF(floor(rand()*10)%2, "ONE PHASE", 0)` + --let $one_phase = `SELECT IF($i%2, "ONE PHASE", 0)` if (!$one_phase) { + # Only ROW format can produce empty XA-prepare so generates notes. + # STATEMENT (MIXED) format case features actual binlogging yet + # of ineffective in Engine updates (same record updated). + if ($ignore_note) + { + set @@session.note_verbosity=""; + } --eval XA PREPARE '$conn$trx$i' + if ($ignore_note) + { + set @@session.note_verbosity=default; + } --let $one_phase = } @@ -85,7 +98,7 @@ while($i > 0) --source include/stop_slave.inc -# II. Logging XS:s from multiple connections in random interweaving manner: +# II. Logging XS:s from multiple connections in interweaving manner: # # in a loop ($i) per connection # arrange an inner ($k) loop where @@ -126,7 +139,7 @@ while($i > 0) # the row id of the last connection that committed its XA --let $c_max = 1 --let $k = 0 - while ($decision < 3) + while ($decision < 4) { --inc $k --eval XA START '$conn_i$trx$k' @@ -147,7 +160,6 @@ while($i > 0) --eval XA END '$conn_i$trx$k' --let $term = COMMIT - --let $decision = `SELECT (floor(rand()*10 % 10) + ($i+$k)) % 4` if ($decision == 1) { --let $term = ROLLBACK @@ -162,6 +174,8 @@ while($i > 0) { --eval XA COMMIT '$conn_i$trx$k' ONE PHASE } + + --inc $decision } # $decision = 3 diff --git a/mysql-test/suite/rpl/t/rpl_xa.inc b/mysql-test/suite/rpl/t/rpl_xa.inc index 7fb42d711af58..361fbdc35a716 100644 --- a/mysql-test/suite/rpl/t/rpl_xa.inc +++ b/mysql-test/suite/rpl/t/rpl_xa.inc @@ -71,8 +71,7 @@ let $diff_tables= master:t2, slave:t2; source include/diff_tables.inc; # -# Read-only XA remains prepared after disconnect and must rollback at XA-complete -# after recoonect. To the read-only also belongs non-transactional engine XA. +# Read-only XA is not binlogged and is rolled back by disconnect. # --connection master @@ -128,7 +127,7 @@ while ($p_trx) --dec $p_trx } ---echo *** $ro_cases prepared xa:s must be in the list: +--echo *** Zero prepared xa:s must be in the list: --connection master sorted_result; xa recover; @@ -140,7 +139,7 @@ while ($p_trx) --let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')` --disable_query_log --disable_result_log - --error ER_XA_RBROLLBACK + --error ER_XAER_NOTA --eval xa $complete '$xid' --enable_result_log --enable_query_log @@ -150,7 +149,7 @@ while ($p_trx) --echo *** Zero prepared xa:s must be in the list: xa recover; ---echo *** At the end of read-only section gtid list has 0 more compare with previous check: +--echo *** At the end of the above read-only section gtid list must not change from the previous check: flush logs; --let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1) --source include/show_gtid_list.inc @@ -160,8 +159,9 @@ flush logs; # XA logging cases while some of XA resources are read-only # # A1. Binlog filter - - +# The empty XAP is created. It reaches slave though where it is read-only +# optimized. This affects the following XA-complete which causes slave error out. +# --let $db=test_ign --eval create database $db set @@sql_log_bin = 0; @@ -179,12 +179,13 @@ set @@sql_log_bin = 1; xa recover; --let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')` ---error 0 ---disable_query_log ---disable_result_log ---eval xa $complete '$xid' ---enable_result_log ---enable_query_log +# ignore-db filtered out XA-PREPARE is empty-binlogged and ... +# as of now unnecessarily replicated. +# todo: implement SKIP_REPLICATION flag for either of XA -PREPARE and -COMPLETE +# As a work-around skip binlogging of its completion. +set @@session.sql_log_bin = 0; +--evalp xa $complete '$xid' +set @@session.sql_log_bin = 1; --echo *** Zero must be in the list: --connection master @@ -192,15 +193,15 @@ xa recover; # restore for the following tests --let $db=test ---echo *** At the end of --binlog-ignore-db section gtid list has 2 more: +--echo *** At the end of --binlog-ignore-db section gtid list remains the same. flush logs; --let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1) --source include/show_gtid_list.inc # # A2. Opposite to A1, ineffective execution in Engine may create a -# binlog transaction -# +# binlog transaction. +# This case is non-crash-recoverable by design of "maybe-all" record DELETE. connection master; create table t3 (a int) engine=innodb; @@ -265,8 +266,8 @@ flush logs; --source include/show_gtid_list.inc # -# A3 MyISAM "xa" logs empty XA-prepare group, followed by -# an XA-complete event +# A3 MyISAM "xa" does not log even empty XA-prepare group. +# Such transaction is considered to be read-only so optimized away. create table tm (a int) engine=myisam; # No disconnect @@ -298,6 +299,8 @@ xa recover; --let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')` --disable_query_log --disable_result_log + # the result of read-only optimization + --error ER_XAER_NOTA --eval xa $complete '$xid' --enable_result_log --enable_query_log @@ -305,18 +308,27 @@ xa recover; --echo *** Zero prepared xa:s must be in the list: xa recover; ---echo *** At the end of MyISAM "xa" section gtid list has 7 more compare with previous check: +--echo *** At the end of MyISAM "xa" section gtid list has 3 more compare with previous check: flush logs; --let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1) --source include/show_gtid_list.inc -# B. Session binlog disable does not log even empty XA-prepare -# Therefore XA-complete should be also run in sql_log_bin-OFF environment. +# B. Session binlog disable does log even empty XA-prepare. +# As a workaround XA-complete should be also run in sql_log_bin=OFF. --let $db=test --let $xid=skip_binlog --let $query=insert into t2 values(1) +set @@session.sql_log_bin = OFF; + --source rpl_create_xa_prepared.inc + --let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')` + --evalp xa $complete '$xid' +set @@session.sql_log_bin = ON; + --disconnect master_$xid + --source include/wait_until_disconnected.inc + +# with disconnect --connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,) set @@session.sql_log_bin = OFF; --eval xa start '$xid' diff --git a/mysql-test/suite/rpl/t/rpl_xa_concurrent_2pc.test b/mysql-test/suite/rpl/t/rpl_xa_concurrent_2pc.test new file mode 100644 index 0000000000000..12d07cf327c0b --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_xa_concurrent_2pc.test @@ -0,0 +1,111 @@ +# +# This test ensures that two-phase XA transactions have their first and +# second phases parallelized for both XA COMMIT and XA ROLLBACK. It ensures the +# following behaviors: +# +# Test Case 1: Ensure that a 2-phase XA transaction has its XA PREPARE and +# XA COMMIT/ROLLBACK run concurrently. That is, the XA COMMIT/ROLLBACK will +# wait at group commit until the XA PREPARE binlogs, and then it will wait +# again until the XA PREPARE finishes preparing in all engines. At this point, +# the XA COMMIT/ROLLBACK will run to completion. +# +# Test Case 2: If two XA transactions have different XIDs, if XA COMMIT ends +# a transaction, ensure both phases of both transactions can all execute +# concurrently. +# +# Test Case 3: Two current 2-phase XA transactions with matching XIDs should +# run one after the other, while each transaction still allows both phases of +# its own transaction to run concurrently. +# +# Test Case 4: Error Case. If an XAP errors while its XAC/R is waiting on it, +# both the XAP and XAC/R should rollback successfully. Note this tests both: +# a) XAC/R is waiting in group commit (first phase times out in DMLs) +# b) XAC/R is waiting in group commit, with another XAP with a duplicate XID +# waiting on it. +# +# Test Case 5: If an XAP is skipped by the replica (e.g. by incorrectly +# setting gtid_slave_pos), and only its XAC/XAR is tried to execute, the +# replica should report ER_XAER_NOTA. +# +# +# References: +# MDEV-31949: slow parallel replication of user xa +# +--source include/have_debug.inc +--source include/have_innodb.inc +--source include/have_binlog_format_mixed.inc +--source include/master-slave.inc + +--let $xa_complete_sym= COMMIT +--source include/rpl_xa_concurrent_2pc.inc + +--let $xa_complete_sym= ROLLBACK +--source include/rpl_xa_concurrent_2pc.inc + +--echo # +--echo # Test Case 5: If an XAP is skipped by the replica (e.g. by incorrectly +--echo # setting gtid_slave_pos), and only its XAC/XAR is tried to execute, the +--echo # replica should report ER_XAER_NOTA. + +--connection master +create table t1 (a int) engine=innodb; +--source include/save_master_gtid.inc + +--connection slave +--source include/sync_with_master_gtid.inc +call mtr.add_suppression("XAER_NOTA: Unknown XID"); + +--let $i=2 +while ($i) +{ + --source include/stop_slave.inc + --replace_regex /[0-9]*-[0-9]*-[0-9]*// + change master to master_use_gtid = slave_pos; + + --connection master + --let $complete=rollback + if ($i == 1) + { + --let $complete=commit + } + xa start '1'; insert into t1 set a=1; xa end '1'; xa prepare '1'; + --eval xa $complete '1' + insert into t1 set a=2; + --source include/save_master_gtid.inc + + --connection slave + + # reposition the slave to skip one transaction from master + set @save_gtid_slave_pos= @@global.gtid_slave_pos; + SELECT CONCAT(domain_id,"-",server_id,"-", seq_no + 1) + into @gtid_skip + FROM mysql.gtid_slave_pos + WHERE seq_no = (SELECT DISTINCT max(seq_no) FROM mysql.gtid_slave_pos) limit 1; + set @@global.gtid_slave_pos = @gtid_skip; + + start slave; + let $slave_sql_errno= 1397; # ER_XAER_NOTA + source include/wait_for_slave_sql_error.inc; + --eval select count(*) = $i % 2 as 'must be true' from t1; + --let $rpl_allow_error = 1 + --source include/stop_slave.inc + + --disable_warnings + set @@global.gtid_slave_pos = @save_gtid_slave_pos; + --enable_warnings + --replace_regex /[0-9]*-[0-9]*-[0-9]*// + show warnings; + --source include/start_slave.inc + --source include/sync_with_master_gtid.inc + + --dec $i +} + +# MDEV-31949 cleanup +--connection master +drop table t1; + +--sync_slave_with_master +--source include/rpl_end.inc +--echo # End of rpl_xa_concurrent_2pc.test + diff --git a/mysql-test/suite/rpl/t/rpl_xa_concurrent_2pc_lsu_off-slave.opt b/mysql-test/suite/rpl/t/rpl_xa_concurrent_2pc_lsu_off-slave.opt new file mode 100644 index 0000000000000..acd68493e0aeb --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_xa_concurrent_2pc_lsu_off-slave.opt @@ -0,0 +1 @@ +--log-slave-updates=0 diff --git a/mysql-test/suite/rpl/t/rpl_xa_concurrent_2pc_lsu_off.test b/mysql-test/suite/rpl/t/rpl_xa_concurrent_2pc_lsu_off.test new file mode 100644 index 0000000000000..547d04990ec59 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_xa_concurrent_2pc_lsu_off.test @@ -0,0 +1 @@ +--source rpl_xa_concurrent_2pc.test diff --git a/mysql-test/suite/rpl/t/rpl_xa_empty_transaction.test b/mysql-test/suite/rpl/t/rpl_xa_empty_transaction.test index 85b6bcdb87397..891195699b4e8 100644 --- a/mysql-test/suite/rpl/t/rpl_xa_empty_transaction.test +++ b/mysql-test/suite/rpl/t/rpl_xa_empty_transaction.test @@ -111,7 +111,8 @@ set @@global.binlog_format = @sav_binlog_format; --echo # --echo # Test Case 5: A mixed XA transaction consisting of one statement that --echo # can successfully be rolled back (first statement), and another that ---echo # can not (second statement) should be binlogged +--echo # can not (second statement) should be binlogged similarly to the normal +--echo # transaction to reflect the 2nd statement. --connection server_1 set @sav_binlog_format = @@binlog_format; @@ -133,7 +134,8 @@ set @@global.binlog_format = @sav_binlog_format; --echo # --echo # Test Case 6: A mixed XA transaction consisting of one statement that --echo # cannot successfully be rolled back (first statement), and another that ---echo # can (second statement) should be binlogged +--echo # can (second statement) should be binlogged similarly to the normal +--echo # transaction to reflect the 1st statement. --connection server_1 set @sav_binlog_format = @@binlog_format; @@ -153,7 +155,7 @@ set @@global.binlog_format = @sav_binlog_format; --echo # --echo # Test Case 7: An XA transaction consisting of two failed ---echo # non-transactional statements should be binlogged +--echo # non-transactional statements should be binlogged akin to 5,6. --connection server_1 set @sav_binlog_format = @@binlog_format; @@ -197,6 +199,7 @@ change master to master_use_gtid=slave_pos; SET @@GLOBAL.replicate_ignore_db= ""; SET @@GLOBAL.replicate_do_db= "db2"; change master to master_use_gtid=slave_pos; +SET @@GLOBAL.slave_parallel_workers=2; --source include/start_slave.inc --connection server_1 @@ -296,7 +299,53 @@ SET @@GLOBAL.replicate_do_db=""; --connection server_1 drop database db1; drop database db2; +use test; +# MDEV-32257 dangling XA-rollback in binlog from emtpy XA +# create a case of XA ROLLBACK gets to binlog while its XAP was not and +# try replicate it. +# Expected result is both slaves error out. +--connection server_1 +--let $binlog_start = query_get_value(SHOW MASTER STATUS, Position, 1) +--let $binlog_file = query_get_value(SHOW MASTER STATUS, File, 1) +create table t_not_in_binlog (a int) engine=innodb; +flush logs; +--source include/save_master_gtid.inc +--let $binlog_file=query_get_value(SHOW MASTER STATUS, File, 1) + +# External connection XID access after disconnect is subject to race. +# "(" open parenthesis to remember # of connection before ... +--source include/count_sessions.inc + +--connect(con1,localhost,root,,) +call mtr.add_suppression("XAER_NOTA: Unknown XID"); + +SET sql_log_bin=0; +XA START 'a'; +insert into t_not_in_binlog set a=1; +XA END 'a'; +XA PREPARE 'a'; +--disconnect con1 + +--connection server_1 +# .. ")" close parenthesis, to wait until con1 fully releases access to xid. +--source include/wait_until_count_sessions.inc +xa recover; +# +# replicate orphan XAR to server 2,3 and expect the error first +# after that compensate it. + +--error 0 +XA ROLLBACK 'a'; +# cleanup at once +drop table t_not_in_binlog; +--source include/save_master_gtid.inc + +--connection server_2 +--source include/sync_with_master_gtid.inc + +--connection server_3 +--source include/sync_with_master_gtid.inc # # Cleanup diff --git a/mysql-test/suite/rpl/t/rpl_xa_prepare_gtid_fail.test b/mysql-test/suite/rpl/t/rpl_xa_prepare_gtid_fail.test index aa1b088ed232c..1fb7677e087ee 100644 --- a/mysql-test/suite/rpl/t/rpl_xa_prepare_gtid_fail.test +++ b/mysql-test/suite/rpl/t/rpl_xa_prepare_gtid_fail.test @@ -16,9 +16,9 @@ # MDEV-31038: Parallel Replication Breaks if XA PREPARE Fails Updating Slave # GTID State # -source include/master-slave.inc; source include/have_binlog_format_row.inc; source include/have_innodb.inc; +source include/master-slave.inc; --connection slave --source include/stop_slave.inc diff --git a/sql/handler.cc b/sql/handler.cc index 13c6473746dc7..c18cdcfb87788 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1534,9 +1534,22 @@ int ha_prepare(THD *thd) if (ha_info) { - for (; ha_info; ha_info= ha_info->next()) + int err; + bool is_rw, has_binlog; + + for (is_rw= false, has_binlog= false; ha_info; ha_info= ha_info->next()) { transaction_participant *ht= ha_info->ht(); + + if (ht == &binlog_tp) + { + /* + The marking may be optimistic when the transaction cache is empty. + However it's safe as binlog_prepare et al would return early. + */ + has_binlog= true; + continue; + } if (ht->prepare) { if (unlikely(prepare_or_error(ht, thd, all))) @@ -1546,6 +1559,7 @@ int ha_prepare(THD *thd) error=1; break; } + is_rw= is_rw || ha_info->is_trx_read_write(); } else { @@ -1556,17 +1570,27 @@ int ha_prepare(THD *thd) } } - - DEBUG_SYNC(thd, "at_unlog_xa_prepare"); - - if (tc_log->unlog_xa_prepare(thd, all)) + // engine read-only must proceed into binlogging by slave applier + if (!is_rw && !thd->rgi_slave) + { + goto ro_xa; + } + else if (has_binlog && (err= prepare_or_error(&binlog_tp, thd, all))) { ha_rollback_trans(thd, all); - error=1; + my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); + error= 1; + goto err; } } - else if (thd->rgi_slave) + else { +ro_xa: + if (!thd->rgi_slave) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_XA_RDONLY, ER_THD(thd, ER_XA_RDONLY), ""); + } /* Slave threads will always process XA COMMITs in the binlog handler (see MDEV-25616 and MDEV-30423), so if this is a slave thread preparing a @@ -1578,10 +1602,14 @@ int ha_prepare(THD *thd) If the xid_cache is cleared before the completion event comes, before issuing ER_XAER_NOTA, first check if the event targets an ignored database, and ignore the error if so. + + The above policy is generalized over any user read-only xa, de-facto + implementing the XA specs' read-only optimization. */ thd->transaction->xid_state.set_rollback_only(); } +err: DBUG_RETURN(error); } @@ -2170,7 +2198,7 @@ static bool is_ro_1pc_trans(THD *thd, Ha_trx_info *ha_info, bool all, return !rw_trans; } -static bool has_binlog_hton(Ha_trx_info *ha_info) +bool has_binlog_hton(Ha_trx_info *ha_info) { bool rc; for (rc= false; ha_info && !rc; ha_info= ha_info->next()) @@ -2304,7 +2332,11 @@ int ha_rollback_trans(THD *thd, bool all) if (thd->rgi_slave && !thd->rgi_slave->worker_error && - thd->rgi_slave->did_mark_start_commit) + thd->rgi_slave->did_mark_start_commit && + /* rollback-only XA executes rollback on success, should not unmark */ + !(thd->transaction->xid_state.is_explicit_XA() && + thd->transaction->xid_state.get_state_code() == + XA_ROLLBACK_ONLY)) thd->rgi_slave->unmark_start_commit(); } #endif @@ -2336,11 +2368,24 @@ int ha_rollback_trans(THD *thd, bool all) if (is_real_trans) /* not a statement commit */ thd->stmt_map.close_transient_cursors(); + int err; + bool has_binlog= has_binlog_hton(ha_info); + + // !m_ht->commit_ordered condition can occur e.g with Spider + DBUG_ASSERT(thd->lex->sql_command != SQLCOM_XA_ROLLBACK || + (thd->transaction->xid_state.is_explicit_XA() || + !thd->transaction->stmt.ha_list->ht()->commit_ordered)); + + if (has_binlog && (err= binlog_tp.rollback(thd, all))) + { + my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); + error= 1; + } for (; ha_info; ha_info= ha_info_next) { - int err; transaction_participant *ht= ha_info->ht(); - if ((err= ht->rollback(thd, all))) + if ((ht != &binlog_tp) && + (err= ht->rollback(thd, all))) { // cannot happen my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err); @@ -2426,51 +2471,114 @@ int ha_rollback_trans(THD *thd, bool all) DBUG_RETURN(error); } - struct xahton_st { XID *xid; int result; + int ordered; }; +/* + Commit an engine branch of XA. The function serves for XA completion + after disconnect. + It can be the ordered commit (note OC label below) e.g when requested + form binlog group commit provided the particiant is capable of that, + or it can be a "general" commit-by-xid of the ordered-commit uncapable + particiants (note !OC label) or in the binlog-off server (!B label). +*/ static bool xacommit_handlerton(THD *, transaction_participant *hton, void *arg) { - if (hton->recover) + if (hton == &binlog_tp || !hton->commit_by_xid) + return FALSE; + + int ordered= ((struct xahton_st *)arg)->ordered; + if ((ordered == 0) /* !B */ || + (ordered == 1 && hton->commit_ordered) /* OC */ || + (ordered == -1 && !hton->commit_ordered)) /* !OC */ { + // In case ordered is 1, it's assumed commit_by_xid is has + // the ordered commit property. hton->commit_by_xid(((struct xahton_st *)arg)->xid); ((struct xahton_st *)arg)->result= 0; } return FALSE; } -static bool xarollback_handlerton(THD *, transaction_participant *hton, void *arg) +/* + The rollback XA recovery similar to the commit one. +*/ +bool xarollback_handlerton(THD *, transaction_participant *hton, void *arg) { - if (hton->recover) + if (hton == &binlog_tp || !hton->rollback_by_xid) + return FALSE; + + int ordered= ((struct xahton_st *)arg)->ordered; + if ((ordered == 0) || + (ordered == 1 && hton->commit_ordered) || + (ordered == -1 && !hton->commit_ordered)) { + // In case ordered is 1, it's assumed rollback_by_xid has + // the ordered ~commit~ rollback propery. hton->rollback_by_xid(((struct xahton_st *)arg)->xid); ((struct xahton_st *)arg)->result= 0; } return FALSE; } +/** + Completes xa transaction in engines upon xa or server recovery. + + @param xid pointer to a XID object + @param is_commit which of xa- commit or rollback to execute + @param ordered 1 means the commit/rollback + operation must be handled by tp::commit_ordered, + -1 means skip any tp::commit_ordered participant, + 0 means execute "complete"_by_xid regardless. + @return zero as success or error code as failure +*/ +int commit_or_rollback_xa_engine(XID *xid, bool is_commit, int ordered) +{ + struct xahton_st xaop= { xid, 1, ordered }; + tp_foreach(NULL, is_commit ? xacommit_handlerton : xarollback_handlerton, + &xaop); + + return xaop.result; +} -int ha_commit_or_rollback_by_xid(XID *xid, bool commit) +/* + The function completes a prepared XA transaction. + It consists of two disjoint branches. + When the transaction has a binlog branch the completion must be logged + before the Engine action. The binlog-enabled path executes the engine + actions as part of binlog-group-ordered commit. + When binlog is disabled on slave the completion has to wait for its + turn to commit/rollback. +*/ +int ha_commit_or_rollback_by_xid(XID *xid, bool is_commit, THD *thd) { - struct xahton_st xaop; - xaop.xid= xid; - xaop.result= 1; + int rc= 0; + bool skip_binlog= + thd->is_current_stmt_binlog_disabled() || !is_xap_binlogged(thd); - /* - When the binlogging service is enabled complete the transaction - by it first. - */ - if (commit) - binlog_commit_by_xid(xid); + if (!skip_binlog) + { + // when binlog is ON start from its transaction branch + if (is_commit) + binlog_commit_by_xid(xid); + else + binlog_rollback_by_xid(xid); + // order-commit uncapable participants commit (todo: optimize away for normal cases) + rc= commit_or_rollback_xa_engine(xid, is_commit, -1); + } else - binlog_rollback_by_xid(xid); - - tp_foreach(NULL, commit ? xacommit_handlerton : xarollback_handlerton, &xaop); + { + int rc= !thd->rgi_slave ? 0 : thd->wait_for_prior_commit(); + if (!rc) + { + rc= commit_or_rollback_xa_engine(xid, is_commit, 0); + } + } - return xaop.result; + return rc; } @@ -2834,7 +2942,12 @@ static bool xarecover_handlerton(THD *, transaction_participant *hton, void *arg char buf[XIDDATASIZE*4+6]; _db_doprnt_("ignore xid %s", xid_to_str(buf, info->list[i])); }); - xid_cache_insert(info->list + i); + /* + TODO: the xa crash recovery is not ready yet. The passed `true` + parameter value assumes the xid is found in binlog + which is of course "optimistic". + */ + xid_cache_insert(info->list + i, true); info->found_foreign_xids++; continue; } diff --git a/sql/handler.h b/sql/handler.h index 9081e8350b04f..e6453129abe3c 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -5715,7 +5715,8 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache); /* transactions: interface to handlerton functions */ int ha_start_consistent_snapshot(THD *thd); -int ha_commit_or_rollback_by_xid(XID *xid, bool commit); +int ha_commit_or_rollback_by_xid(XID *xid, bool commit, THD *thd); +int commit_or_rollback_xa_engine(XID *xid, bool is_commit, int ordered); int ha_commit_one_phase(THD *thd, bool all); int ha_commit_trans(THD *thd, bool all); int ha_rollback_trans(THD *thd, bool all); diff --git a/sql/log.cc b/sql/log.cc index 37139d239fb43..b46cfce3cc2e6 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1996,9 +1996,26 @@ inline size_t serialize_with_xid(XID *xid, char *buf, q_len + strlen(static_cast(xid)->serialize(buf + q_len)); } +static inline int write_xa_end(THD *thd, XID *xid, binlog_cache_mngr *cache_mngr) +{ + const char query[]= "XA END "; + const size_t q_len= sizeof(query) - 1; + char buf[q_len + ser_buf_size]; + size_t buflen; + binlog_cache_data *cache_data= cache_mngr->get_binlog_cache_data(true); + IO_CACHE *file= &cache_data->cache_log; + + memcpy(buf, query, q_len); + buflen= + q_len + strlen(static_cast(xid)->serialize(buf + q_len)); + cache_data= cache_mngr->get_binlog_cache_data(true); + Query_log_event xa_end(thd, buf, buflen, true, false, true, 0); + + return mysql_bin_log.write_event(&xa_end, cache_data, file); +} /** - This function flushes the trx-cache upon commit. + This function flushes the trx-cache at commit or XA-PREPARE. @param thd The thread whose transaction should be flushed @param cache_mngr Pointer to the cache manager @@ -2012,24 +2029,49 @@ binlog_commit_flush_trx_cache(THD *thd, bool all, binlog_cache_mngr *cache_mngr, { DBUG_ENTER("binlog_commit_flush_trx_cache"); - const char query[]= "XA COMMIT "; - const size_t q_len= sizeof(query) - 1; // do not count trailing 0 - char buf[q_len + ser_buf_size]= "COMMIT"; - size_t buflen= sizeof("COMMIT") - 1; - - if (thd->lex->sql_command == SQLCOM_XA_COMMIT && - thd->lex->xa_opt != XA_ONE_PHASE) + if (thd->lex->sql_command == SQLCOM_XA_PREPARE) { - DBUG_ASSERT(thd->transaction->xid_state.is_explicit_XA()); - DBUG_ASSERT(thd->transaction->xid_state.get_state_code() == - XA_PREPARED); + XID *xid= thd->transaction->xid_state.get_xid(); + /* + Log the XA END event first. + We don't do that in trans_xa_end() as XA COMMIT ONE PHASE + is logged as simple BEGIN/COMMIT so the XA END should + not get to the log. + */ + if (write_xa_end(thd, xid, cache_mngr)) + DBUG_RETURN(1); - buflen= serialize_with_xid(thd->transaction->xid_state.get_xid(), - buf, query, q_len); + cache_mngr->using_xa= FALSE; // need not engine commit ordered + XA_prepare_log_event end_evt(thd, xid, FALSE); + + DBUG_RETURN(binlog_flush_cache(thd, cache_mngr, &end_evt, all, + FALSE, TRUE, ro_1pc)); } - Query_log_event end_evt(thd, buf, buflen, TRUE, TRUE, TRUE, 0); + else + { + const char query[]= "XA COMMIT "; + const size_t q_len= sizeof(query) - 1; // do not count trailing 0 + char buf[q_len + ser_buf_size]= "COMMIT"; + size_t buflen= sizeof("COMMIT") - 1; + bool using_stmt_cache= true; - DBUG_RETURN(binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE, ro_1pc)); + if (thd->lex->sql_command == SQLCOM_XA_COMMIT && + thd->lex->xa_opt != XA_ONE_PHASE) + { + DBUG_ASSERT(thd->transaction->xid_state.is_explicit_XA()); + DBUG_ASSERT(thd->transaction->xid_state.get_state_code() == + XA_PREPARED); + + buflen= serialize_with_xid(thd->transaction->xid_state.get_xid(), + buf, query, q_len); + cache_mngr->using_xa= TRUE; // to take part in group commit + using_stmt_cache= false; + } + Query_log_event end_evt(thd, buf, buflen, TRUE, TRUE, TRUE, 0); + + DBUG_RETURN(binlog_flush_cache(thd, cache_mngr, &end_evt, all, + using_stmt_cache, TRUE, ro_1pc)); + } } @@ -2055,8 +2097,11 @@ binlog_rollback_flush_trx_cache(THD *thd, bool all, { /* for not prepared use plain ROLLBACK */ if (thd->transaction->xid_state.get_state_code() == XA_PREPARED) + { buflen= serialize_with_xid(thd->transaction->xid_state.get_xid(), buf, query, q_len); + cache_mngr->using_xa= TRUE; + } } Query_log_event end_evt(thd, buf, buflen, TRUE, TRUE, TRUE, 0); @@ -2158,26 +2203,38 @@ inline bool is_preparing_xa(THD *thd) } +/** + Does nothing in the normal transaction case. + In the user XA the image of the prepared transaction is binlogged. + @retval + 0 as normal + non-zero as an error +*/ static int binlog_prepare(THD *thd, bool all) { /* Do nothing unless the transaction is a user XA. */ return is_preparing_xa(thd) ? binlog_commit(thd, all, FALSE) : 0; } +/* + Log an xa completion event from an exteral connection. + For that an associated with the XA-"complete" "subtransaction" binlog branch + needs registering. It's empty but binlog_"complete"() honor its presence + to produce the aimed binlog event. -int binlog_commit_by_xid(XID *xid) + The function is invoked only when its prepared state has been logged. +*/ +inline int binlog_complete_by_xid(XID *xid, bool is_commit) { int rc= 0; THD *thd= current_thd; - if (thd->is_current_stmt_binlog_disabled()) - { - return thd->wait_for_prior_commit(); - } + DBUG_ASSERT(is_xap_binlogged(thd)); /* the asserted state can't be reachable with xa commit */ - DBUG_ASSERT(!thd->get_stmt_da()->is_error() || - thd->get_stmt_da()->sql_errno() != ER_XA_RBROLLBACK); + DBUG_ASSERT(!is_commit || + (!thd->get_stmt_da()->is_error() || + thd->get_stmt_da()->sql_errno() != ER_XA_RBROLLBACK)); /* This is a recovered user xa transaction commit. Create a "temporary" binlog transaction to write the commit record @@ -2188,46 +2245,43 @@ int binlog_commit_by_xid(XID *xid) thd->ha_data[binlog_tp.slot].ha_info[1].register_ha(&trans, &binlog_tp); thd->ha_data[binlog_tp.slot].ha_info[1].set_trx_read_write(); - (void) thd->binlog_setup_trx_data(); - - DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_COMMIT); + binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data(); + cache_mngr->using_xa= TRUE; // just as normal trx take part in group commit - rc= binlog_commit(thd, TRUE, FALSE); + rc= is_commit ? + binlog_commit(thd, TRUE, FALSE) : binlog_rollback(thd, TRUE); thd->ha_data[binlog_tp.slot].ha_info[1].reset(); return rc; } - -int binlog_rollback_by_xid(XID *xid) +int binlog_commit_by_xid(XID *xid) { - int rc= 0; THD *thd= current_thd; + DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_COMMIT); + if (thd->is_current_stmt_binlog_disabled()) { return thd->wait_for_prior_commit(); } + return binlog_complete_by_xid(xid, true); +} - if (thd->get_stmt_da()->is_error() && - thd->get_stmt_da()->sql_errno() == ER_XA_RBROLLBACK) - return rc; - - THD_TRANS trans; - trans.ha_list= NULL; - - thd->ha_data[binlog_tp.slot].ha_info[1].register_ha(&trans, &binlog_tp); - thd->ha_data[binlog_tp.slot].ha_info[1].set_trx_read_write(); - (void) thd->binlog_setup_trx_data(); +int binlog_rollback_by_xid(XID *xid) +{ + int rc= 0; + THD *thd= current_thd; DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_ROLLBACK || (thd->transaction->xid_state.get_state_code() == XA_ROLLBACK_ONLY)); - rc= binlog_rollback(thd, TRUE); - thd->ha_data[binlog_tp.slot].ha_info[1].reset(); - return rc; -} + if (thd->get_stmt_da()->is_error() && + thd->get_stmt_da()->sql_errno() == ER_XA_RBROLLBACK) + return rc; + return binlog_complete_by_xid(xid, false); +} inline bool is_prepared_xa(THD *thd) { @@ -2235,6 +2289,11 @@ inline bool is_prepared_xa(THD *thd) thd->transaction->xid_state.get_state_code() == XA_PREPARED; } +inline bool is_prepared_logged_xa(THD *thd) +{ + return is_prepared_xa(thd) && is_xap_binlogged(thd); +} + /* We flush the cache wrapped in a beginning/rollback if: @@ -2262,57 +2321,23 @@ static bool trans_cannot_safely_rollback(THD *thd, bool all) is_prepared_xa(thd)); } - -/** - Specific log flusher invoked through log_xa_prepare(). -*/ -static int binlog_commit_flush_xa_prepare(THD *thd, bool all, - binlog_cache_mngr *cache_mngr) +#ifndef DBUG_OFF +inline bool is_binlog_read_write(THD *thd) { - XID *xid= thd->transaction->xid_state.get_xid(); - { - // todo assert wsrep_simulate || is_open() - - /* - Log the XA END event first. - We don't do that in trans_xa_end() as XA COMMIT ONE PHASE - is logged as simple BEGIN/COMMIT so the XA END should - not get to the log. - */ - const char query[]= "XA END "; - const size_t q_len= sizeof(query) - 1; // do not count trailing 0 - char buf[q_len + ser_buf_size]; - size_t buflen; - binlog_cache_data *cache_data; - IO_CACHE *file; - - memcpy(buf, query, q_len); - buflen= q_len + - strlen(static_cast(xid)->serialize(buf + q_len)); - cache_data= cache_mngr->get_binlog_cache_data(true); - file= &cache_data->cache_log; - thd->lex->sql_command= SQLCOM_XA_END; - Query_log_event xa_end(thd, buf, buflen, true, false, true, 0); - if (mysql_bin_log.write_event(&xa_end, cache_data, file)) - return 1; - thd->lex->sql_command= SQLCOM_XA_PREPARE; - } - - cache_mngr->using_xa= FALSE; - XA_prepare_log_event end_evt(thd, xid, FALSE); - - return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE)); + return (thd->ha_data[binlog_tp.slot].ha_info[1].is_started() && + thd->ha_data[binlog_tp.slot].ha_info[1].is_trx_read_write()); } - +#endif /** This function is called once after each statement. - It has the responsibility to flush the caches to the binary log on commits. + It has the responsibility to flush the caches to the binary log on commits + of normal and XA transactions or XA-prepare. @param thd The client thread that executes the transaction. - @param all This is @c true if this is a real transaction commit, and - @false otherwise. + @param all This is @c true if this is a real transaction commit or + XA-prepare, and @false otherwise. @param ro_1pc read-only one-phase commit transaction */ int binlog_commit(THD *thd, bool all, bool ro_1pc) @@ -2368,44 +2393,40 @@ int binlog_commit(THD *thd, bool all, bool ro_1pc) #endif /* WITH_WSREP */ error= binlog_commit_flush_stmt_cache(thd, all, cache_mngr); } - if (cache_mngr->trx_cache.empty() && - (thd->transaction->xid_state.get_state_code() != XA_PREPARED || - !(thd->ha_data[binlog_tp.slot].ha_info[1].is_started() && - thd->ha_data[binlog_tp.slot].ha_info[1].is_trx_read_write()))) + if (cache_mngr->trx_cache.empty() && !is_prepared_logged_xa(thd)) { /* - This is an empty transaction commit (both the regular and xa), - or such transaction xa-prepare or - either one's statement having no effect on the transactional cache - as any prior to it. - The empty xa-prepare sinks in only when binlog is read-only. - */ + This is an empty transaction/statement commit (both the regular and xa), + or the xa transaction is running XA-COMPLETE in which case + its binlog branch must be read-write. + */ cache_mngr->reset(false, true); THD_STAGE_INFO(thd, org_stage); DBUG_RETURN(error); } + else + { + // XA-commmit time empty cache is marked read-write for further logging + DBUG_ASSERT(!is_prepared_xa(thd) || is_binlog_read_write(thd)); + } /* - We commit the transaction if: + We commit the transaction and log its changes if: - We are not in a transaction and committing a statement, or - - We are in a transaction and a full transaction is committed. + - We are in a transaction and a full transaction is committed; + We also log the trx cache of a being prepared XA transaction. Otherwise, we accumulate the changes. */ if (likely(!error) && is_ending_transaction) { - bool is_xa_prepare= is_preparing_xa(thd); - - error= is_xa_prepare ? - binlog_commit_flush_xa_prepare(thd, all, cache_mngr) : - binlog_commit_flush_trx_cache (thd, all, cache_mngr, ro_1pc); - // the user xa is unlogged on common exec path with the "empty" xa case - if (cache_mngr->need_unlog && !is_xa_prepare) - { - error= - mysql_bin_log.unlog(BINLOG_COOKIE_MAKE(cache_mngr->binlog_id, - cache_mngr->delayed_error), 1); - cache_mngr->need_unlog= false; - } + error= binlog_commit_flush_trx_cache(thd, all, cache_mngr, ro_1pc); + if (cache_mngr->need_unlog) + { + error= + mysql_bin_log.unlog(BINLOG_COOKIE_MAKE(cache_mngr->binlog_id, + cache_mngr->delayed_error), 1); + cache_mngr->need_unlog= false; + } } else if (thd->lock) { @@ -2468,17 +2489,17 @@ static int binlog_rollback(THD *thd, bool all) } if (!cache_mngr->trx_cache.has_incident() && cache_mngr->trx_cache.empty() && - (thd->transaction->xid_state.get_state_code() != XA_PREPARED || - !(thd->ha_data[binlog_tp.slot].ha_info[1].is_started() && - thd->ha_data[binlog_tp.slot].ha_info[1].is_trx_read_write()))) + !is_prepared_logged_xa(thd)) { - /* - The same comments apply as in the binlog commit method's branch. - */ + // see a similar brach in the binlog commit cache_mngr->reset(false, true); thd->reset_binlog_for_next_statement(); DBUG_RETURN(error); } + else + { + DBUG_ASSERT(!is_prepared_xa(thd) || is_binlog_read_write(thd)); + } if (!wsrep_emulate_bin_log && Event_log::check_write_error(thd)) { /* @@ -2486,8 +2507,9 @@ static int binlog_rollback(THD *thd, bool all) this function was called. However, this must not happen as a rollback is written directly to the binary log. And in auto-commit mode, a single statement that is rolled back has the flag all == false. + An errored "all == true" XA-PREPARE can turn in this block though. */ - DBUG_ASSERT(!all); + DBUG_ASSERT(!all || is_preparing_xa(thd)); /* We reach this point if the effect of a statement did not properly get into a cache and need to be rolled back. @@ -8512,7 +8534,7 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd, entry.all= all; entry.using_stmt_cache= using_stmt_cache; entry.using_trx_cache= using_trx_cache; - entry.need_unlog= is_preparing_xa(thd); + entry.need_unlog= 0; ha_info= all ? thd->transaction->all.ha_list : thd->transaction->stmt.ha_list; entry.ro_1pc= is_ro_1pc; entry.end_event= end_ev; @@ -9265,6 +9287,15 @@ void MYSQL_BIN_LOG::trx_group_commit_with_engines(group_commit_entry *leader, any_error= true; } #endif + + // mark the user xid to indicate XA- prepare and "complete" logged + XID_STATE *xid_state= ¤t->thd->transaction->xid_state; + if (is_preparing_xa(current->thd) || is_prepared_xa(current->thd)) + { + xid_cache_update_xa_binlog_state( + current->thd, xid_state, + (current->thd->lex->sql_command == SQLCOM_XA_PREPARE)); + } } /* @@ -10251,15 +10282,76 @@ TC_LOG::run_prepare_ordered(THD *thd, bool all) } } +/* + The user XA completion in group commit. + The completion - XA-COMMIT or XA-ROLLBACK goes through one of two braches, + depending on which of native or external connection the completion is issued. +*/ +inline void run_xa_complete_ordered(THD *thd) +{ + Ha_trx_info *ha_info= thd->transaction->all.ha_list; + + if (!ha_info) + { + // slave or master's xa-"complete" external + DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_COMMIT || + thd->lex->sql_command == SQLCOM_XA_ROLLBACK); + // the prepare time flag is dropped by the completion's binlog write + DBUG_ASSERT(!is_xap_binlogged(thd)); + + XID *xid= thd->transaction->xid_state.get_xid(); + bool is_commit= thd->lex->sql_command == SQLCOM_XA_COMMIT; + commit_or_rollback_xa_engine(xid, is_commit, 1); + return; + } + for (; ha_info; ha_info= ha_info->next()) + { + DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_COMMIT || + (thd->lex->sql_command == SQLCOM_XA_ROLLBACK || + /* MDEV-32455, MDEV-36799 */ + thd->get_stmt_da()->get_sql_errno() == ER_XAER_RMFAIL)); + + transaction_participant *ht= ha_info->ht(); + if (ht == &binlog_tp || !ht->commit_ordered) + continue; + if (thd->lex->sql_command == SQLCOM_XA_COMMIT) + { + ht->commit_ordered(thd, true); + } + else + { + ht->rollback(thd, true); + } + } +} + +/* + The function executes the fast part of ordered commit in engine branches + for normal and XA transactions, as well as the entirety of XA rollback. +*/ void TC_LOG::run_commit_ordered(THD *thd, bool all) { - Ha_trx_info *ha_info= - all ? thd->transaction->all.ha_list : thd->transaction->stmt.ha_list; - mysql_mutex_assert_owner(&LOCK_commit_ordered); - for (; ha_info; ha_info= ha_info->next()) + + if (thd->transaction->xid_state.is_explicit_XA() && + likely(thd->transaction->xid_state.get_state_code() != XA_ACTIVE)) + { + DBUG_ASSERT(all); + run_xa_complete_ordered(thd); + + return; + } + else + { + DBUG_ASSERT(!thd->transaction->xid_state.is_explicit_XA() || + thd->transaction->xid_state.get_error() > 0); + } + + for (Ha_trx_info *ha_info= all ? + thd->transaction->all.ha_list : thd->transaction->stmt.ha_list; + ha_info; ha_info= ha_info->next()) { transaction_participant *ht= ha_info->ht(); if (!ht->commit_ordered) @@ -10274,7 +10366,6 @@ TC_LOG::run_commit_ordered(THD *thd, bool all) } } - int TC_LOG_MMAP::log_and_order(THD *thd, my_xid xid, bool all, bool need_prepare_ordered, bool need_commit_ordered) @@ -11390,49 +11481,6 @@ int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid) DBUG_RETURN(BINLOG_COOKIE_GET_ERROR_FLAG(cookie)); } -static bool write_empty_xa_prepare(THD *thd, binlog_cache_mngr *cache_mngr) -{ - return binlog_commit_flush_xa_prepare(thd, true, cache_mngr); -} - -int TC_LOG_BINLOG::unlog_xa_prepare(THD *thd, bool all) -{ - DBUG_ASSERT(is_preparing_xa(thd)); - - binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data(); - int cookie= 0; - - if (!cache_mngr->need_unlog) - { - Ha_trx_info *ha_info; - uint rw_count= ha_count_rw_all(thd, &ha_info); - bool rc= false; - - /* - This transaction has not been binlogged as indicated by need_unlog. - Such exceptional cases include transactions with no effect to engines, - e.g REPLACE that does not change the dat but still the Engine - transaction branch claims to be rw, and few more. - In all such cases an empty XA-prepare group of events is bin-logged. - */ - if (rw_count > 0) - { - /* an empty XA-prepare event group is logged */ - rc= write_empty_xa_prepare(thd, cache_mngr); // normally gains need_unlog - trans_register_ha(thd, true, &binlog_tp, 0); // do it for future commmit - thd->ha_data[binlog_tp.slot].ha_info[1].set_trx_read_write(); - } - if (rw_count == 0 || !cache_mngr->need_unlog) - return rc; - } - - cookie= BINLOG_COOKIE_MAKE(cache_mngr->binlog_id, cache_mngr->delayed_error); - cache_mngr->need_unlog= false; - - return unlog(cookie, 1); -} - - void TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie) { diff --git a/sql/log.h b/sql/log.h index dc95f33de63cd..849bac4351c50 100644 --- a/sql/log.h +++ b/sql/log.h @@ -62,7 +62,6 @@ class TC_LOG bool need_prepare_ordered, bool need_commit_ordered) = 0; virtual int unlog(ulong cookie, my_xid xid)=0; - virtual int unlog_xa_prepare(THD *thd, bool all)= 0; virtual void commit_checkpoint_notify(void *cookie)= 0; protected: @@ -117,10 +116,6 @@ class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging return 1; } int unlog(ulong cookie, my_xid xid) override { return 0; } - int unlog_xa_prepare(THD *thd, bool all) override - { - return 0; - } void commit_checkpoint_notify(void *cookie) override { DBUG_ASSERT(0); }; }; @@ -204,10 +199,6 @@ class TC_LOG_MMAP: public TC_LOG int log_and_order(THD *thd, my_xid xid, bool all, bool need_prepare_ordered, bool need_commit_ordered) override; int unlog(ulong cookie, my_xid xid) override; - int unlog_xa_prepare(THD *thd, bool all) override - { - return 0; - } void commit_checkpoint_notify(void *cookie) override; int recover(); @@ -921,7 +912,6 @@ class MYSQL_BIN_LOG: public TC_LOG, private Event_log int log_and_order(THD *thd, my_xid xid, bool all, bool need_prepare_ordered, bool need_commit_ordered) override; int unlog(ulong cookie, my_xid xid) override; - int unlog_xa_prepare(THD *thd, bool all) override; void commit_checkpoint_notify(void *cookie) override; int recover(LOG_INFO *linfo, const char *last_log_name, IO_CACHE *first_log, Format_description_log_event *fdle, bool do_xa); diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc index e7e3ece27170b..f67f7f6d9667c 100644 --- a/sql/log_event_server.cc +++ b/sql/log_event_server.cc @@ -1467,7 +1467,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, case SQLCOM_RELEASE_SAVEPOINT: case SQLCOM_ROLLBACK_TO_SAVEPOINT: case SQLCOM_SAVEPOINT: - case SQLCOM_XA_END: + case SQLCOM_XA_PREPARE: use_cache= trx_cache= TRUE; break; default: @@ -2907,7 +2907,9 @@ Gtid_log_event::Gtid_log_event(THD *thd_arg, uint64 seq_no_arg, } else if (thd->lex->sql_command == SQLCOM_XA_PREPARE) { - DBUG_ASSERT(thd_arg->in_multi_stmt_transaction_mode()); + /* destined to rollback xa has already reset the multi-stmt feature */ + DBUG_ASSERT(thd->in_multi_stmt_transaction_mode() || + xid_state.get_state_code() == XA_ROLLBACK_ONLY); uint8 count= ha_count_rw_2pc(thd_arg, true); extra_engines= count > 1 ? 0 : UCHAR_MAX; diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index d82d75eb36e5e..3535810e3b5d3 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -2537,6 +2537,10 @@ rpl_group_info::unmark_start_commit() if (!did_mark_start_commit) return; did_mark_start_commit= false; + /* MDEV-36826 read-only xa tried unmarking for no reason */ + DBUG_ASSERT(!(thd->transaction->xid_state.is_explicit_XA() && + thd->transaction->xid_state.get_state_code() == + XA_ROLLBACK_ONLY)); e= this->parallel_entry; mysql_mutex_lock(&e->LOCK_parallel_entry); diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index ee13a1c501ae3..82f8179d4d534 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -12328,3 +12328,6 @@ ER_WARN_MALFORMED_HINT eng "Hint %s is ignored as malformed" ER_WARN_HINTS_ON_INSERT_PART_OF_INSERT_SELECT eng "Optimizer hints at the INSERT part of a INSERT..SELECT statement are not supported" +ER_XA_RDONLY + eng "Found to be read-only XA transaction is rolled back" + diff --git a/sql/xa.cc b/sql/xa.cc index 951232c646c1d..7b7d3e1d04b49 100644 --- a/sql/xa.cc +++ b/sql/xa.cc @@ -22,6 +22,10 @@ #include "my_cpu.h" #include #include +#ifndef DBUG_OFF +#include "rpl_rli.h" // rpl_group_info +#endif +#include "debug_sync.h" // DEBUG_SYNC static bool slave_applier_reset_xa_trans(THD *thd); @@ -79,6 +83,13 @@ class XID_cache_element uint rm_error; enum xa_states xa_state; XA_data xid; + /* + The flag memorizes the fact of the prepared XA has been binlogged. + It gets dropped with binlogging of the transaction's XA-COMPLETE. + FIXME: alternatively, could we use bit 32 of m_state or, + TODO: better, XID::formatID highest bit (also required by MDEV-21777). + */ + bool xap_binlogged_awaiting_xac; bool is_set(int32_t flag) { return m_state.load(std::memory_order_relaxed) & flag; } void set(int32_t flag) @@ -106,6 +117,7 @@ class XID_cache_element old&= ACQUIRED | RECOVERED; (void) LF_BACKOFF(); } + xap_binlogged_awaiting_xac= FALSE; } void acquired_to_recovered() { @@ -126,6 +138,16 @@ class XID_cache_element } return true; } + void notify_xap_binlogged() + { + xap_binlogged_awaiting_xac= TRUE; + } + void notify_xac_binlogged() + { + DBUG_ASSERT(opt_bin_log); + + xap_binlogged_awaiting_xac= FALSE; + } static void lf_hash_initializer(LF_HASH *, void *el, const void *ie) { XID_cache_element *element= static_cast(el); @@ -135,6 +157,7 @@ class XID_cache_element element->rm_error= 0; element->xa_state= new_element->xa_state; element->xid.set(new_element->xid); + element->xap_binlogged_awaiting_xac= FALSE; new_element->xid_cache_element= element; } static void lf_alloc_constructor(uchar *ptr) @@ -194,6 +217,12 @@ void XID_STATE::set_rollback_only() MYSQL_SET_TRANSACTION_XA_STATE(current_thd->m_transaction_psi, XA_ROLLBACK_ONLY); } +#ifndef DBUG_OFF +uint XID_STATE::get_error() +{ + return is_explicit_XA() ? xid_cache_element->rm_error : 0; +} +#endif void XID_STATE::er_xaer_rmfail() const { @@ -278,8 +307,7 @@ static XID_cache_element *xid_cache_search(THD *thd, XID *xid) return element; } - -bool xid_cache_insert(XID *xid) +bool xid_cache_insert(XID *xid, bool is_binlogged) { XID_cache_insert_element new_element(XA_PREPARED, xid); LF_PINS *pins; @@ -292,6 +320,8 @@ bool xid_cache_insert(XID *xid) { case 0: new_element.xid_cache_element->set(XID_cache_element::RECOVERED); + if (is_binlogged) + new_element.xid_cache_element->notify_xap_binlogged(); break; case 1: res= 0; @@ -334,10 +364,45 @@ static void xid_cache_delete(THD *thd, XID_cache_element *&element) void xid_cache_delete(THD *thd, XID_STATE *xid_state) { DBUG_ASSERT(xid_state->is_explicit_XA()); + xid_cache_delete(thd, xid_state->xid_cache_element); xid_state->xid_cache_element= 0; } +void xid_cache_delete(THD *thd) +{ + xid_cache_delete(thd, &thd->transaction->xid_state); +} + + +void xid_cache_update_xa_binlog_state(THD *thd, XID_STATE *xid_state, + bool is_xap) +{ + DBUG_ASSERT(xid_state->is_explicit_XA()); + if (is_xap) + { + DBUG_ASSERT(!xid_state->xid_cache_element->xap_binlogged_awaiting_xac); + + xid_state->xid_cache_element->notify_xap_binlogged(); + } + else + { + /* + The flag may not be set by ineffective XA-PREPARE groups + that logged on master in few exceptional cases. + */ + DBUG_ASSERT(xid_state->xid_cache_element->xap_binlogged_awaiting_xac || + thd->transaction->all.ha_list == 0); + + xid_state->xid_cache_element->notify_xac_binlogged(); + } +} + +bool is_xap_binlogged(THD *thd) +{ + return + thd->transaction->xid_state.xid_cache_element->xap_binlogged_awaiting_xac; +} struct xid_cache_iterate_arg { @@ -570,6 +635,15 @@ bool trans_xa_prepare(THD *thd) my_error(ER_XAER_NOTA, MYF(0)); else { +#ifdef ENABLED_DEBUG_SYNC + DBUG_EXECUTE_IF( + "stop_before_binlog_prepare", + if (thd->rgi_slave->current_gtid.seq_no % 100 == 0) + { + DBUG_ASSERT(!debug_sync_set_action( + thd, STRING_WITH_LEN("now WAIT_FOR binlog_xap"))); + };); +#endif MDL_request mdl_request; if (trans_xa_get_backup_lock(thd, &mdl_request) || ha_prepare(thd)) @@ -586,6 +660,8 @@ bool trans_xa_prepare(THD *thd) } else { + DBUG_ASSERT(thd->transaction->xid_state.xid_cache_element); + if (thd->transaction->xid_state.xid_cache_element->xa_state != XA_ROLLBACK_ONLY) { @@ -595,14 +671,37 @@ bool trans_xa_prepare(THD *thd) else { /* - In the non-err case, XA_ROLLBACK_ONLY should only be set by a slave - thread which prepared an empty transaction, to prevent binlogging a - standalone XA COMMIT. + In the non-err case, XA_ROLLBACK_ONLY should be set for a + read-only transaction, has nothing to binlog. */ - DBUG_ASSERT(thd->rgi_slave && !(thd->transaction->all.ha_list)); +#ifndef DBUG_OFF + bool is_rw; + Ha_trx_info *ha_info= thd->transaction->all.ha_list; + for (is_rw= false; ha_info; ha_info= ha_info->next()) + { + transaction_participant *ht= ha_info->ht(); + if (ht == &binlog_tp || !ht->prepare) + continue; + is_rw= is_rw || ha_info->is_trx_read_write(); + } + DBUG_ASSERT((thd->rgi_slave && !(thd->transaction->all.ha_list)) || + !is_rw); +#endif } res= thd->variables.pseudo_slave_mode || thd->slave_thread ? slave_applier_reset_xa_trans(thd) : 0; +#ifdef ENABLED_DEBUG_SYNC + DBUG_EXECUTE_IF( + "stop_after_binlog_prepare", + if (thd->rgi_slave->current_gtid.seq_no % 100 == 0) + { + DBUG_ASSERT(!debug_sync_set_action( + thd, + STRING_WITH_LEN( + "now SIGNAL xa_prepare_binlogged WAIT_FOR continue_xap"))); + };); +#endif + } trans_xa_release_backup_lock(thd); } @@ -612,99 +711,118 @@ bool trans_xa_prepare(THD *thd) /** - Commit and terminate the a XA transaction. - Transactional locks are released if transaction ended + Commit/Rollback a prepared XA transaction through "external" connection. - @param thd Current thread + @param thd Current "external" connection thread + @bool do_commit true for Commit, false for Rollback @retval FALSE Success @retval TRUE Failure - */ -bool trans_xa_commit(THD *thd) +static bool xa_complete(THD *thd, bool do_commit) { - bool res= true; XID_STATE &xid_state= thd->transaction->xid_state; - DBUG_ENTER("trans_xa_commit"); + DBUG_ENTER("xa_complete"); - if (!xid_state.is_explicit_XA() || - !xid_state.xid_cache_element->xid.eq(thd->lex->xid)) + if (thd->in_multi_stmt_transaction_mode()) + { + /* + Not allow to commit from inside an not-"native" to xid + ongoing transaction: the commit effect can't be reversed. + */ + my_error(ER_XAER_OUTSIDE, MYF(0)); + DBUG_RETURN(TRUE); + } + if (do_commit && thd->lex->xa_opt != XA_NONE) + { + /* + Not allow to commit with one phase a prepared xa out of compatibility + with the native commit branch's error out. + */ + my_error(ER_XAER_INVAL, MYF(0)); + DBUG_RETURN(TRUE); + } + if (thd->fix_xid_hash_pins()) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + DBUG_RETURN(TRUE); + } + + if (auto xs= xid_cache_search(thd, thd->lex->xid)) { - if (thd->in_multi_stmt_transaction_mode()) + bool res; + MDL_request mdl_request; + bool rw_trans= (xs->rm_error != ER_XA_RBROLLBACK); + + if (rw_trans && thd->check_read_only_with_error()) { - /* - Not allow to commit from inside an not-"native" to xid - ongoing transaction: the commit effect can't be reversed. - */ - my_error(ER_XAER_OUTSIDE, MYF(0)); - DBUG_RETURN(TRUE); + DBUG_ASSERT(thd->is_error()); + + goto _end_external_xid; } - if (thd->lex->xa_opt != XA_NONE) + + res= xa_trans_rolled_back(xs); + if (trans_xa_get_backup_lock(thd, &mdl_request)) { /* - Not allow to commit with one phase a prepared xa out of compatibility - with the native commit branch's error out. + We can't rollback an XA transaction on lock failure due to + Innodb redo log and bin log update is involved in rollback. + Return error to user for a retry. */ - my_error(ER_XAER_INVAL, MYF(0)); - DBUG_RETURN(TRUE); + DBUG_ASSERT(thd->is_error()); + + goto _end_external_xid; } - if (thd->fix_xid_hash_pins()) + + DBUG_ASSERT(!xid_state.xid_cache_element); + + xid_state.xid_cache_element= xs; + ha_commit_or_rollback_by_xid(&xs->xid, do_commit ? !res : 0, thd); + + if (!res && thd->is_error()) { - my_error(ER_OUT_OF_RESOURCES, MYF(0)); - DBUG_RETURN(TRUE); + // hton completion error retains xs/xid in the cache, + // unless there had been already one as reflected by `res`. + goto _end_external_xid; } + xid_cache_delete(thd, &xid_state); - if (auto xs= xid_cache_search(thd, thd->lex->xid)) - { - bool xid_deleted= false; - MDL_request mdl_request; - bool rw_trans= (xs->rm_error != ER_XA_RBROLLBACK); + xs= NULL; - if (rw_trans && thd->check_read_only_with_error()) - { - res= 1; - goto _end_external_xid; - } - res= xa_trans_rolled_back(xs); - if (trans_xa_get_backup_lock(thd, &mdl_request)) - { - /* - We can't rollback an XA transaction on lock failure due to - Innodb redo log and bin log update is involved in rollback. - Return error to user for a retry. - */ - DBUG_ASSERT(thd->is_error()); +_end_external_xid: + if (xs) + xs->acquired_to_recovered(); + xid_state.xid_cache_element= 0; + trans_xa_release_backup_lock(thd); + } + else + my_error(ER_XAER_NOTA, MYF(0)); + DBUG_RETURN(thd->get_stmt_da()->is_error()); +} - res= true; - goto _end_external_xid; - } - DBUG_ASSERT(!xid_state.xid_cache_element); +/** + Commit and terminate the a XA transaction. + Transactional locks are released if transaction ended - xid_state.xid_cache_element= xs; - ha_commit_or_rollback_by_xid(&xs->xid, !res); - if (!res && thd->is_error()) - { - // hton completion error retains xs/xid in the cache, - // unless there had been already one as reflected by `res`. - res= true; - goto _end_external_xid; - } - xid_cache_delete(thd, xs); - xid_deleted= true; - - _end_external_xid: - xid_state.xid_cache_element= 0; - res= res || thd->is_error(); - if (!xid_deleted) - xs->acquired_to_recovered(); - trans_xa_release_backup_lock(thd); - } - else - my_error(ER_XAER_NOTA, MYF(0)); - DBUG_RETURN(res); - } + @param thd Current thread + + @retval FALSE Success + @retval TRUE Failure + +*/ + +bool trans_xa_commit(THD *thd) +{ + XID_STATE &xid_state= thd->transaction->xid_state; + bool res; + + DBUG_ENTER("trans_xa_commit"); + + if (!xid_state.is_explicit_XA() || + !xid_state.xid_cache_element->xid.eq(thd->lex->xid)) + DBUG_RETURN(xa_complete(thd, true)); if (thd->transaction->all.is_trx_read_write() && thd->check_read_only_with_error()) DBUG_RETURN(TRUE); @@ -802,63 +920,7 @@ bool trans_xa_rollback(THD *thd) if (!xid_state.is_explicit_XA() || !xid_state.xid_cache_element->xid.eq(thd->lex->xid)) - { - if (thd->in_multi_stmt_transaction_mode()) - { - my_error(ER_XAER_OUTSIDE, MYF(0)); - DBUG_RETURN(TRUE); - } - if (thd->fix_xid_hash_pins()) - { - my_error(ER_OUT_OF_RESOURCES, MYF(0)); - DBUG_RETURN(TRUE); - } - - if (auto xs= xid_cache_search(thd, thd->lex->xid)) - { - bool res; - bool xid_deleted= false; - bool rw_trans= (xs->rm_error != ER_XA_RBROLLBACK); - - if (rw_trans && thd->check_read_only_with_error()) - { - res= 1; - goto _end_external_xid; - } - - if (trans_xa_get_backup_lock(thd, &mdl_request)) - { - /* - We can't rollback an XA transaction on lock failure due to - Innodb redo log and bin log update is involved in rollback. - Return error to user for a retry. - */ - DBUG_ASSERT(thd->is_error()); - - goto _end_external_xid; - } - res= xa_trans_rolled_back(xs); - DBUG_ASSERT(!xid_state.xid_cache_element); - - xid_state.xid_cache_element= xs; - ha_commit_or_rollback_by_xid(&xs->xid, 0); - if (!res && thd->is_error()) - { - goto _end_external_xid; - } - xid_cache_delete(thd, xs); - xid_deleted= true; - - _end_external_xid: - xid_state.xid_cache_element= 0; - if (!xid_deleted) - xs->acquired_to_recovered(); - trans_xa_release_backup_lock(thd); - } - else - my_error(ER_XAER_NOTA, MYF(0)); - DBUG_RETURN(thd->get_stmt_da()->is_error()); - } + DBUG_RETURN(xa_complete(thd, false)); if (thd->transaction->all.is_trx_read_write() && thd->check_read_only_with_error()) DBUG_RETURN(TRUE); @@ -891,7 +953,12 @@ bool trans_xa_detach(THD *thd) DBUG_ASSERT(thd->transaction->xid_state.is_explicit_XA()); if (thd->transaction->xid_state.xid_cache_element->xa_state != XA_PREPARED) + { +#ifndef DBUG_OFF + thd->transaction->xid_state.set_error(ER_XA_RBROLLBACK); +#endif return xa_trans_force_rollback(thd); + } else if (!thd->transaction->all.is_trx_read_write()) { thd->transaction->xid_state.set_error(ER_XA_RBROLLBACK); @@ -1151,13 +1218,17 @@ static bool slave_applier_reset_xa_trans(THD *thd) ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY); DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS")); - if (thd->variables.pseudo_slave_mode && - !thd->transaction->all.is_trx_read_write()) + if (thd->transaction->xid_state.xid_cache_element->xa_state != XA_PREPARED) { - thd->transaction->xid_state.set_error(ER_XA_RBROLLBACK); + DBUG_ASSERT(thd->transaction->xid_state.xid_cache_element->xa_state == + XA_ROLLBACK_ONLY); + xa_trans_force_rollback(thd); + } + else + { + thd->transaction->xid_state.xid_cache_element->acquired_to_recovered(); + thd->transaction->xid_state.xid_cache_element= 0; } - thd->transaction->xid_state.xid_cache_element->acquired_to_recovered(); - thd->transaction->xid_state.xid_cache_element= 0; for (Ha_trx_info *ha_info= thd->transaction->all.ha_list, *ha_info_next; ha_info; ha_info= ha_info_next) diff --git a/sql/xa.h b/sql/xa.h index a6732039140d7..87d302eedcae6 100644 --- a/sql/xa.h +++ b/sql/xa.h @@ -39,13 +39,18 @@ struct XID_STATE { void er_xaer_rmfail() const; XID *get_xid() const; enum xa_states get_state_code() const; +#ifndef DBUG_OFF + uint get_error(); +#endif }; void xid_cache_init(void); void xid_cache_free(void); -bool xid_cache_insert(XID *xid); +bool xid_cache_insert(XID *xid, bool is_binlogged= false); bool xid_cache_insert(THD *thd, XID_STATE *xid_state, XID *xid); void xid_cache_delete(THD *thd, XID_STATE *xid_state); +void xid_cache_update_xa_binlog_state(THD *thd, XID_STATE *xid_state, bool is_xap); +bool is_xap_binlogged(THD *thd); bool trans_xa_start(THD *thd); bool trans_xa_end(THD *thd);