Commit 83a19306 authored by Alexey Botchkov's avatar Alexey Botchkov

MDEV-7974 XA transactions.

FLUSH TABLES WITH READ LOCK now blocks the XA COMMIT/ROLLBACK/PREPARE
statements as they write to the binlog/Innodb redo.
parent dfa24e55
......@@ -162,7 +162,19 @@ Success: FTWRL is blocked when 'alter event e1 comment 'test'' is active in anot
# --read-only for a discussion why.
#
Success: Was able to run 'analyze table t1_base' under FTWRL.
Success: Was able to run 'analyze table t1_base' with FTWRL active in another connection.
Timeout in wait_condition.inc for select count(*) = 0 from information_schema.processlist
where info = "analyze table t1_base"
Id User Host db Command Time State Info Progress
3 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
4 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
1 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
2 system user NULL Daemon NULL InnoDB purge coordinator NULL 0.000
5 system user NULL Daemon NULL InnoDB shutdown handler NULL 0.000
9 root localhost test Query 31 Waiting for backup lock analyze table t1_base 0.000
10 root localhost test Query 0 Init show full processlist 0.000
11 root localhost test Sleep 31 NULL 0.000
12 root localhost test Sleep 31 NULL 0.000
Error: Wasn't able to run 'analyze table t1_base' with FTWRL active in another connection!
Success: Was able to run FTWRL while 'analyze table t1_base' was active in another connection.
#
# 3) BEGIN, ROLLBACK and COMMIT statements.
......@@ -1309,6 +1321,8 @@ unlock tables;
# Check that XA non-COMMIT statements are not and COMMIT is
# blocked by active FTWRL in another connection
#
# XA COMMIT, XA ROLLBACK and XA PREPARE does take COMMIT lock to ensure
# that nothing is written to bin log and redo log under FTWRL mode.
connection con1;
flush tables with read lock;
connection default;
......@@ -1321,11 +1335,51 @@ connection con1;
flush tables with read lock;
connection default;
xa end 'test1';
xa prepare 'test1';
xa rollback 'test1';
xa prepare 'test1';;
connection con1;
Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
where state = "Waiting for commit lock" and
info = "xa prepare 'test1'"
Id User Host db Command Time State Info Progress
3 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
4 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
1 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
2 system user NULL Daemon NULL InnoDB purge coordinator NULL 0.000
5 system user NULL Daemon NULL InnoDB shutdown handler NULL 0.000
9 root localhost test Query 30 Waiting for backup lock xa prepare 'test1' 0.000
10 root localhost test Query 0 Init show full processlist 0.000
11 root localhost test Sleep 30 NULL 0.000
12 root localhost test Sleep 63 NULL 0.000
unlock tables;
# Switching to connection 'default'.
connection default;
# Reap XA PREPARE.
# Switching to connection 'con1'.
connection con1;
flush tables with read lock;
# Switching to connection 'default'.
connection default;
# Send XA ROLLBACK 'test1'
xa rollback 'test1';
# Switching to connection 'con1'.
connection con1;
# Wait until XA ROLLBACK is blocked.
Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
where state = "Waiting for commit lock" and
info = "xa rollback 'test1'"
Id User Host db Command Time State Info Progress
3 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
4 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
1 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
2 system user NULL Daemon NULL InnoDB purge coordinator NULL 0.000
5 system user NULL Daemon NULL InnoDB shutdown handler NULL 0.000
9 root localhost test Query 30 Waiting for backup lock xa rollback 'test1' 0.000
10 root localhost test Query 0 Init show full processlist 0.000
11 root localhost test Sleep 61 NULL 0.000
12 root localhost test Sleep 94 NULL 0.000
unlock tables;
connection default;
# Reap XA ROLLBACK
xa start 'test1';
insert into t3_trans values (1);
connection con1;
......@@ -1333,7 +1387,33 @@ flush tables with read lock;
connection default;
connection default;
xa end 'test1';
# Send XA PREPARE 'test1'
xa prepare 'test1';
# Switching to connection 'con1'.
connection con1;
# Wait until XA PREPARE is blocked.
Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
where state = "Waiting for commit lock" and
info = "xa prepare 'test1'"
Id User Host db Command Time State Info Progress
3 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
4 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
1 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
2 system user NULL Daemon NULL InnoDB purge coordinator NULL 0.000
5 system user NULL Daemon NULL InnoDB shutdown handler NULL 0.000
9 root localhost test Query 31 Waiting for backup lock xa prepare 'test1' 0.000
10 root localhost test Query 0 Init show full processlist 0.000
11 root localhost test Sleep 92 NULL 0.000
12 root localhost test Sleep 125 NULL 0.000
unlock tables;
# Switching to connection 'default'.
connection default;
# Reap XA PREPARE.
# Switching to connection 'con1'.
connection con1;
flush tables with read lock;
# Switching to connection 'default'.
connection default;
# Send:
xa commit 'test1';;
connection con1;
......@@ -1343,6 +1423,77 @@ connection default;
# Reap XA COMMIT.
delete from t3_trans;
#
# Check that XA COMMIT / ROLLBACK for prepared transaction from a
# disconnected session is blocked by active FTWRL in another connection.
#
# Create temporary connection for XA transaction.
connect con_tmp,localhost,root,,;
xa start 'test1';
insert into t3_trans values (1);
xa end 'test1';
xa prepare 'test1';
# Disconnect temporary connection
disconnect con_tmp;
# Create temporary connection for XA transaction.
connect con_tmp,localhost,root,,;
xa start 'test2';
insert into t3_trans values (2);
xa end 'test2';
xa prepare 'test2';
# Disconnect temporary connection
disconnect con_tmp;
# Switching to connection 'con1'.
connection con1;
flush tables with read lock;
# Switching to connection 'default'.
connection default;
# Send XA ROLLBACK 'test1'
xa rollback 'test1';
# Switching to connection 'con1'.
connection con1;
# Wait until XA ROLLBACK is blocked.
Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
where state = "Waiting for commit lock" and
info = "xa rollback 'test1'"
Id User Host db Command Time State Info Progress
3 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
4 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
1 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
2 system user NULL Daemon NULL InnoDB purge coordinator NULL 0.000
5 system user NULL Daemon NULL InnoDB shutdown handler NULL 0.000
9 root localhost test Query 30 Waiting for backup lock xa rollback 'test1' 0.000
10 root localhost test Query 0 Init show full processlist 0.000
11 root localhost test Sleep 123 NULL 0.000
12 root localhost test Sleep 157 NULL 0.000
unlock tables;
flush tables with read lock;
# Switching to connection 'default'.
connection default;
# Reap XA ROLLBACK
# Send XA COMMIT
xa commit 'test2';;
# Switching to connection 'con1'.
connection con1;
# Wait until XA COMMIT is blocked.
Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
where state = "Waiting for commit lock" and
info = "xa commit 'test2'"
Id User Host db Command Time State Info Progress
3 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
4 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
1 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
2 system user NULL Daemon NULL InnoDB purge coordinator NULL 0.000
5 system user NULL Daemon NULL InnoDB shutdown handler NULL 0.000
9 root localhost test Query 31 Waiting for backup lock xa commit 'test2' 0.000
10 root localhost test Query 0 Init show full processlist 0.000
11 root localhost test Sleep 154 NULL 0.000
12 root localhost test Sleep 188 NULL 0.000
unlock tables;
# Switching to connection 'default'.
connection default;
# Reap XA COMMIT.
delete from t3_trans;
#
# Check that XA COMMIT blocks FTWRL in another connection.
xa start 'test1';
insert into t3_trans values (1);
......@@ -1412,6 +1563,7 @@ flush tables with read lock;
# Implicit commits are allowed under FTWRL.
analyze table t3_trans;
Table Op Msg_type Msg_text
test.t3_trans analyze status Engine-independent statistics collected
test.t3_trans analyze status OK
unlock tables;
#
......@@ -1425,6 +1577,7 @@ unlock tables;
connection default;
# Reap ANALYZE TABLE
Table Op Msg_type Msg_text
test.t3_trans analyze status Engine-independent statistics collected
test.t3_trans analyze status OK
#
# 39.1.b) CHECK TABLE for transactional table is compatible with FTWRL.
......
......@@ -1590,6 +1590,8 @@ unlock tables;
--echo # Check that XA non-COMMIT statements are not and COMMIT is
--echo # blocked by active FTWRL in another connection
--echo #
--echo # XA COMMIT, XA ROLLBACK and XA PREPARE does take COMMIT lock to ensure
--echo # that nothing is written to bin log and redo log under FTWRL mode.
connection $con_aux1;
flush tables with read lock;
connection default;
......@@ -1602,11 +1604,37 @@ connection $con_aux1;
flush tables with read lock;
connection default;
xa end 'test1';
xa prepare 'test1';
xa rollback 'test1';
--send xa prepare 'test1';
connection $con_aux1;
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for commit lock" and
info = "xa prepare 'test1'";
--source include/wait_condition.inc
unlock tables;
--echo # Switching to connection 'default'.
connection default;
--echo # Reap XA PREPARE.
--reap
--echo # Switching to connection '$con_aux1'.
connection $con_aux1;
flush tables with read lock;
--echo # Switching to connection 'default'.
connection default;
--echo # Send XA ROLLBACK 'test1'
--send xa rollback 'test1'
--echo # Switching to connection '$con_aux1'.
connection $con_aux1;
--echo # Wait until XA ROLLBACK is blocked.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for commit lock" and
info = "xa rollback 'test1'";
--source include/wait_condition.inc
unlock tables;
connection default;
--echo # Reap XA ROLLBACK
--reap
xa start 'test1';
insert into t3_trans values (1);
connection $con_aux1;
......@@ -1614,7 +1642,27 @@ flush tables with read lock;
connection default;
connection default;
xa end 'test1';
xa prepare 'test1';
--echo # Send XA PREPARE 'test1'
--send xa prepare 'test1'
--echo # Switching to connection '$con_aux1'.
connection $con_aux1;
--echo # Wait until XA PREPARE is blocked.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for commit lock" and
info = "xa prepare 'test1'";
--source include/wait_condition.inc
unlock tables;
--echo # Switching to connection 'default'.
connection default;
--echo # Reap XA PREPARE.
--reap
--echo # Switching to connection '$con_aux1'.
connection $con_aux1;
flush tables with read lock;
--echo # Switching to connection 'default'.
connection default;
--echo # Send:
--send xa commit 'test1';
connection $con_aux1;
......@@ -1629,6 +1677,64 @@ connection default;
--echo # Reap XA COMMIT.
--reap
delete from t3_trans;
--echo #
--echo # Check that XA COMMIT / ROLLBACK for prepared transaction from a
--echo # disconnected session is blocked by active FTWRL in another connection.
--echo #
--echo # Create temporary connection for XA transaction.
connect (con_tmp,localhost,root,,);
xa start 'test1';
insert into t3_trans values (1);
xa end 'test1';
xa prepare 'test1';
--echo # Disconnect temporary connection
disconnect con_tmp;
--echo # Create temporary connection for XA transaction.
connect (con_tmp,localhost,root,,);
xa start 'test2';
insert into t3_trans values (2);
xa end 'test2';
xa prepare 'test2';
--echo # Disconnect temporary connection
disconnect con_tmp;
--echo # Switching to connection '$con_aux1'.
connection $con_aux1;
flush tables with read lock;
--echo # Switching to connection 'default'.
connection default;
--echo # Send XA ROLLBACK 'test1'
--send xa rollback 'test1'
--echo # Switching to connection '$con_aux1'.
connection $con_aux1;
--echo # Wait until XA ROLLBACK is blocked.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for commit lock" and
info = "xa rollback 'test1'";
--source include/wait_condition.inc
unlock tables;
flush tables with read lock;
--echo # Switching to connection 'default'.
connection default;
--echo # Reap XA ROLLBACK
--reap
--echo # Send XA COMMIT
--send xa commit 'test2';
--echo # Switching to connection '$con_aux1'.
connection $con_aux1;
--echo # Wait until XA COMMIT is blocked.
let $wait_condition=
select count(*) = 1 from information_schema.processlist
where state = "Waiting for commit lock" and
info = "xa commit 'test2'";
--source include/wait_condition.inc
unlock tables;
--echo # Switching to connection 'default'.
connection default;
--echo # Reap XA COMMIT.
--reap
delete from t3_trans;
--echo #
--echo # Check that XA COMMIT blocks FTWRL in another connection.
xa start 'test1';
......
......@@ -355,3 +355,63 @@ DROP TABLE t1, t2, t3;
xa rollback 'testb',0x2030405060,11;
XA RECOVER;
formatID gtrid_length bqual_length data
# Check XA state when lock_wait_timeout happens
# More tests added to flush_read_lock.test
connect con_tmp,localhost,root,,;
set session lock_wait_timeout=1;
create table asd (a int);
xa start 'test1';
insert into asd values(1);
xa end 'test1';
connection default;
flush table with read lock;
connection con_tmp;
# PREPARE error will do auto rollback.
xa prepare 'test1';
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
show errors;
Level Code Message
Error 1205 Lock wait timeout exceeded; try restarting transaction
Error 1402 XA_RBROLLBACK: Transaction branch was rolled back
connection default;
unlock tables;
connection con_tmp;
xa start 'test1';
insert into asd values(1);
xa end 'test1';
xa prepare 'test1';
connection default;
flush tables with read lock;
connection con_tmp;
# LOCK error during ROLLBACK will not alter transaction state.
xa rollback 'test1';
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
show errors;
Level Code Message
Error 1205 Lock wait timeout exceeded; try restarting transaction
Error 1401 XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency
xa recover;
formatID gtrid_length bqual_length data
1 5 0 test1
# LOCK error during COMMIT will not alter transaction state.
xa commit 'test1';
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
show errors;
Level Code Message
Error 1205 Lock wait timeout exceeded; try restarting transaction
Error 1401 XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency
xa recover;
formatID gtrid_length bqual_length data
1 5 0 test1
connection default;
unlock tables;
connection con_tmp;
xa rollback 'test1';
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
Warning 1196 Some non-transactional changed tables couldn't be rolled back
xa recover;
formatID gtrid_length bqual_length data
drop table asd;
disconnect con_tmp;
connection default;
......@@ -468,7 +468,6 @@ REPLACE INTO t2 SELECT * FROM t2;
INSERT INTO t3 VALUES (1);
XA BEGIN 'xid3';
#Cleanup
--disconnect con2
--connection default
......@@ -481,3 +480,48 @@ XA RECOVER;
--source include/wait_until_count_sessions.inc
--echo # Check XA state when lock_wait_timeout happens
--echo # More tests added to flush_read_lock.test
connect (con_tmp,localhost,root,,);
set session lock_wait_timeout=1;
create table asd (a int);
xa start 'test1';
insert into asd values(1);
xa end 'test1';
connection default;
flush table with read lock;
connection con_tmp;
--echo # PREPARE error will do auto rollback.
--ERROR ER_LOCK_WAIT_TIMEOUT
xa prepare 'test1';
show errors;
connection default;
unlock tables;
connection con_tmp;
xa start 'test1';
insert into asd values(1);
xa end 'test1';
xa prepare 'test1';
connection default;
flush tables with read lock;
connection con_tmp;
--echo # LOCK error during ROLLBACK will not alter transaction state.
--ERROR ER_LOCK_WAIT_TIMEOUT
xa rollback 'test1';
show errors;
xa recover;
--echo # LOCK error during COMMIT will not alter transaction state.
--ERROR ER_LOCK_WAIT_TIMEOUT
xa commit 'test1';
show errors;
xa recover;
connection default;
unlock tables;
connection con_tmp;
xa rollback 'test1';
xa recover;
drop table asd;
disconnect con_tmp;
--source include/wait_until_disconnected.inc
connection default;
......@@ -1047,18 +1047,39 @@ bool trans_xa_prepare(THD *thd)
xa_state_names[thd->transaction.xid_state.xa_state]);
else if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
my_error(ER_XAER_NOTA, MYF(0));
else if (ha_prepare(thd))
{
xid_cache_delete(thd, &thd->transaction.xid_state);
thd->transaction.xid_state.xa_state= XA_NOTR;
my_error(ER_XA_RBROLLBACK, MYF(0));
}
else
{
res= 0;
thd->transaction.xid_state.xa_state= XA_PREPARED;
if (thd->variables.pseudo_slave_mode)
res= applier_reset_xa_trans(thd);
/*
Acquire metadata lock which will ensure that COMMIT is blocked
by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
progress blocks FTWRL).
We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
*/
MDL_request mdl_request;
mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
MDL_STATEMENT);
if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout) ||
ha_prepare(thd))
{
if (!mdl_request.ticket)
ha_rollback_trans(thd, TRUE);
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.reset();
thd->server_status&=
~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
xid_cache_delete(thd, &thd->transaction.xid_state);
thd->transaction.xid_state.xa_state= XA_NOTR;
my_error(ER_XA_RBROLLBACK, MYF(0));
}
else
{
res= 0;
thd->transaction.xid_state.xa_state= XA_PREPARED;
if (thd->variables.pseudo_slave_mode)
res= applier_reset_xa_trans(thd);
}
}
DBUG_RETURN(res);
......@@ -1101,6 +1122,27 @@ bool trans_xa_commit(THD *thd)
else
{
res= xa_trans_rolled_back(xs);
/*
Acquire metadata lock which will ensure that COMMIT is blocked
by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
progress blocks FTWRL).
We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
*/
MDL_request mdl_request;
mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
MDL_STATEMENT);
if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
{
/*
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_RMERR, MYF(0));
DBUG_RETURN(true);
}
ha_commit_or_rollback_by_xid(thd->lex->xid, !res);
if((WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) &&
xs->is_binlogged)
......@@ -1139,13 +1181,18 @@ bool trans_xa_commit(THD *thd)
We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
*/
mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
MDL_TRANSACTION);
MDL_STATEMENT);
if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
{
ha_rollback_trans(thd, TRUE);
/*
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_RMERR, MYF(0));
DBUG_RETURN(true);
}
else
{
......@@ -1213,6 +1260,21 @@ bool trans_xa_rollback(THD *thd)
my_error(ER_XAER_NOTA, MYF(0));
else
{
MDL_request mdl_request;
mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
MDL_STATEMENT);
if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
{
/*
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_RMERR, MYF(0));
DBUG_RETURN(true);
}
xa_trans_rolled_back(xs);
if (ha_commit_or_rollback_by_xid(thd->lex->xid, 0) == 0 &&
xs->is_binlogged &&
......@@ -1232,6 +1294,21 @@ bool trans_xa_rollback(THD *thd)
DBUG_RETURN(TRUE);
}
MDL_request mdl_request;
mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
MDL_STATEMENT);
if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
{
/*
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_RMERR, MYF(0));
DBUG_RETURN(true);
}
if(xa_state == XA_PREPARED && thd->transaction.xid_state.is_binlogged &&
(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()))
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment