diff --git a/storage/innobase/include/trx0sys.h b/storage/innobase/include/trx0sys.h index d4a87efbc30..97521f6b1fb 100644 --- a/storage/innobase/include/trx0sys.h +++ b/storage/innobase/include/trx0sys.h @@ -503,7 +503,7 @@ class rw_trx_hash_t (!srv_was_started || srv_read_only_mode || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO))); - trx_free_prepared(trx); + trx_free_at_shutdown(trx); } element->~rw_trx_hash_element_t(); } diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index bf267382329..9ff1a7212f0 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -114,12 +114,9 @@ trx_free_resurrected(trx_t* trx); void trx_free_for_background(trx_t* trx); -/********************************************************************//** -At shutdown, frees a transaction object that is in the PREPARED state. */ +/** At shutdown, frees a transaction object. */ void -trx_free_prepared( -/*==============*/ - trx_t* trx); /*!< in, own: trx object */ +trx_free_at_shutdown(trx_t *trx); /** Free a transaction object for MySQL. @param[in,out] trx transaction */ diff --git a/storage/innobase/include/trx0undo.h b/storage/innobase/include/trx0undo.h index cd7cdca28ca..cdb1cfaf478 100644 --- a/storage/innobase/include/trx0undo.h +++ b/storage/innobase/include/trx0undo.h @@ -295,13 +295,9 @@ the data can be discarded. void trx_undo_commit_cleanup(trx_undo_t* undo, bool is_temp); -/********************************************************************//** -At shutdown, frees the undo logs of a PREPARED transaction. */ +/** At shutdown, frees the undo logs of a transaction. */ void -trx_undo_free_prepared( -/*===================*/ - trx_t* trx) /*!< in/out: PREPARED transaction */ - ATTRIBUTE_COLD __attribute__((nonnull)); +trx_undo_free_at_shutdown(trx_t *trx); /* Forward declaration. */ namespace undo { diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc index 133c8367f01..2a4cc0f56a9 100644 --- a/storage/innobase/trx/trx0roll.cc +++ b/storage/innobase/trx/trx0roll.cc @@ -723,74 +723,6 @@ func_exit: trx_roll_crash_recv_trx = NULL; } -/*******************************************************************//** -Rollback or clean up any resurrected incomplete transactions. It assumes -that the caller holds the trx_sys_t::mutex and it will release the -lock if it does a clean up or rollback. -@return TRUE if the transaction was cleaned up or rolled back -and trx_sys->mutex was released. */ -static -ibool -trx_rollback_resurrected( -/*=====================*/ - trx_t* trx, /*!< in: transaction to rollback or clean */ - bool* all) /*!< in/out: FALSE=roll back dictionary transactions; - TRUE=roll back all non-PREPARED transactions */ -{ - ut_ad(trx_sys_mutex_own()); - - /* The trx->is_recovered flag and trx->state are set - atomically under the protection of the trx->mutex (and - lock_sys->mutex) in lock_trx_release_locks(). We do not want - to accidentally clean up a non-recovered transaction here. */ - - trx_mutex_enter(trx); - if (!trx->is_recovered) { -func_exit: - trx_mutex_exit(trx); - return(FALSE); - } - - switch (trx->state) { - case TRX_STATE_ACTIVE: - if (!srv_is_being_started - && !srv_undo_sources && srv_fast_shutdown) { -fake_prepared: - trx->state = TRX_STATE_PREPARED; - *all = false; - goto func_exit; - } - trx_mutex_exit(trx); - - if (*all || trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { - trx_sys_mutex_exit(); - trx_rollback_active(trx); - if (trx->error_state != DB_SUCCESS) { - ut_ad(trx->error_state == DB_INTERRUPTED); - trx->error_state = DB_SUCCESS; - ut_ad(!srv_undo_sources); - ut_ad(srv_fast_shutdown); - mutex_enter(&trx_sys->mutex); - trx_mutex_enter(trx); - goto fake_prepared; - } - trx_free_for_background(trx); - return(TRUE); - } - return(FALSE); - case TRX_STATE_COMMITTED_IN_MEMORY: - ut_ad(trx->xid); - case TRX_STATE_PREPARED: - goto func_exit; - case TRX_STATE_NOT_STARTED: - case TRX_STATE_FORCED_ROLLBACK: - break; - } - - ut_error; - goto func_exit; -} - struct trx_roll_count_callback_arg { @@ -856,54 +788,88 @@ trx_roll_must_shutdown() return false; } -/*******************************************************************//** -Rollback or clean up any incomplete transactions which were -encountered in crash recovery. If the transaction already was -committed, then we clean up a possible insert undo log. If the -transaction was not yet committed, then we roll it back. -@param all true=roll back all recovered active transactions; -false=roll back any incomplete dictionary transaction */ -void -trx_rollback_recovered(bool all) + +static my_bool trx_rollback_recovered_callback(rw_trx_hash_element_t *element, + trx_ut_list_t *trx_list) { - trx_t* trx; - - ut_a(srv_force_recovery < SRV_FORCE_NO_TRX_UNDO); - - /* Note: For XA recovered transactions, we rely on MySQL to - do rollback. They will be in TRX_STATE_PREPARED state. If the server - is shutdown and they are still lingering in trx_sys_t::trx_list - then the shutdown will hang. */ - - /* Loop over the transaction list as long as there are - recovered transactions to clean up or recover. */ - - do { - trx_sys_mutex_enter(); - - for (trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list); - trx != NULL; - trx = UT_LIST_GET_NEXT(trx_list, trx)) { - - assert_trx_in_rw_list(trx); - - /* If this function does a cleanup or rollback - then it will release the trx_sys->mutex, therefore - we need to reacquire it before retrying the loop. */ - - if (trx_rollback_resurrected(trx, &all)) { - - trx_sys_mutex_enter(); - - break; - } - } - - trx_sys_mutex_exit(); - - } while (trx != NULL); + mutex_enter(&element->mutex); + if (trx_t *trx= element->trx) + { + mutex_enter(&trx->mutex); + assert_trx_in_rw_list(trx); + if (trx->is_recovered && trx_state_eq(trx, TRX_STATE_ACTIVE)) + UT_LIST_ADD_FIRST(*trx_list, trx); + mutex_exit(&trx->mutex); + } + mutex_exit(&element->mutex); + return 0; } + +/** + Rollback any incomplete transactions which were encountered in crash recovery. + + If the transaction already was committed, then we clean up a possible insert + undo log. If the transaction was not yet committed, then we roll it back. + + Note: For XA recovered transactions, we rely on MySQL to + do rollback. They will be in TRX_STATE_PREPARED state. If the server + is shutdown and they are still lingering in trx_sys_t::trx_list + then the shutdown will hang. + + @param[in] all true=roll back all recovered active transactions; + false=roll back any incomplete dictionary transaction +*/ + +void trx_rollback_recovered(bool all) +{ + trx_ut_list_t trx_list; + + ut_a(srv_force_recovery < SRV_FORCE_NO_TRX_UNDO); + UT_LIST_INIT(trx_list, &trx_t::mysql_trx_list); + + /* + Collect list of recovered ACTIVE transaction ids first. Once collected, no + other thread is allowed to modify or remove these transactions from + rw_trx_hash. + */ + trx_sys->rw_trx_hash.iterate_no_dups(reinterpret_cast + (trx_rollback_recovered_callback), &trx_list); + + while (trx_t *trx= UT_LIST_GET_FIRST(trx_list)) + { + UT_LIST_REMOVE(trx_list, trx); + +#ifdef UNIV_DEBUG + ut_ad(trx); + trx_mutex_enter(trx); + ut_ad(trx->is_recovered && trx_state_eq(trx, TRX_STATE_ACTIVE)); + trx_mutex_exit(trx); +#endif + + if (!srv_is_being_started && !srv_undo_sources && srv_fast_shutdown) + goto discard; + + if (all || trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) + { + trx_rollback_active(trx); + if (trx->error_state != DB_SUCCESS) + { + ut_ad(trx->error_state == DB_INTERRUPTED); + trx->error_state= DB_SUCCESS; + ut_ad(!srv_undo_sources); + ut_ad(srv_fast_shutdown); +discard: + trx_sys->rw_trx_hash.erase(trx); + trx_free_at_shutdown(trx); + } + else + trx_free_for_background(trx); + } + } +} + + /*******************************************************************//** Rollback or clean up any incomplete transactions which were encountered in crash recovery. If the transaction already was diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index e31e32e2531..9af1f98b96d 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -623,26 +623,24 @@ trx_free_for_background(trx_t* trx) trx_free(trx); } -/********************************************************************//** -At shutdown, frees a transaction object that is in the PREPARED state. */ +/** At shutdown, frees a transaction object. */ void -trx_free_prepared( -/*==============*/ - trx_t* trx) /*!< in, own: trx object */ +trx_free_at_shutdown(trx_t *trx) { + ut_ad(trx->is_recovered); ut_a(trx_state_eq(trx, TRX_STATE_PREPARED) - || (trx->is_recovered - && (trx_state_eq(trx, TRX_STATE_ACTIVE) - || trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)) + || (trx_state_eq(trx, TRX_STATE_ACTIVE) && (!srv_was_started || srv_operation == SRV_OPERATION_RESTORE || srv_operation == SRV_OPERATION_RESTORE_EXPORT || srv_read_only_mode - || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO))); + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO + || (!srv_is_being_started + && !srv_undo_sources && srv_fast_shutdown)))); ut_a(trx->magic_n == TRX_MAGIC_N); lock_trx_release_locks(trx); - trx_undo_free_prepared(trx); + trx_undo_free_at_shutdown(trx); assert_trx_in_rw_list(trx); UT_LIST_REMOVE(trx_sys->rw_trx_list, trx); diff --git a/storage/innobase/trx/trx0undo.cc b/storage/innobase/trx/trx0undo.cc index 372bcaee620..43b1ab1e02d 100644 --- a/storage/innobase/trx/trx0undo.cc +++ b/storage/innobase/trx/trx0undo.cc @@ -1622,15 +1622,10 @@ trx_undo_commit_cleanup(trx_undo_t* undo, bool is_temp) mutex_exit(&rseg->mutex); } -/********************************************************************//** -At shutdown, frees the undo logs of a PREPARED transaction. */ +/** At shutdown, frees the undo logs of a transaction. */ void -trx_undo_free_prepared( -/*===================*/ - trx_t* trx) /*!< in/out: PREPARED transaction */ +trx_undo_free_at_shutdown(trx_t *trx) { - ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS); - if (trx_undo_t*& undo = trx->rsegs.m_redo.undo) { switch (undo->state) { case TRX_UNDO_PREPARED: @@ -1643,9 +1638,7 @@ trx_undo_free_prepared( /* fall through */ case TRX_UNDO_ACTIVE: /* lock_trx_release_locks() assigns - trx->state = TRX_STATE_COMMITTED_IN_MEMORY, - also for transactions that we faked - to TRX_STATE_PREPARED in trx_rollback_resurrected(). */ + trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */ ut_a(!srv_was_started || srv_read_only_mode || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO @@ -1672,9 +1665,7 @@ trx_undo_free_prepared( /* fall through */ case TRX_UNDO_ACTIVE: /* lock_trx_release_locks() assigns - trx->state = TRX_STATE_COMMITTED_IN_MEMORY, - also for transactions that we faked - to TRX_STATE_PREPARED in trx_rollback_resurrected(). */ + trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */ ut_a(!srv_was_started || srv_read_only_mode || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO