Commit d5da8ae0 authored by Thirunarayanan Balathandayuthapani's avatar Thirunarayanan Balathandayuthapani Committed by Marko Mäkelä

MDEV-15772 Potential list overrun during XA recovery

InnoDB could return the same list again and again if the buffer
passed to trx_recover_for_mysql() is smaller than the number of
transactions that InnoDB recovered in XA PREPARE state.

We introduce the transaction state TRX_PREPARED_RECOVERED, which
is like TRX_PREPARED, but will be set during trx_recover_for_mysql()
so that each transaction will only be returned once.

Because init_server_components() is invoking ha_recover() twice,
we must reset the state of the transactions back to TRX_PREPARED
after returning the complete list, so that repeated traversals
will see the complete list again, instead of seeing an empty list.
Without this tweak, the test main.tc_heuristic_recover would hang
in MariaDB 10.1.
parent cb8d888c
call mtr.add_suppression("Found 50 prepared XA transactions");
create table t1 (a int) engine=innodb;
insert into t1 values(1);
xa start 'test50';
insert into t1 values(1);
xa end 'test50';
xa prepare 'test50';
xa start 'test49';
insert into t1 values(1);
xa end 'test49';
xa prepare 'test49';
xa start 'test48';
insert into t1 values(1);
xa end 'test48';
xa prepare 'test48';
xa start 'test47';
insert into t1 values(1);
xa end 'test47';
xa prepare 'test47';
xa start 'test46';
insert into t1 values(1);
xa end 'test46';
xa prepare 'test46';
xa start 'test45';
insert into t1 values(1);
xa end 'test45';
xa prepare 'test45';
xa start 'test44';
insert into t1 values(1);
xa end 'test44';
xa prepare 'test44';
xa start 'test43';
insert into t1 values(1);
xa end 'test43';
xa prepare 'test43';
xa start 'test42';
insert into t1 values(1);
xa end 'test42';
xa prepare 'test42';
xa start 'test41';
insert into t1 values(1);
xa end 'test41';
xa prepare 'test41';
xa start 'test40';
insert into t1 values(1);
xa end 'test40';
xa prepare 'test40';
xa start 'test39';
insert into t1 values(1);
xa end 'test39';
xa prepare 'test39';
xa start 'test38';
insert into t1 values(1);
xa end 'test38';
xa prepare 'test38';
xa start 'test37';
insert into t1 values(1);
xa end 'test37';
xa prepare 'test37';
xa start 'test36';
insert into t1 values(1);
xa end 'test36';
xa prepare 'test36';
xa start 'test35';
insert into t1 values(1);
xa end 'test35';
xa prepare 'test35';
xa start 'test34';
insert into t1 values(1);
xa end 'test34';
xa prepare 'test34';
xa start 'test33';
insert into t1 values(1);
xa end 'test33';
xa prepare 'test33';
xa start 'test32';
insert into t1 values(1);
xa end 'test32';
xa prepare 'test32';
xa start 'test31';
insert into t1 values(1);
xa end 'test31';
xa prepare 'test31';
xa start 'test30';
insert into t1 values(1);
xa end 'test30';
xa prepare 'test30';
xa start 'test29';
insert into t1 values(1);
xa end 'test29';
xa prepare 'test29';
xa start 'test28';
insert into t1 values(1);
xa end 'test28';
xa prepare 'test28';
xa start 'test27';
insert into t1 values(1);
xa end 'test27';
xa prepare 'test27';
xa start 'test26';
insert into t1 values(1);
xa end 'test26';
xa prepare 'test26';
xa start 'test25';
insert into t1 values(1);
xa end 'test25';
xa prepare 'test25';
xa start 'test24';
insert into t1 values(1);
xa end 'test24';
xa prepare 'test24';
xa start 'test23';
insert into t1 values(1);
xa end 'test23';
xa prepare 'test23';
xa start 'test22';
insert into t1 values(1);
xa end 'test22';
xa prepare 'test22';
xa start 'test21';
insert into t1 values(1);
xa end 'test21';
xa prepare 'test21';
xa start 'test20';
insert into t1 values(1);
xa end 'test20';
xa prepare 'test20';
xa start 'test19';
insert into t1 values(1);
xa end 'test19';
xa prepare 'test19';
xa start 'test18';
insert into t1 values(1);
xa end 'test18';
xa prepare 'test18';
xa start 'test17';
insert into t1 values(1);
xa end 'test17';
xa prepare 'test17';
xa start 'test16';
insert into t1 values(1);
xa end 'test16';
xa prepare 'test16';
xa start 'test15';
insert into t1 values(1);
xa end 'test15';
xa prepare 'test15';
xa start 'test14';
insert into t1 values(1);
xa end 'test14';
xa prepare 'test14';
xa start 'test13';
insert into t1 values(1);
xa end 'test13';
xa prepare 'test13';
xa start 'test12';
insert into t1 values(1);
xa end 'test12';
xa prepare 'test12';
xa start 'test11';
insert into t1 values(1);
xa end 'test11';
xa prepare 'test11';
xa start 'test10';
insert into t1 values(1);
xa end 'test10';
xa prepare 'test10';
xa start 'test9';
insert into t1 values(1);
xa end 'test9';
xa prepare 'test9';
xa start 'test8';
insert into t1 values(1);
xa end 'test8';
xa prepare 'test8';
xa start 'test7';
insert into t1 values(1);
xa end 'test7';
xa prepare 'test7';
xa start 'test6';
insert into t1 values(1);
xa end 'test6';
xa prepare 'test6';
xa start 'test5';
insert into t1 values(1);
xa end 'test5';
xa prepare 'test5';
xa start 'test4';
insert into t1 values(1);
xa end 'test4';
xa prepare 'test4';
xa start 'test3';
insert into t1 values(1);
xa end 'test3';
xa prepare 'test3';
xa start 'test2';
insert into t1 values(1);
xa end 'test2';
xa prepare 'test2';
xa start 'test1';
insert into t1 values(1);
xa end 'test1';
xa prepare 'test1';
xa recover;
formatID gtrid_length bqual_length data
1 5 0 test1
1 5 0 test2
1 5 0 test3
1 5 0 test4
1 5 0 test5
1 5 0 test6
1 5 0 test7
1 5 0 test8
1 5 0 test9
1 6 0 test10
1 6 0 test11
1 6 0 test12
1 6 0 test13
1 6 0 test14
1 6 0 test15
1 6 0 test16
1 6 0 test17
1 6 0 test18
1 6 0 test19
1 6 0 test20
1 6 0 test21
1 6 0 test22
1 6 0 test23
1 6 0 test24
1 6 0 test25
1 6 0 test26
1 6 0 test27
1 6 0 test28
1 6 0 test29
1 6 0 test30
1 6 0 test31
1 6 0 test32
1 6 0 test33
1 6 0 test34
1 6 0 test35
1 6 0 test36
1 6 0 test37
1 6 0 test38
1 6 0 test39
1 6 0 test40
1 6 0 test41
1 6 0 test42
1 6 0 test43
1 6 0 test44
1 6 0 test45
1 6 0 test46
1 6 0 test47
1 6 0 test48
1 6 0 test49
1 6 0 test50
xa recover;
formatID gtrid_length bqual_length data
1 5 0 test1
1 5 0 test2
1 5 0 test3
1 5 0 test4
1 5 0 test5
1 5 0 test6
1 5 0 test7
1 5 0 test8
1 5 0 test9
1 6 0 test10
1 6 0 test11
1 6 0 test12
1 6 0 test13
1 6 0 test14
1 6 0 test15
1 6 0 test16
1 6 0 test17
1 6 0 test18
1 6 0 test19
1 6 0 test20
1 6 0 test21
1 6 0 test22
1 6 0 test23
1 6 0 test24
1 6 0 test25
1 6 0 test26
1 6 0 test27
1 6 0 test28
1 6 0 test29
1 6 0 test30
1 6 0 test31
1 6 0 test32
1 6 0 test33
1 6 0 test34
1 6 0 test35
1 6 0 test36
1 6 0 test37
1 6 0 test38
1 6 0 test39
1 6 0 test40
1 6 0 test41
1 6 0 test42
1 6 0 test43
1 6 0 test44
1 6 0 test45
1 6 0 test46
1 6 0 test47
1 6 0 test48
1 6 0 test49
1 6 0 test50
xa recover;
formatID gtrid_length bqual_length data
drop table t1;
-- source include/have_innodb.inc
-- source include/have_debug.inc
-- source include/not_embedded.inc
call mtr.add_suppression("Found 50 prepared XA transactions");
create table t1 (a int) engine=innodb;
insert into t1 values(1);
let $trial = 50;
while ($trial)
{
--connect (con$trial, localhost, root,,)
let $st_pre = `select concat('test', $trial)`;
eval xa start '$st_pre';
insert into t1 values(1);
eval xa end '$st_pre';
eval xa prepare '$st_pre';
dec $trial;
}
connection default;
# Kill and restart the server.
-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
-- shutdown_server 0
-- source include/wait_until_disconnected.inc
-- exec echo "restart:--debug_dbug=+d,min_xa_len" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
-- enable_reconnect
-- source include/wait_until_connected_again.inc
-- disable_reconnect
--sorted_result
xa recover;
--sorted_result
xa recover;
--disable_query_log
let $trial = 50;
while ($trial)
{
let $st_pre = `select concat('test', $trial)`;
eval xa commit '$st_pre';
dec $trial;
}
--enable_query_log
xa recover;
drop table t1;
...@@ -1740,6 +1740,7 @@ int ha_recover(HASH *commit_list) ...@@ -1740,6 +1740,7 @@ int ha_recover(HASH *commit_list)
for (info.len= MAX_XID_LIST_SIZE ; for (info.len= MAX_XID_LIST_SIZE ;
info.list==0 && info.len > MIN_XID_LIST_SIZE; info.len/=2) info.list==0 && info.len > MIN_XID_LIST_SIZE; info.len/=2)
{ {
DBUG_EXECUTE_IF("min_xa_len", info.len = 16;);
info.list=(XID *)my_malloc(info.len*sizeof(XID), MYF(0)); info.list=(XID *)my_malloc(info.len*sizeof(XID), MYF(0));
} }
if (!info.list) if (!info.list)
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -353,10 +354,13 @@ trx_is_active( ...@@ -353,10 +354,13 @@ trx_is_active(
} }
trx = trx_get_on_id(trx_id); trx = trx_get_on_id(trx_id);
if (trx && (trx->conc_state == TRX_ACTIVE if (trx) {
|| trx->conc_state == TRX_PREPARED)) { switch (trx->conc_state) {
case TRX_ACTIVE:
return(TRUE); case TRX_PREPARED:
case TRX_PREPARED_RECOVERED:
return(TRUE);
}
} }
return(FALSE); return(FALSE);
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -741,6 +742,8 @@ struct trx_struct{ ...@@ -741,6 +742,8 @@ struct trx_struct{
#define TRX_ACTIVE 1 #define TRX_ACTIVE 1
#define TRX_COMMITTED_IN_MEMORY 2 #define TRX_COMMITTED_IN_MEMORY 2
#define TRX_PREPARED 3 /* Support for 2PC/XA */ #define TRX_PREPARED 3 /* Support for 2PC/XA */
#define TRX_PREPARED_RECOVERED 4 /* XA PREPARE transaction that
was returned to ha_recover() */
/* Transaction execution states when trx->conc_state == TRX_ACTIVE */ /* Transaction execution states when trx->conc_state == TRX_ACTIVE */
#define TRX_QUE_RUNNING 0 /* transaction is running */ #define TRX_QUE_RUNNING 0 /* transaction is running */
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -4782,6 +4783,22 @@ lock_print_info_all_transactions( ...@@ -4782,6 +4783,22 @@ lock_print_info_all_transactions(
} }
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
/** Validate the state of a transaction that holds locks */
static void lock_trx_state_validate(const trx_t* trx)
{
switch (trx->conc_state) {
case TRX_NOT_STARTED:
break;
case TRX_ACTIVE:
case TRX_PREPARED:
case TRX_PREPARED_RECOVERED:
case TRX_COMMITTED_IN_MEMORY:
return;
}
ut_ad(!"wrong state");
}
/*********************************************************************//** /*********************************************************************//**
Validates the lock queue on a table. Validates the lock queue on a table.
@return TRUE if ok */ @return TRUE if ok */
...@@ -4798,9 +4815,7 @@ lock_table_queue_validate( ...@@ -4798,9 +4815,7 @@ lock_table_queue_validate(
lock = UT_LIST_GET_FIRST(table->locks); lock = UT_LIST_GET_FIRST(table->locks);
while (lock) { while (lock) {
ut_a(((lock->trx)->conc_state == TRX_ACTIVE) ut_d(lock_trx_state_validate(lock->trx));
|| ((lock->trx)->conc_state == TRX_PREPARED)
|| ((lock->trx)->conc_state == TRX_COMMITTED_IN_MEMORY));
if (!lock_get_wait(lock)) { if (!lock_get_wait(lock)) {
...@@ -4848,15 +4863,7 @@ lock_rec_queue_validate( ...@@ -4848,15 +4863,7 @@ lock_rec_queue_validate(
lock = lock_rec_get_first(block, heap_no); lock = lock_rec_get_first(block, heap_no);
while (lock) { while (lock) {
switch(lock->trx->conc_state) { ut_d(lock_trx_state_validate(lock->trx));
case TRX_ACTIVE:
case TRX_PREPARED:
case TRX_COMMITTED_IN_MEMORY:
break;
default:
ut_error;
}
ut_a(trx_in_trx_list(lock->trx)); ut_a(trx_in_trx_list(lock->trx));
if (lock_get_wait(lock)) { if (lock_get_wait(lock)) {
...@@ -4935,9 +4942,7 @@ lock_rec_queue_validate( ...@@ -4935,9 +4942,7 @@ lock_rec_queue_validate(
lock = lock_rec_get_first(block, heap_no); lock = lock_rec_get_first(block, heap_no);
while (lock) { while (lock) {
ut_a(lock->trx->conc_state == TRX_ACTIVE ut_d(lock_trx_state_validate(lock->trx));
|| lock->trx->conc_state == TRX_PREPARED
|| lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY);
ut_a(trx_in_trx_list(lock->trx)); ut_a(trx_in_trx_list(lock->trx));
if (index) { if (index) {
...@@ -5014,10 +5019,8 @@ lock_rec_validate_page( ...@@ -5014,10 +5019,8 @@ lock_rec_validate_page(
} }
} }
ut_d(lock_trx_state_validate(lock->trx));
ut_a(trx_in_trx_list(lock->trx)); ut_a(trx_in_trx_list(lock->trx));
ut_a(lock->trx->conc_state == TRX_ACTIVE
|| lock->trx->conc_state == TRX_PREPARED
|| lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY);
# ifdef UNIV_SYNC_DEBUG # ifdef UNIV_SYNC_DEBUG
/* Only validate the record queues when this thread is not /* Only validate the record queues when this thread is not
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -272,15 +273,19 @@ read_view_open_now( ...@@ -272,15 +273,19 @@ read_view_open_now(
view->low_limit_id = view->low_limit_no; view->low_limit_id = view->low_limit_no;
n = 0; n = 0;
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
/* No active transaction should be visible, except cr_trx */ /* No active transaction should be visible, except cr_trx */
while (trx) { for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx;
if (trx->id != cr_trx_id trx = UT_LIST_GET_NEXT(trx_list, trx)) {
&& (trx->conc_state == TRX_ACTIVE if (trx->id == cr_trx_id) {
|| trx->conc_state == TRX_PREPARED)) { continue;
}
switch (trx->conc_state) {
case TRX_ACTIVE:
case TRX_PREPARED:
case TRX_PREPARED_RECOVERED:
read_view_set_nth_trx_id(view, n, trx->id); read_view_set_nth_trx_id(view, n, trx->id);
n++; n++;
...@@ -296,8 +301,6 @@ read_view_open_now( ...@@ -296,8 +301,6 @@ read_view_open_now(
view->low_limit_no = trx->no; view->low_limit_no = trx->no;
} }
} }
trx = UT_LIST_GET_NEXT(trx_list, trx);
} }
view->n_trx_ids = n; view->n_trx_ids = n;
...@@ -437,18 +440,15 @@ read_cursor_view_create_for_mysql( ...@@ -437,18 +440,15 @@ read_cursor_view_create_for_mysql(
view->low_limit_id = view->low_limit_no; view->low_limit_id = view->low_limit_no;
n = 0; n = 0;
trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
/* No active transaction should be visible */ /* No active transaction should be visible */
for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx;
while (trx) { trx = UT_LIST_GET_NEXT(trx_list, trx)) {
switch (trx->conc_state) {
if (trx->conc_state == TRX_ACTIVE case TRX_ACTIVE:
|| trx->conc_state == TRX_PREPARED) { case TRX_PREPARED:
case TRX_PREPARED_RECOVERED:
read_view_set_nth_trx_id(view, n, trx->id); read_view_set_nth_trx_id(view, n++, trx->id);
n++;
/* NOTE that a transaction whose trx number is < /* NOTE that a transaction whose trx number is <
trx_sys->max_trx_id can still be active, if it is trx_sys->max_trx_id can still be active, if it is
...@@ -461,8 +461,6 @@ read_cursor_view_create_for_mysql( ...@@ -461,8 +461,6 @@ read_cursor_view_create_for_mysql(
view->low_limit_no = trx->no; view->low_limit_no = trx->no;
} }
} }
trx = UT_LIST_GET_NEXT(trx_list, trx);
} }
view->n_trx_ids = n; view->n_trx_ids = n;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved.
Copyright (c) 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -564,6 +565,7 @@ trx_rollback_or_clean_recovered( ...@@ -564,6 +565,7 @@ trx_rollback_or_clean_recovered(
switch (trx->conc_state) { switch (trx->conc_state) {
case TRX_NOT_STARTED: case TRX_NOT_STARTED:
case TRX_PREPARED: case TRX_PREPARED:
case TRX_PREPARED_RECOVERED:
continue; continue;
case TRX_COMMITTED_IN_MEMORY: case TRX_COMMITTED_IN_MEMORY:
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -1031,7 +1032,7 @@ trx_sys_init_at_db_start(void) ...@@ -1031,7 +1032,7 @@ trx_sys_init_at_db_start(void)
trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
for (;;) { for (;;) {
ut_ad(trx->conc_state != TRX_PREPARED_RECOVERED);
if (trx->conc_state != TRX_PREPARED) { if (trx->conc_state != TRX_PREPARED) {
rows_to_undo += trx->undo_no; rows_to_undo += trx->undo_no;
} }
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2011, Innobase Oy. All Rights Reserved. Copyright (c) 1996, 2011, Innobase Oy. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -348,7 +349,8 @@ trx_free_prepared( ...@@ -348,7 +349,8 @@ trx_free_prepared(
trx_t* trx) /*!< in, own: trx object */ trx_t* trx) /*!< in, own: trx object */
{ {
ut_ad(mutex_own(&kernel_mutex)); ut_ad(mutex_own(&kernel_mutex));
ut_a(trx->conc_state == TRX_PREPARED); ut_a(trx->conc_state == TRX_PREPARED
|| trx->conc_state == TRX_PREPARED_RECOVERED);
ut_a(trx->magic_n == TRX_MAGIC_N); ut_a(trx->magic_n == TRX_MAGIC_N);
/* Prepared transactions are sort of active; they allow /* Prepared transactions are sort of active; they allow
...@@ -932,10 +934,11 @@ trx_commit_off_kernel( ...@@ -932,10 +934,11 @@ trx_commit_off_kernel(
lsn = 0; lsn = 0;
} }
ut_ad(trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED); ut_ad(trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED
|| trx->conc_state == TRX_PREPARED_RECOVERED);
ut_ad(mutex_own(&kernel_mutex)); ut_ad(mutex_own(&kernel_mutex));
if (UNIV_UNLIKELY(trx->conc_state == TRX_PREPARED)) { if (UNIV_UNLIKELY(trx->conc_state != TRX_ACTIVE)) {
ut_a(trx_n_prepared > 0); ut_a(trx_n_prepared > 0);
trx_n_prepared--; trx_n_prepared--;
} }
...@@ -2072,6 +2075,7 @@ trx_recover_for_mysql( ...@@ -2072,6 +2075,7 @@ trx_recover_for_mysql(
while (trx) { while (trx) {
if (trx->conc_state == TRX_PREPARED) { if (trx->conc_state == TRX_PREPARED) {
trx->conc_state = TRX_PREPARED_RECOVERED;
xid_list[count] = trx->xid; xid_list[count] = trx->xid;
if (count == 0) { if (count == 0) {
...@@ -2096,13 +2100,25 @@ trx_recover_for_mysql( ...@@ -2096,13 +2100,25 @@ trx_recover_for_mysql(
count++; count++;
if (count == len) { if (count == len) {
break; goto partial;
} }
} }
trx = UT_LIST_GET_NEXT(trx_list, trx); trx = UT_LIST_GET_NEXT(trx_list, trx);
} }
/* After returning the full list, reset the state, because
init_server_components() wants to recover the collection of
transactions twice, by first calling tc_log->open() and then
ha_recover() directly. */
for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx;
trx = UT_LIST_GET_NEXT(trx_list, trx)) {
if (trx->conc_state == TRX_PREPARED_RECOVERED) {
trx->conc_state = TRX_PREPARED;
}
}
partial:
mutex_exit(&kernel_mutex); mutex_exit(&kernel_mutex);
if (count > 0){ if (count > 0){
...@@ -2144,7 +2160,8 @@ trx_get_trx_by_xid( ...@@ -2144,7 +2160,8 @@ trx_get_trx_by_xid(
the same */ the same */
if (trx->is_recovered if (trx->is_recovered
&& trx->conc_state == TRX_PREPARED && (trx->conc_state == TRX_PREPARED
|| trx->conc_state == TRX_PREPARED_RECOVERED)
&& xid->gtrid_length == trx->xid.gtrid_length && xid->gtrid_length == trx->xid.gtrid_length
&& xid->bqual_length == trx->xid.bqual_length && xid->bqual_length == trx->xid.bqual_length
&& memcmp(xid->data, trx->xid.data, && memcmp(xid->data, trx->xid.data,
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved. Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -802,6 +803,8 @@ struct trx_struct{ ...@@ -802,6 +803,8 @@ struct trx_struct{
#define TRX_ACTIVE 1 #define TRX_ACTIVE 1
#define TRX_COMMITTED_IN_MEMORY 2 #define TRX_COMMITTED_IN_MEMORY 2
#define TRX_PREPARED 3 /* Support for 2PC/XA */ #define TRX_PREPARED 3 /* Support for 2PC/XA */
#define TRX_PREPARED_RECOVERED 4 /* XA PREPARE transaction that
was returned to ha_recover() */
/* Transaction execution states when trx->conc_state == TRX_ACTIVE */ /* Transaction execution states when trx->conc_state == TRX_ACTIVE */
#define TRX_QUE_RUNNING 0 /* transaction is running */ #define TRX_QUE_RUNNING 0 /* transaction is running */
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -4811,6 +4812,22 @@ lock_print_info_all_transactions( ...@@ -4811,6 +4812,22 @@ lock_print_info_all_transactions(
} }
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
/** Validate the state of a transaction that holds locks */
static void lock_trx_state_validate(const trx_t* trx)
{
switch (trx->state) {
case TRX_NOT_STARTED:
break;
case TRX_ACTIVE:
case TRX_PREPARED:
case TRX_PREPARED_RECOVERED:
case TRX_COMMITTED_IN_MEMORY:
return;
}
ut_ad(!"wrong state");
}
/*********************************************************************//** /*********************************************************************//**
Validates the lock queue on a table. Validates the lock queue on a table.
@return TRUE if ok */ @return TRUE if ok */
...@@ -4827,9 +4844,7 @@ lock_table_queue_validate( ...@@ -4827,9 +4844,7 @@ lock_table_queue_validate(
lock = UT_LIST_GET_FIRST(table->locks); lock = UT_LIST_GET_FIRST(table->locks);
while (lock) { while (lock) {
ut_a(((lock->trx)->state == TRX_ACTIVE) ut_d(lock_trx_state_validate(lock->trx));
|| ((lock->trx)->state == TRX_PREPARED)
|| ((lock->trx)->state == TRX_COMMITTED_IN_MEMORY));
if (!lock_get_wait(lock)) { if (!lock_get_wait(lock)) {
...@@ -4877,15 +4892,7 @@ lock_rec_queue_validate( ...@@ -4877,15 +4892,7 @@ lock_rec_queue_validate(
lock = lock_rec_get_first(block, heap_no); lock = lock_rec_get_first(block, heap_no);
while (lock) { while (lock) {
switch(lock->trx->state) { ut_d(lock_trx_state_validate(lock->trx));
case TRX_ACTIVE:
case TRX_PREPARED:
case TRX_COMMITTED_IN_MEMORY:
break;
default:
ut_error;
}
ut_a(trx_in_trx_list(lock->trx)); ut_a(trx_in_trx_list(lock->trx));
if (lock_get_wait(lock)) { if (lock_get_wait(lock)) {
...@@ -4964,9 +4971,7 @@ lock_rec_queue_validate( ...@@ -4964,9 +4971,7 @@ lock_rec_queue_validate(
lock = lock_rec_get_first(block, heap_no); lock = lock_rec_get_first(block, heap_no);
while (lock) { while (lock) {
ut_a(lock->trx->state == TRX_ACTIVE ut_d(lock_trx_state_validate(lock->trx));
|| lock->trx->state == TRX_PREPARED
|| lock->trx->state == TRX_COMMITTED_IN_MEMORY);
ut_a(trx_in_trx_list(lock->trx)); ut_a(trx_in_trx_list(lock->trx));
if (index) { if (index) {
...@@ -5043,10 +5048,8 @@ lock_rec_validate_page( ...@@ -5043,10 +5048,8 @@ lock_rec_validate_page(
} }
} }
ut_d(lock_trx_state_validate(lock->trx));
ut_a(trx_in_trx_list(lock->trx)); ut_a(trx_in_trx_list(lock->trx));
ut_a(lock->trx->state == TRX_ACTIVE
|| lock->trx->state == TRX_PREPARED
|| lock->trx->state == TRX_COMMITTED_IN_MEMORY);
# ifdef UNIV_SYNC_DEBUG # ifdef UNIV_SYNC_DEBUG
/* Only validate the record queues when this thread is not /* Only validate the record queues when this thread is not
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved.
Copyright (c) 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -564,6 +565,7 @@ trx_rollback_or_clean_recovered( ...@@ -564,6 +565,7 @@ trx_rollback_or_clean_recovered(
switch (trx->state) { switch (trx->state) {
case TRX_NOT_STARTED: case TRX_NOT_STARTED:
case TRX_PREPARED: case TRX_PREPARED:
case TRX_PREPARED_RECOVERED:
continue; continue;
case TRX_COMMITTED_IN_MEMORY: case TRX_COMMITTED_IN_MEMORY:
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -1355,7 +1356,7 @@ trx_sys_init_at_db_start(void) ...@@ -1355,7 +1356,7 @@ trx_sys_init_at_db_start(void)
trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx = UT_LIST_GET_FIRST(trx_sys->trx_list);
for (;;) { for (;;) {
ut_ad(trx->state != TRX_PREPARED_RECOVERED);
if (trx->state != TRX_PREPARED) { if (trx->state != TRX_PREPARED) {
rows_to_undo += trx->undo_no; rows_to_undo += trx->undo_no;
} }
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2011, Innobase Oy. All Rights Reserved. Copyright (c) 1996, 2011, Innobase Oy. All Rights Reserved.
Copyright (c) 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -481,7 +482,8 @@ trx_free_prepared( ...@@ -481,7 +482,8 @@ trx_free_prepared(
trx_t* trx) /*!< in, own: trx object */ trx_t* trx) /*!< in, own: trx object */
{ {
ut_ad(mutex_own(&kernel_mutex)); ut_ad(mutex_own(&kernel_mutex));
ut_a(trx->state == TRX_PREPARED); ut_a(trx->state == TRX_PREPARED
|| trx->state == TRX_PREPARED_RECOVERED);
ut_a(trx->magic_n == TRX_MAGIC_N); ut_a(trx->magic_n == TRX_MAGIC_N);
/* Prepared transactions are sort of active; they allow /* Prepared transactions are sort of active; they allow
...@@ -1148,10 +1150,11 @@ trx_commit_off_kernel( ...@@ -1148,10 +1150,11 @@ trx_commit_off_kernel(
lsn = 0; lsn = 0;
} }
ut_ad(trx->state == TRX_ACTIVE || trx->state == TRX_PREPARED); ut_ad(trx->state == TRX_ACTIVE || trx->state == TRX_PREPARED
|| trx->state == TRX_PREPARED_RECOVERED);
ut_ad(mutex_own(&kernel_mutex)); ut_ad(mutex_own(&kernel_mutex));
if (UNIV_UNLIKELY(trx->state == TRX_PREPARED)) { if (UNIV_UNLIKELY(trx->state != TRX_ACTIVE)) {
ut_a(trx_n_prepared > 0); ut_a(trx_n_prepared > 0);
trx_n_prepared--; trx_n_prepared--;
} }
...@@ -2359,6 +2362,7 @@ trx_recover_for_mysql( ...@@ -2359,6 +2362,7 @@ trx_recover_for_mysql(
while (trx) { while (trx) {
if (trx->state == TRX_PREPARED) { if (trx->state == TRX_PREPARED) {
trx->state = TRX_PREPARED_RECOVERED;
xid_list[count] = trx->xid; xid_list[count] = trx->xid;
if (count == 0) { if (count == 0) {
...@@ -2383,13 +2387,25 @@ trx_recover_for_mysql( ...@@ -2383,13 +2387,25 @@ trx_recover_for_mysql(
count++; count++;
if (count == len) { if (count == len) {
break; goto partial;
} }
} }
trx = UT_LIST_GET_NEXT(trx_list, trx); trx = UT_LIST_GET_NEXT(trx_list, trx);
} }
/* After returning the full list, reset the state, because
init_server_components() wants to recover the collection of
transactions twice, by first calling tc_log->open() and then
ha_recover() directly. */
for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx;
trx = UT_LIST_GET_NEXT(trx_list, trx)) {
if (trx->state == TRX_PREPARED_RECOVERED) {
trx->state = TRX_PREPARED;
}
}
partial:
mutex_exit(&kernel_mutex); mutex_exit(&kernel_mutex);
if (count > 0){ if (count > 0){
...@@ -2431,7 +2447,8 @@ trx_get_trx_by_xid( ...@@ -2431,7 +2447,8 @@ trx_get_trx_by_xid(
the same */ the same */
if (trx->is_recovered if (trx->is_recovered
&& trx->state == TRX_PREPARED && (trx->state == TRX_PREPARED
|| trx->state == TRX_PREPARED_RECOVERED)
&& xid->gtrid_length == trx->xid.gtrid_length && xid->gtrid_length == trx->xid.gtrid_length
&& xid->bqual_length == trx->xid.bqual_length && xid->bqual_length == trx->xid.bqual_length
&& memcmp(xid->data, trx->xid.data, && memcmp(xid->data, trx->xid.data,
......
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