Skip to content

Commit

Permalink
MDEV-32444 Data from orphaned XA transaction is lost after online alter
Browse files Browse the repository at this point in the history
XA support for online alter was totally missing.

Tying on binlog_hton made this hardly visible: simply having binlog_commit
called from xa_commit made an impression that it will automagically work
for online alter, which turns out wrong: all binlog does is writes
"XA END" into trx cache and flushes it to a real binlog.

In comparison, online alter can't do the same, since online replication
happens in a single transaction.

Solution: make a dedicated XA support.
* Extend struct xid_t with a pointer to Online_alter_cache_list
* On prepare: move online alter cache from THD::ha_data to XID passed
* On XA commit/rollback: use the online alter cache stored in this XID.
  This makes us pass xid_cache_element->xid to xa_commit/xa_rollback
  instead of lex->xid
* Use manual memory management for online alter cache list, instead of
  mem_root allocation, since we don't have mem_root connected to the XA
  transaction.
  • Loading branch information
FooBarrior committed Nov 4, 2023
1 parent a569515 commit 23f9e34
Show file tree
Hide file tree
Showing 8 changed files with 337 additions and 22 deletions.
110 changes: 110 additions & 0 deletions mysql-test/main/alter_table_online_debug.result
Original file line number Diff line number Diff line change
Expand Up @@ -1563,6 +1563,116 @@ connection default;
drop table t1, t2;
set @@binlog_format=default;
set debug_sync= reset;
# MDEV-32444 Data from orphaned XA transaction is lost after online alter
create table t (a int primary key) engine=innodb;
insert into t values (1);
# XA commit
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
alter table t force, algorithm=copy, lock=none;
connection con1;
set debug_sync= 'now wait_for downgraded';
xa begin 'x1';
update t set a = 2 where a = 1;
xa end 'x1';
xa prepare 'x1';
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
disconnect con1;
connection con2;
set debug_sync= 'now wait_for xa_detach';
xa commit 'x1';
set debug_sync= 'now signal go';
connection default;
select * from t;
a
2
# XA rollback
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
alter table t force, algorithm=copy, lock=none;
connect con1, localhost, root,,;
set debug_sync= 'now wait_for downgraded';
xa begin 'x2';
insert into t values (53);
xa end 'x2';
xa prepare 'x2';
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
disconnect con1;
connection con2;
set debug_sync= 'now wait_for xa_detach';
xa rollback 'x2';
set debug_sync= 'now signal go';
connection default;
select * from t;
a
2
# XA transaction is left uncommitted
# end then is rollbacked after alter fails
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
set statement innodb_lock_wait_timeout=0, lock_wait_timeout= 0
for alter table t force, algorithm=copy, lock=none;
connect con1, localhost, root,,;
set debug_sync= 'now wait_for downgraded';
xa begin 'xuncommitted';
insert into t values (3);
xa end 'xuncommitted';
xa prepare 'xuncommitted';
set debug_sync= 'now signal go';
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
disconnect con1;
connection default;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
set debug_sync= 'now wait_for xa_detach';
xa rollback 'xuncommitted';
select * from t;
a
2
# Same, but commit
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
set statement innodb_lock_wait_timeout=0, lock_wait_timeout= 0
for alter table t force, algorithm=copy, lock=none;
connect con1, localhost, root,,;
set debug_sync= 'now wait_for downgraded';
xa begin 'committed_later';
insert into t values (3);
xa end 'committed_later';
xa prepare 'committed_later';
set debug_sync= 'now signal go';
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
disconnect con1;
connection default;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
set debug_sync= 'now wait_for xa_detach';
xa commit 'committed_later';
select * from t;
a
2
3
# Commit, but error in statement, and there is some stmt data to rollback
set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
alter table t force, algorithm=copy, lock=none;
connect con1, localhost, root,,;
set debug_sync= 'now wait_for downgraded';
xa begin 'x1';
insert into t values (4), (3);
ERROR 23000: Duplicate entry '3' for key 'PRIMARY'
insert into t values (5);
xa end 'x1';
xa prepare 'x1';
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
disconnect con1;
connection con2;
set debug_sync= 'now wait_for xa_detach';
xa commit 'x1';
set debug_sync= 'now signal go';
connection default;
select * from t;
a
2
3
5
connect con1, localhost, root,,;
connection default;
drop table t;
set debug_sync= reset;
disconnect con1;
disconnect con2;
#
Expand Down
129 changes: 129 additions & 0 deletions mysql-test/main/alter_table_online_debug.test
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,135 @@ set @@binlog_format=default;
set debug_sync= reset;


--echo # MDEV-32444 Data from orphaned XA transaction is lost after online alter

create table t (a int primary key) engine=innodb;
insert into t values (1);

--echo # XA commit

set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
send alter table t force, algorithm=copy, lock=none;

