diff options
author | Andrei Elkin <andrei.elkin@mariadb.com> | 2020-03-08 21:52:30 +0200 |
---|---|---|
committer | Andrei Elkin <andrei.elkin@mariadb.com> | 2020-03-15 17:04:29 +0200 |
commit | dc9057184296583af86522c12c27204401819c75 (patch) | |
tree | dfa3c5e2ba3b52364d04c0a4c04769f3184eb8cd | |
parent | a467e6755dc28159ee0bef42bea35cffb9b7bacb (diff) | |
download | mariadb-git-bb-10.5-mdev_742.tar.gz |
MDEV-742: read-only, pure myisam, binlog-*, @@skip_log_bin corner casesbb-10.5-mdev_742
(Pushed to 10.5)
Are addressed along the following policies.
Prepared read-only, or on non-transactional engines, or
binlog-* filter in binlog, or skipped to binlog XA:s remains in the xid cache after disconnect.
But their consequent completion with Commit or Rollback differs.
1. The read-only at reconnect marks XID to fail for future
completion with ER_XA_RBROLLBACK.
2. `binlog-*` filtered XA when it changes engine data is regarded
as loggable even when nothing got cached for binlog.
An empty XA-prepare group is recorded. Consequent Commit-or-Rollback
succeeds in the Engine(s) as well as recorded into binlog.
3. The same applies to the non-transactional engine XA.
4. @@skip_log_bin=OFF does not record anything at XA-prepare (obviously),
but the completion event is recorded into binlog to admit
inconsistency with slave.
The following actions are taken by the patch.
At XA-prepare:
when empty binlog cache - don't do anything to binlog if RO,
otherwise write empty XA_prepare (assert(binlog-filter case)).
At Disconnect:
when Prepared && RO (=> no binlogging was done)
set Xid_cache_element::error := ER_XA_RBROLLBACK
*keep* XID in the cache, and rollback the transaction.
At XA-"complete":
Discover the error, if any don't binlog the "complete", return the error to the user.
RO patch review notes.
-rw-r--r-- | mysql-test/main/xa_sync.test | 2 | ||||
-rw-r--r-- | mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc | 2 | ||||
-rw-r--r-- | mysql-test/suite/rpl/r/rpl_xa.result | 178 | ||||
-rw-r--r-- | mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result | 178 | ||||
-rw-r--r-- | mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc | 9 | ||||
-rw-r--r-- | mysql-test/suite/rpl/t/rpl_xa-master.opt | 1 | ||||
-rw-r--r-- | mysql-test/suite/rpl/t/rpl_xa.inc | 283 | ||||
-rw-r--r-- | mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine-master.opt | 1 | ||||
-rw-r--r-- | sql/handler.cc | 23 | ||||
-rw-r--r-- | sql/handler.h | 1 | ||||
-rw-r--r-- | sql/log.cc | 34 | ||||
-rw-r--r-- | sql/xa.cc | 6 |
12 files changed, 708 insertions, 10 deletions
diff --git a/mysql-test/main/xa_sync.test b/mysql-test/main/xa_sync.test index 2fe7337501e..ad1243ce6f8 100644 --- a/mysql-test/main/xa_sync.test +++ b/mysql-test/main/xa_sync.test @@ -38,7 +38,7 @@ while ($i) --echo *** Must have 'xatest' in the list XA RECOVER; # second time yields no error - --error 0 + --error 0,1402 --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 1f6ce713cc9..4a83aa5c282 100644 --- a/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc +++ b/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc @@ -23,6 +23,7 @@ XA RECOVER; --source include/wait_condition.inc # It will conclude now +--error 0,1402 --eval $terminate_with 'trx1$type' --replace_result $conn3_id CONN_ID @@ -32,4 +33,5 @@ XA RECOVER; --source include/wait_condition.inc # It will conclude now +--error 0,1402 --eval $terminate_with 'trx3$type' diff --git a/mysql-test/suite/rpl/r/rpl_xa.result b/mysql-test/suite/rpl/r/rpl_xa.result index 3420f2348e2..ed07edd8fb5 100644 --- a/mysql-test/suite/rpl/r/rpl_xa.result +++ b/mysql-test/suite/rpl/r/rpl_xa.result @@ -44,5 +44,181 @@ connection slave; include/diff_tables.inc [master:t1, slave:t1] include/diff_tables.inc [master:t2, slave:t2] connection master; -drop table t1, t2; +*** At the start of read-only section gtid list is: +flush logs; +show binlog events in 'master-bin.000002' limit 1,1; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000002 # Gtid_list 1 # [0-1-11] +set @query1="select 1"; +set @query2="select count(*) into @s2 from t1"; +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'ro_2'; +select count(*) into @s2 from t1; +xa end 'ro_2'; +xa prepare 'ro_2';; +disconnect master_ro_2; +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'ro_1'; +select 1; +1 +1 +xa end 'ro_1'; +xa prepare 'ro_1';; +disconnect master_ro_1; +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'ro_2'; +select count(*) into @s2 from t1; +xa end 'ro_2'; +xa prepare 'ro_2';; +disconnect master_ro_2; +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'ro_1'; +select 1; +1 +1 +xa end 'ro_1'; +xa prepare 'ro_1';; +disconnect master_ro_1; +*** 2 prepared xa:s must be in the list: +connection master; +xa recover; +formatID gtrid_length bqual_length data +1 4 0 ro_2 +1 4 0 ro_1 +*** 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: +flush logs; +show binlog events in 'master-bin.000003' limit 1,1; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000003 # Gtid_list 1 # [0-1-11] +create database test_ign; +set @@sql_log_bin = 0; +create table test_ign.t (a int) engine=InnoDB; +set @@sql_log_bin = 1; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'rw_no_binlog'; +insert into test_ign.t set a=1; +xa end 'rw_no_binlog'; +xa prepare 'rw_no_binlog';; +disconnect master_rw_no_binlog; +*** rw_no_binlog must be in the list: +connection master; +xa recover; +formatID gtrid_length bqual_length data +1 12 0 rw_no_binlog +*** 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: +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] +connection master; +create table t3 (a int) engine=innodb; +*** the disconnected prepare case +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +set @@binlog_format=statement; +xa start 'rw_binlog_only'; +delete from t3; +xa end 'rw_binlog_only'; +xa prepare 'rw_binlog_only'; +disconnect master_rw_binlog_only; +connection master; +*** rw_binlog_only must be in the list: +xa recover; +formatID gtrid_length bqual_length data +1 14 0 rw_binlog_only +*** Zero must be in the list: +xa recover; +formatID gtrid_length bqual_length data +*** the same connection complete case. +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +set @@binlog_format=statement; +xa start 'rw_binlog_only'; +delete from t3; +xa end 'rw_binlog_only'; +xa prepare 'rw_binlog_only'; +*** rw_binlog_only must be in the list: +xa recover; +formatID gtrid_length bqual_length data +1 14 0 rw_binlog_only +disconnect master_rw_binlog_only; +*** Zero must be in the list: +connection master; +xa recover; +formatID gtrid_length bqual_length data +*** At the end of ineffective in engine section gtid list has 5 more: +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] +create table tm (a int) engine=myisam; +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'rw_myisam'; +insert into tm set a=1; +xa end 'rw_myisam'; +xa prepare 'rw_myisam';; +disconnect master_rw_myisam; +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'rw_myisam'; +insert into tm set a=1; +xa end 'rw_myisam'; +xa prepare 'rw_myisam';; +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: +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] +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +set @@session.sql_log_bin = OFF; +xa start 'skip_binlog'; +insert into t2 values(1); +xa end 'skip_binlog'; +xa prepare 'skip_binlog'; +disconnect master_skip_binlog; +*** skip_binlog must be in the list: +connection master; +xa recover; +formatID gtrid_length bqual_length data +1 11 0 skip_binlog +connection master; +call mtr.add_suppression("Slave: XAER_NOTA: Unknown XID"); +xa rollback 'skip_binlog'; +*** 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: +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-27] +include/save_master_gtid.inc +connection slave; +include/wait_for_slave_sql_error.inc [errno=1397] +set @@global.sql_slave_skip_counter= 1; +include/start_slave.inc +connection master; +drop database test_ign; +drop table t1, t2, t3, tm; include/rpl_end.inc 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 a7ed0f97ea2..3826bf32f32 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 @@ -53,7 +53,183 @@ connection slave; include/diff_tables.inc [master:t1, slave:t1] include/diff_tables.inc [master:t2, slave:t2] connection master; -drop table t1, t2; +*** At the start of read-only section gtid list is: +flush logs; +show binlog events in 'master-bin.000002' limit 1,1; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000002 # Gtid_list 1 # [0-1-11] +set @query1="select 1"; +set @query2="select count(*) into @s2 from t1"; +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'ro_2'; +select count(*) into @s2 from t1; +xa end 'ro_2'; +xa prepare 'ro_2';; +disconnect master_ro_2; +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'ro_1'; +select 1; +1 +1 +xa end 'ro_1'; +xa prepare 'ro_1';; +disconnect master_ro_1; +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'ro_2'; +select count(*) into @s2 from t1; +xa end 'ro_2'; +xa prepare 'ro_2';; +disconnect master_ro_2; +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'ro_1'; +select 1; +1 +1 +xa end 'ro_1'; +xa prepare 'ro_1';; +disconnect master_ro_1; +*** 2 prepared xa:s must be in the list: +connection master; +xa recover; +formatID gtrid_length bqual_length data +1 4 0 ro_2 +1 4 0 ro_1 +*** 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: +flush logs; +show binlog events in 'master-bin.000003' limit 1,1; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000003 # Gtid_list 1 # [0-1-11] +create database test_ign; +set @@sql_log_bin = 0; +create table test_ign.t (a int) engine=InnoDB; +set @@sql_log_bin = 1; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'rw_no_binlog'; +insert into test_ign.t set a=1; +xa end 'rw_no_binlog'; +xa prepare 'rw_no_binlog';; +disconnect master_rw_no_binlog; +*** rw_no_binlog must be in the list: +connection master; +xa recover; +formatID gtrid_length bqual_length data +1 12 0 rw_no_binlog +*** 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: +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] +connection master; +create table t3 (a int) engine=innodb; +*** the disconnected prepare case +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +set @@binlog_format=statement; +xa start 'rw_binlog_only'; +delete from t3; +xa end 'rw_binlog_only'; +xa prepare 'rw_binlog_only'; +disconnect master_rw_binlog_only; +connection master; +*** rw_binlog_only must be in the list: +xa recover; +formatID gtrid_length bqual_length data +1 14 0 rw_binlog_only +*** Zero must be in the list: +xa recover; +formatID gtrid_length bqual_length data +*** the same connection complete case. +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +set @@binlog_format=statement; +xa start 'rw_binlog_only'; +delete from t3; +xa end 'rw_binlog_only'; +xa prepare 'rw_binlog_only'; +*** rw_binlog_only must be in the list: +xa recover; +formatID gtrid_length bqual_length data +1 14 0 rw_binlog_only +disconnect master_rw_binlog_only; +*** Zero must be in the list: +connection master; +xa recover; +formatID gtrid_length bqual_length data +*** At the end of ineffective in engine section gtid list has 5 more: +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] +create table tm (a int) engine=myisam; +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'rw_myisam'; +insert into tm set a=1; +xa end 'rw_myisam'; +xa prepare 'rw_myisam';; +disconnect master_rw_myisam; +connection master; +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +xa start 'rw_myisam'; +insert into tm set a=1; +xa end 'rw_myisam'; +xa prepare 'rw_myisam';; +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: +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] +connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,; +set @@session.sql_log_bin = OFF; +xa start 'skip_binlog'; +insert into t2 values(1); +xa end 'skip_binlog'; +xa prepare 'skip_binlog'; +disconnect master_skip_binlog; +*** skip_binlog must be in the list: +connection master; +xa recover; +formatID gtrid_length bqual_length data +1 11 0 skip_binlog +connection master; +call mtr.add_suppression("Slave: XAER_NOTA: Unknown XID"); +xa rollback 'skip_binlog'; +*** 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: +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-27] +include/save_master_gtid.inc +connection slave; +include/wait_for_slave_sql_error.inc [errno=1397] +set @@global.sql_slave_skip_counter= 1; +include/start_slave.inc +connection master; +drop database test_ign; +drop table t1, t2, t3, tm; connection slave; include/stop_slave.inc SET @@global.gtid_pos_auto_engines=""; diff --git a/mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc b/mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc new file mode 100644 index 00000000000..b823ebf62ee --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc @@ -0,0 +1,9 @@ +# 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 + +--connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,) +--eval xa start '$xid' +--eval $query +--eval xa end '$xid' +--eval xa prepare '$xid'; diff --git a/mysql-test/suite/rpl/t/rpl_xa-master.opt b/mysql-test/suite/rpl/t/rpl_xa-master.opt new file mode 100644 index 00000000000..6794216dc45 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_xa-master.opt @@ -0,0 +1 @@ +--binlog-ignore-db=test_ign diff --git a/mysql-test/suite/rpl/t/rpl_xa.inc b/mysql-test/suite/rpl/t/rpl_xa.inc index f1ba4cf8557..571a79c1acc 100644 --- a/mysql-test/suite/rpl/t/rpl_xa.inc +++ b/mysql-test/suite/rpl/t/rpl_xa.inc @@ -69,5 +69,286 @@ source include/diff_tables.inc; 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. +# +--connection master + +--echo *** At the start of read-only section gtid list is: +flush logs; +--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1) +--source include/show_gtid_list.inc + + +set @query1="select 1"; +set @query2="select count(*) into @s2 from t1"; +--let $ro_cases=2 +--let $db=test + +# No disconnect +--let $p_trx=$ro_cases +while ($p_trx) +{ +--connection master + --let $xid=ro_$p_trx + --let $query=`SELECT @query$p_trx` + --source rpl_create_xa_prepared.inc + --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 + + --disconnect master_$xid + --source include/wait_until_disconnected.inc + + --dec $p_trx +} + + +--let $p_trx=$ro_cases +# With diconnect +while ($p_trx) +{ +--connection master + --let $xid=ro_$p_trx + --let $query=`SELECT @query$p_trx` + --source rpl_create_xa_prepared.inc + + --disconnect master_$xid + --source include/wait_until_disconnected.inc + + --dec $p_trx +} + +--echo *** $ro_cases prepared xa:s must be in the list: +--connection master +xa recover; + +--let $p_trx=$ro_cases +while ($p_trx) +{ + --let $xid=ro_$p_trx + --let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')` + --disable_query_log + --disable_result_log + --error ER_XA_RBROLLBACK + --eval xa $complete '$xid' + --enable_result_log + --enable_query_log + + --dec $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: +flush logs; +--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1) +--source include/show_gtid_list.inc + + +# +# XA logging cases while some of XA resources are read-only +# +# A1. Binlog filter + + +--let $db=test_ign +--eval create database $db +set @@sql_log_bin = 0; +--eval create table $db.t (a int) engine=InnoDB +set @@sql_log_bin = 1; + +--let $xid=rw_no_binlog +--let $query=insert into $db.t set a=1 +--source rpl_create_xa_prepared.inc +--disconnect master_$xid +--source include/wait_until_disconnected.inc + +--echo *** $xid must be in the list: +--connection master +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 + +--echo *** Zero must be in the list: +--connection master +xa recover; +# restore for the following tests +--let $db=test + +--echo *** At the end of --binlog-ignore-db section gtid list has 2 more: +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 +# +connection master; +create table t3 (a int) engine=innodb; + +--echo *** the disconnected prepare case +--let $xid=rw_binlog_only +--let $query=delete from t3 +--connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,) + set @@binlog_format=statement; + # --source rpl_create_xa_prepared.inc + --eval xa start '$xid' + --eval $query + --eval xa end '$xid' + --eval xa prepare '$xid' + + --disconnect master_$xid + --source include/wait_until_disconnected.inc + +connection master; +--echo *** $xid must be in the list: +xa recover; + + --let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')` + --disable_query_log + --disable_result_log + --eval xa $complete '$xid' + --enable_result_log + --enable_query_log + +--echo *** Zero must be in the list: +xa recover; + +--echo *** the same connection complete case. +connection master; + --let $xid=rw_binlog_only + --let $query=delete from t3 +--connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,) + set @@binlog_format=statement; + # --source rpl_create_xa_prepared.inc + --eval xa start '$xid' + --eval $query + --eval xa end '$xid' + --eval xa prepare '$xid' + +--echo *** $xid must be in the list: +xa recover; + +--disable_query_log + --disable_result_log + --eval xa $complete '$xid' + --enable_result_log + --enable_query_log +--disconnect master_$xid +--source include/wait_until_disconnected.inc + +--echo *** Zero must be in the list: +--connection master +xa recover; + +--echo *** At the end of ineffective in engine section gtid list has 5 more: +flush logs; +--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1) +--source include/show_gtid_list.inc + +# +# A3 MyISAM "xa" logs empty XA-prepare group, followed by +# an XA-complete event +create table tm (a int) engine=myisam; + +# No disconnect +--connection master + --let $xid=rw_myisam + --let $query=insert into tm set a=1 + --source rpl_create_xa_prepared.inc + --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 + + --disconnect master_$xid + --source include/wait_until_disconnected.inc + +# With diconnect +--connection master + --source rpl_create_xa_prepared.inc + --disconnect master_$xid + --source include/wait_until_disconnected.inc + +--echo *** $xid prepared must be in the list: +--connection master +xa recover; + + --let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')` + --disable_query_log + --disable_result_log + --eval xa $complete '$xid' + --enable_result_log + --enable_query_log + +--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: +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 but XA-complete will be +# logged despite of that. + +--let $db=test +--let $xid=skip_binlog +--let $query=insert into t2 values(1) +--connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,) +set @@session.sql_log_bin = OFF; +--eval xa start '$xid' + --eval $query +--eval xa end '$xid' +--eval xa prepare '$xid' + +--disconnect master_$xid +--source include/wait_until_disconnected.inc + +--echo *** $xid must be in the list: +--connection master +xa recover; + +--connection master +call mtr.add_suppression("Slave: XAER_NOTA: Unknown XID"); +--eval xa rollback '$xid' + +--echo *** Zero must be in the list: +--connection master +xa recover; + +--echo *** At the end of --binlog-ignore-db section gtid list has 2 more: +flush logs; +--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1) +--source include/show_gtid_list.inc +--source include/save_master_gtid.inc + +# +# Expected error on slave and manual correction +# +--connection slave +let $slave_sql_errno= 1397; # ER_XAER_NOTA +source include/wait_for_slave_sql_error.inc; +set @@global.sql_slave_skip_counter= 1; +--source include/start_slave.inc + connection master; -drop table t1, t2; +--eval drop database test_ign +drop table t1, t2, t3, tm; diff --git a/mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine-master.opt b/mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine-master.opt new file mode 100644 index 00000000000..6794216dc45 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine-master.opt @@ -0,0 +1 @@ +--binlog-ignore-db=test_ign diff --git a/sql/handler.cc b/sql/handler.cc index c5b6e05b448..5964f6cd7c5 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1367,6 +1367,29 @@ int ha_prepare(THD *thd) DBUG_RETURN(error); } +/* + Like ha_check_and_coalesce_trx_read_only to return counted number of + read-write transaction participants limited to two, but works in the 'all' + context. + Also returns the last found rw ha_info through the 2nd argument. +*/ +uint ha_count_rw_all(THD *thd, Ha_trx_info **ptr_ha_info) +{ + unsigned rw_ha_count= 0; + + for (auto ha_info= thd->transaction.all.ha_list; ha_info; + ha_info= ha_info->next()) + { + if (ha_info->is_trx_read_write()) + { + *ptr_ha_info= ha_info; + if (++rw_ha_count > 1) + break; + } + } + return rw_ha_count; +} + /** Check if we can skip the two-phase commit. diff --git a/sql/handler.h b/sql/handler.h index ebddb659977..22592821164 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -5173,4 +5173,5 @@ void print_keydup_error(TABLE *table, KEY *key, myf errflag); int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info); int del_global_table_stat(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table); +uint ha_count_rw_all(THD *thd, Ha_trx_info **ptr_ha_info); #endif /* HANDLER_INCLUDED */ diff --git a/sql/log.cc b/sql/log.cc index 747313f37d3..3e7f3a043c3 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2040,8 +2040,8 @@ static int binlog_rollback_by_xid(handlerton *hton, XID *xid) (void) thd->binlog_setup_trx_data(); - DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_ROLLBACK); - + DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_ROLLBACK || + (thd->transaction.xid_state.get_state_code() == XA_ROLLBACK_ONLY)); return binlog_rollback(hton, thd, TRUE); } @@ -10009,6 +10009,10 @@ 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) { @@ -10017,10 +10021,28 @@ int TC_LOG_BINLOG::unlog_xa_prepare(THD *thd, bool all) binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data(); int cookie= 0; - if (!cache_mngr || !cache_mngr->need_unlog) - return 0; - else - cookie= BINLOG_COOKIE_MAKE(cache_mngr->binlog_id, cache_mngr->delayed_error); + if (!cache_mngr->need_unlog) + { + Ha_trx_info *ha_info; + uint rw_count= ha_count_rw_all(thd, &ha_info); + bool rc= false; + + if (rw_count > 0) + { + /* an empty XA-prepare event group is logged */ +#ifndef DBUG_OFF + for (ha_info= thd->transaction.all.ha_list; rw_count > 1 && ha_info; + ha_info= ha_info->next()) + DBUG_ASSERT(ha_info->ht() != binlog_hton); +#endif + rc= write_empty_xa_prepare(thd, cache_mngr); // normally gains need_unlog + trans_register_ha(thd, true, binlog_hton, 0); // do it for future commmit + } + 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); diff --git a/sql/xa.cc b/sql/xa.cc index 5a9310ad3df..cde6350f38d 100644 --- a/sql/xa.cc +++ b/sql/xa.cc @@ -820,6 +820,12 @@ bool trans_xa_detach(THD *thd) if (thd->transaction.xid_state.xid_cache_element->xa_state != XA_PREPARED) return xa_trans_force_rollback(thd); + else if (!thd->transaction.all.is_trx_read_write()) + { + thd->transaction.xid_state.set_error(ER_XA_RBROLLBACK); + ha_rollback_trans(thd, true); + } + thd->transaction.xid_state.xid_cache_element->acquired_to_recovered(); thd->transaction.xid_state.xid_cache_element= 0; thd->transaction.cleanup(); |