--connection con1
set debug_sync= 'now wait_for downgraded';
xa begin 'x1';
update t set a = 2 where a = 1;
xa end 'x1';
xa prepare 'x1';
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
--disconnect con1

--connection con2
set debug_sync= 'now wait_for xa_detach';
xa commit 'x1';
set debug_sync= 'now signal go';
--connection default
--reap # alter table

select * from t;

--echo # XA rollback

set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
send alter table t force, algorithm=copy, lock=none;
--connect(con1, localhost, root,,)
set debug_sync= 'now wait_for downgraded';
xa begin 'x2';
insert into t values (53);
xa end 'x2';
xa prepare 'x2';
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
--disconnect con1

--connection con2
set debug_sync= 'now wait_for xa_detach';
xa rollback 'x2';
set debug_sync= 'now signal go';
--connection default
--reap # alter table

select * from t;

--echo # XA transaction is left uncommitted
--echo # end then is rollbacked after alter fails

set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
send set statement innodb_lock_wait_timeout=0, lock_wait_timeout= 0
for alter table t force, algorithm=copy, lock=none;

--connect(con1, localhost, root,,)
set debug_sync= 'now wait_for downgraded';
xa begin 'xuncommitted';
insert into t values (3);
xa end 'xuncommitted';
xa prepare 'xuncommitted';
set debug_sync= 'now signal go';
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
--disconnect con1

--connection default
--error ER_LOCK_WAIT_TIMEOUT
--reap # alter table
set debug_sync= 'now wait_for xa_detach';
xa rollback 'xuncommitted';

select * from t;

--echo # Same, but commit

set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
send set statement innodb_lock_wait_timeout=0, lock_wait_timeout= 0
for alter table t force, algorithm=copy, lock=none;

--connect(con1, localhost, root,,)
set debug_sync= 'now wait_for downgraded';
xa begin 'committed_later';
insert into t values (3);
xa end 'committed_later';
xa prepare 'committed_later';
set debug_sync= 'now signal go';
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
--disconnect con1

--connection default
--error ER_LOCK_WAIT_TIMEOUT
--reap # alter table
set debug_sync= 'now wait_for xa_detach';
xa commit 'committed_later';


select * from t;

--echo # Commit, but error in statement, and there is some stmt data to rollback

set debug_sync= 'alter_table_online_downgraded signal downgraded wait_for go';
send alter table t force, algorithm=copy, lock=none;
--connect(con1, localhost, root,,)
set debug_sync= 'now wait_for downgraded';
xa begin 'x1';
--error ER_DUP_ENTRY
insert into t values (4), (3);
insert into t values (5);
xa end 'x1';
xa prepare 'x1';
set debug_sync= 'THD_cleanup_after_trans_cleanup signal xa_detach';
--disconnect con1

--connection con2
set debug_sync= 'now wait_for xa_detach';
xa commit 'x1';
set debug_sync= 'now signal go';
--connection default
--reap # alter table

select * from t;

--connect(con1, localhost, root,,)
--connection default
drop table t;
set debug_sync= reset;

--disconnect con1
--disconnect con2
--echo #
Expand Down
4 changes: 2 additions & 2 deletions sql/handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2596,7 +2596,7 @@ static bool xarecover_decide_to_commit(xid_recovery_member* member,
static void xarecover_do_commit_or_rollback(handlerton *hton,
xarecover_complete_arg *arg)
{
xid_t x;
XA_data x;
my_bool rc;
xid_recovery_member *member= arg->member;
Binlog_offset *ptr_commit_max= arg->binlog_coord;
Expand All @@ -2605,7 +2605,7 @@ static void xarecover_do_commit_or_rollback(handlerton *hton,
// Populate xid using the server_id from original transaction
x.set(member->xid, member->server_id);
else
x= *member->full_xid;
(XID)x= *member->full_xid;

rc= xarecover_decide_to_commit(member, ptr_commit_max) ?
hton->commit_by_xid(hton, &x) : hton->rollback_by_xid(hton, &x);
Expand Down
7 changes: 7 additions & 0 deletions sql/handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,7 @@ typedef ulonglong my_xid; // this line is the same as in log_event.h
#define COMPATIBLE_DATA_YES 0
#define COMPATIBLE_DATA_NO 1


/**
struct xid_t is binary compatible with the XID structure as
in the X/Open CAE Specification, Distributed Transaction Processing:
Expand Down Expand Up @@ -973,6 +974,12 @@ struct xid_t {
};
typedef struct xid_t XID;

struct Online_alter_cache_list;
struct XA_data: XID
{
Online_alter_cache_list *online_alter_cache= NULL;
};

/*
Enumerates a sequence in the order of
their creation that is in the top-down order of the index file.
Expand Down
Loading

0 comments on commit 23f9e34

Please sign in to comment.