diff options
Diffstat (limited to 'storage/innobase/trx/trx0roll.cc')
-rw-r--r-- | storage/innobase/trx/trx0roll.cc | 656 |
1 files changed, 256 insertions, 400 deletions
diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc index c65d95a9817..3e431b146a7 100644 --- a/storage/innobase/trx/trx0roll.cc +++ b/storage/innobase/trx/trx0roll.cc @@ -23,32 +23,37 @@ Transaction rollback Created 3/26/1996 Heikki Tuuri *******************************************************/ +#include "ha_prototypes.h" + #include "trx0roll.h" #ifdef UNIV_NONINL #include "trx0roll.ic" #endif +#include <mysql/service_wsrep.h> + #include "fsp0fsp.h" +#include "lock0lock.h" #include "mach0data.h" -#include "trx0rseg.h" -#include "trx0trx.h" -#include "trx0undo.h" -#include "trx0rec.h" +#include "pars0pars.h" #include "que0que.h" -#include "usr0sess.h" -#include "srv0start.h" #include "read0read.h" -#include "row0undo.h" #include "row0mysql.h" -#include "lock0lock.h" -#include "pars0pars.h" +#include "row0undo.h" #include "srv0mon.h" +#include "srv0start.h" +#include "trx0rec.h" +#include "trx0rseg.h" #include "trx0sys.h" +#include "trx0trx.h" +#include "trx0undo.h" +#include "usr0sess.h" +#include "ha_prototypes.h" /** This many pages must be undone before a truncate is tried within rollback */ -#define TRX_ROLL_TRUNC_THRESHOLD 1 +static const ulint TRX_ROLL_TRUNC_THRESHOLD = 1; /** true if trx_rollback_or_clean_all_recovered() thread is active */ bool trx_rollback_or_clean_is_active; @@ -93,14 +98,18 @@ trx_rollback_to_savepoint_low( if (savept != NULL) { roll_node->partial = TRUE; roll_node->savept = *savept; - assert_trx_in_list(trx); - } else { + check_trx_state(trx); + } else { assert_trx_nonlocking_or_in_list(trx); } trx->error_state = DB_SUCCESS; - if (trx->insert_undo || trx->update_undo) { + if (trx_is_rseg_updated(trx)) { + + ut_ad(trx->rsegs.m_redo.rseg != 0 + || trx->rsegs.m_noredo.rseg != 0); + thr = pars_complete_graph_for_exec(roll_node, trx, heap); ut_a(thr == que_fork_start_command( @@ -137,8 +146,7 @@ trx_rollback_to_savepoint_low( /*******************************************************************//** Rollback a transaction to a given savepoint or do a complete rollback. -@return error code or DB_SUCCESS */ -UNIV_INTERN +@return error code or DB_SUCCESS */ dberr_t trx_rollback_to_savepoint( /*======================*/ @@ -149,7 +157,7 @@ trx_rollback_to_savepoint( { ut_ad(!trx_mutex_own(trx)); - trx_start_if_not_started_xa(trx); + trx_start_if_not_started_xa(trx, true); trx_rollback_to_savepoint_low(trx, savept); @@ -158,7 +166,7 @@ trx_rollback_to_savepoint( /*******************************************************************//** Rollback a transaction used in MySQL. -@return error code or DB_SUCCESS */ +@return error code or DB_SUCCESS */ static dberr_t trx_rollback_for_mysql_low( @@ -181,14 +189,13 @@ trx_rollback_for_mysql_low( return(trx->error_state); } -/*******************************************************************//** -Rollback a transaction used in MySQL. -@return error code or DB_SUCCESS */ -UNIV_INTERN +/** Rollback a transaction used in MySQL +@param[in, out] trx transaction +@return error code or DB_SUCCESS */ +static dberr_t -trx_rollback_for_mysql( -/*===================*/ - trx_t* trx) /*!< in/out: transaction */ +trx_rollback_low( + trx_t* trx) { /* We are reading trx->state without holding trx_sys->mutex here, because the rollback should be invoked for a running @@ -196,9 +203,12 @@ trx_rollback_for_mysql( that is associated with the current thread. */ switch (trx->state) { + case TRX_STATE_FORCED_ROLLBACK: case TRX_STATE_NOT_STARTED: + trx->will_lock = 0; ut_ad(trx->in_mysql_trx_list); - return(DB_SUCCESS); + return(trx->state == TRX_STATE_NOT_STARTED + ? DB_SUCCESS : DB_FORCED_ABORT); case TRX_STATE_ACTIVE: ut_ad(trx->in_mysql_trx_list); @@ -207,10 +217,54 @@ trx_rollback_for_mysql( case TRX_STATE_PREPARED: ut_ad(!trx_is_autocommit_non_locking(trx)); + if (trx->rsegs.m_redo.rseg != NULL + && trx_is_redo_rseg_updated(trx)) { + /* Change the undo log state back from + TRX_UNDO_PREPARED to TRX_UNDO_ACTIVE + so that if the system gets killed, + recovery will perform the rollback. */ + trx_undo_ptr_t* undo_ptr = &trx->rsegs.m_redo; + mtr_t mtr; + mtr.start(); + mutex_enter(&trx->rsegs.m_redo.rseg->mutex); + if (undo_ptr->insert_undo != NULL) { + trx_undo_set_state_at_prepare( + trx, undo_ptr->insert_undo, + true, &mtr); + } + if (undo_ptr->update_undo != NULL) { + trx_undo_set_state_at_prepare( + trx, undo_ptr->update_undo, + true, &mtr); + } + mutex_exit(&trx->rsegs.m_redo.rseg->mutex); + /* Persist the XA ROLLBACK, so that crash + recovery will replay the rollback in case + the redo log gets applied past this point. */ + mtr.commit(); + ut_ad(mtr.commit_lsn() > 0); + } +#ifdef ENABLED_DEBUG_SYNC + if (trx->mysql_thd == NULL) { + /* We could be executing XA ROLLBACK after + XA PREPARE and a server restart. */ + } else if (!trx_is_redo_rseg_updated(trx)) { + /* innobase_close_connection() may roll back a + transaction that did not generate any + persistent undo log. The DEBUG_SYNC + would cause an assertion failure for a + disconnected thread. + + NOTE: InnoDB will not know about the XID + if no persistent undo log was generated. */ + } else { + DEBUG_SYNC_C("trx_xa_rollback"); + } +#endif /* ENABLED_DEBUG_SYNC */ return(trx_rollback_for_mysql_low(trx)); case TRX_STATE_COMMITTED_IN_MEMORY: - assert_trx_in_list(trx); + check_trx_state(trx); break; } @@ -219,9 +273,35 @@ trx_rollback_for_mysql( } /*******************************************************************//** +Rollback a transaction used in MySQL. +@return error code or DB_SUCCESS */ +dberr_t +trx_rollback_for_mysql( +/*===================*/ + trx_t* trx) /*!< in/out: transaction */ +{ + /* Avoid the tracking of async rollback killer + thread to enter into InnoDB. */ + if (TrxInInnoDB::is_async_rollback(trx)) { + + return(trx_rollback_low(trx)); + + } else { + + TrxInInnoDB trx_in_innodb(trx, true); + + if (trx_in_innodb.is_aborted()) { + + return(DB_FORCED_ABORT); + } + + return(trx_rollback_low(trx)); + } +} + +/*******************************************************************//** Rollback the latest SQL statement for MySQL. -@return error code or DB_SUCCESS */ -UNIV_INTERN +@return error code or DB_SUCCESS */ dberr_t trx_rollback_last_sql_stat_for_mysql( /*=================================*/ @@ -236,8 +316,12 @@ trx_rollback_last_sql_stat_for_mysql( ut_ad(trx->in_mysql_trx_list); switch (trx->state) { + case TRX_STATE_FORCED_ROLLBACK: + return(DB_FORCED_ABORT); + case TRX_STATE_NOT_STARTED: return(DB_SUCCESS); + case TRX_STATE_ACTIVE: assert_trx_nonlocking_or_in_list(trx); @@ -246,7 +330,7 @@ trx_rollback_last_sql_stat_for_mysql( err = trx_rollback_to_savepoint( trx, &trx->last_sql_stat_start); - if (trx->fts_trx) { + if (trx->fts_trx != NULL) { fts_savepoint_rollback_last_stmt(trx); } @@ -257,6 +341,7 @@ trx_rollback_last_sql_stat_for_mysql( trx->op_info = ""; return(err); + case TRX_STATE_PREPARED: case TRX_STATE_COMMITTED_IN_MEMORY: /* The statement rollback is only allowed on an ACTIVE @@ -301,14 +386,14 @@ trx_roll_savepoint_free( trx_t* trx, /*!< in: transaction handle */ trx_named_savept_t* savep) /*!< in: savepoint to free */ { - UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep); - mem_free(savep->name); - mem_free(savep); + UT_LIST_REMOVE(trx->trx_savepoints, savep); + + ut_free(savep->name); + ut_free(savep); } /*******************************************************************//** Frees savepoint structs starting from savep. */ -UNIV_INTERN void trx_roll_savepoints_free( /*=====================*/ @@ -342,7 +427,7 @@ trx_rollback_to_savepoint_for_mysql_low( /*====================================*/ trx_t* trx, /*!< in/out: transaction */ trx_named_savept_t* savep, /*!< in/out: savepoint */ - ib_int64_t* mysql_binlog_cache_pos) + int64_t* mysql_binlog_cache_pos) /*!< out: the MySQL binlog cache position corresponding to this savepoint; MySQL needs @@ -374,6 +459,12 @@ trx_rollback_to_savepoint_for_mysql_low( trx->op_info = ""; +#ifdef WITH_WSREP + if (wsrep_on(trx->mysql_thd) && + trx->lock.was_chosen_as_deadlock_victim) { + trx->lock.was_chosen_as_deadlock_victim = FALSE; + } +#endif return(err); } @@ -386,13 +477,12 @@ the row, these locks are naturally released in the rollback. Savepoints which were set after this savepoint are deleted. @return if no savepoint of the name found then DB_NO_SAVEPOINT, otherwise DB_SUCCESS */ -UNIV_INTERN dberr_t trx_rollback_to_savepoint_for_mysql( /*================================*/ trx_t* trx, /*!< in: transaction handle */ const char* savepoint_name, /*!< in: savepoint name */ - ib_int64_t* mysql_binlog_cache_pos) /*!< out: the MySQL binlog cache + int64_t* mysql_binlog_cache_pos) /*!< out: the MySQL binlog cache position corresponding to this savepoint; MySQL needs this information to remove the @@ -415,14 +505,19 @@ trx_rollback_to_savepoint_for_mysql( switch (trx->state) { case TRX_STATE_NOT_STARTED: - ut_print_timestamp(stderr); - fputs(" InnoDB: Error: transaction has a savepoint ", stderr); - ut_print_name(stderr, trx, FALSE, savep->name); - fputs(" though it is not started\n", stderr); + case TRX_STATE_FORCED_ROLLBACK: + + ib::error() << "Transaction has a savepoint " + << savep->name + << " though it is not started"; + return(DB_ERROR); + case TRX_STATE_ACTIVE: + return(trx_rollback_to_savepoint_for_mysql_low( trx, savep, mysql_binlog_cache_pos)); + case TRX_STATE_PREPARED: case TRX_STATE_COMMITTED_IN_MEMORY: /* The savepoint rollback is only allowed on an ACTIVE @@ -439,36 +534,36 @@ Creates a named savepoint. If the transaction is not yet started, starts it. If there is already a savepoint of the same name, this call erases that old savepoint and replaces it with a new. Savepoints are deleted in a transaction commit or rollback. -@return always DB_SUCCESS */ -UNIV_INTERN +@return always DB_SUCCESS */ dberr_t trx_savepoint_for_mysql( /*====================*/ trx_t* trx, /*!< in: transaction handle */ const char* savepoint_name, /*!< in: savepoint name */ - ib_int64_t binlog_cache_pos) /*!< in: MySQL binlog cache + int64_t binlog_cache_pos) /*!< in: MySQL binlog cache position corresponding to this connection at the time of the savepoint */ { trx_named_savept_t* savep; - trx_start_if_not_started_xa(trx); + trx_start_if_not_started_xa(trx, false); savep = trx_savepoint_find(trx, savepoint_name); if (savep) { /* There is a savepoint with the same name: free that */ - UT_LIST_REMOVE(trx_savepoints, trx->trx_savepoints, savep); + UT_LIST_REMOVE(trx->trx_savepoints, savep); - mem_free(savep->name); - mem_free(savep); + ut_free(savep->name); + ut_free(savep); } /* Create a new savepoint and add it as the last in the list */ - savep = static_cast<trx_named_savept_t*>(mem_alloc(sizeof(*savep))); + savep = static_cast<trx_named_savept_t*>( + ut_malloc_nokey(sizeof(*savep))); savep->name = mem_strdup(savepoint_name); @@ -476,7 +571,7 @@ trx_savepoint_for_mysql( savep->mysql_binlog_cache_pos = binlog_cache_pos; - UT_LIST_ADD_LAST(trx_savepoints, trx->trx_savepoints, savep); + UT_LIST_ADD_LAST(trx->trx_savepoints, savep); return(DB_SUCCESS); } @@ -486,7 +581,6 @@ Releases only the named savepoint. Savepoints which were set after this savepoint are left as is. @return if no savepoint of the name found then DB_NO_SAVEPOINT, otherwise DB_SUCCESS */ -UNIV_INTERN dberr_t trx_release_savepoint_for_mysql( /*============================*/ @@ -512,7 +606,6 @@ Determines if this transaction is rolling back an incomplete transaction in crash recovery. @return TRUE if trx is an incomplete transaction that is being rolled back in crash recovery */ -UNIV_INTERN ibool trx_is_recv( /*========*/ @@ -523,8 +616,7 @@ trx_is_recv( /*******************************************************************//** Returns a transaction savepoint taken at this point in time. -@return savepoint */ -UNIV_INTERN +@return savepoint */ trx_savept_t trx_savept_take( /*============*/ @@ -550,7 +642,7 @@ trx_rollback_active( que_thr_t* thr; roll_node_t* roll_node; dict_table_t* table; - ib_int64_t rows_to_undo; + int64_t rows_to_undo; const char* unit = ""; ibool dictionary_locked = FALSE; @@ -570,7 +662,7 @@ trx_rollback_active( ut_a(thr == que_fork_start_command(fork)); - mutex_enter(&trx_sys->mutex); + trx_sys_mutex_enter(); trx_roll_crash_recv_trx = trx; @@ -580,19 +672,17 @@ trx_rollback_active( rows_to_undo = trx_roll_max_undo_no; - mutex_exit(&trx_sys->mutex); + trx_sys_mutex_exit(); if (rows_to_undo > 1000000000) { rows_to_undo = rows_to_undo / 1000000; unit = "M"; } - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Rolling back trx with id " TRX_ID_FMT ", %lu%s" - " rows to undo\n", - trx->id, - (ulong) rows_to_undo, unit); + const trx_id_t trx_id = trx_get_id_for_print(trx); + + ib::info() << "Rolling back trx with id " << trx_id << ", " + << rows_to_undo << unit << " rows to undo"; if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { row_mysql_lock_data_dictionary(trx); @@ -615,36 +705,23 @@ trx_rollback_active( if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE && trx->table_id != 0) { + ut_ad(dictionary_locked); + /* If the transaction was for a dictionary operation, we drop the relevant table only if it is not flagged as DISCARDED. If it still exists. */ table = dict_table_open_on_id( - trx->table_id, dictionary_locked, - DICT_TABLE_OP_NORMAL); + trx->table_id, TRUE, DICT_TABLE_OP_NORMAL); if (table && !dict_table_is_discarded(table)) { + ib::warn() << "Dropping table '" << table->name + << "', with id " << trx->table_id + << " in recovery"; - dberr_t err; - - /* Ensure that the table doesn't get evicted from the - cache, keeps things simple for drop. */ + dict_table_close_and_drop(trx, table); - if (table->can_be_evicted) { - dict_table_move_from_lru_to_non_lru(table); - } - - dict_table_close(table, dictionary_locked, FALSE); - - ib_logf(IB_LOG_LEVEL_WARN, - "Dropping table '%s', with id " UINT64PF " " - "in recovery", - table->name, trx->table_id); - - err = row_drop_table_for_mysql(table->name, trx, TRUE, FALSE); trx_commit_for_mysql(trx); - - ut_a(err == DB_SUCCESS); } } @@ -652,8 +729,7 @@ trx_rollback_active( row_mysql_unlock_data_dictionary(trx); } - ib_logf(IB_LOG_LEVEL_INFO, - "Rollback of trx with id " TRX_ID_FMT " completed", trx->id); + ib::info() << "Rollback of trx with id " << trx_id << " completed"; mem_heap_free(heap); @@ -674,7 +750,7 @@ trx_rollback_resurrected( ibool all) /*!< in: FALSE=roll back dictionary transactions; TRUE=roll back all non-PREPARED transactions */ { - ut_ad(mutex_own(&trx_sys->mutex)); + 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 @@ -692,16 +768,16 @@ trx_rollback_resurrected( switch (state) { case TRX_STATE_COMMITTED_IN_MEMORY: - mutex_exit(&trx_sys->mutex); - fprintf(stderr, - "InnoDB: Cleaning up trx with id " TRX_ID_FMT "\n", - trx->id); + trx_sys_mutex_exit(); + ib::info() << "Cleaning up trx with id " + << trx_get_id_for_print(trx); + trx_cleanup_at_db_startup(trx); - trx_free_for_background(trx); + trx_free_resurrected(trx); return(TRUE); case TRX_STATE_ACTIVE: if (all || trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { - mutex_exit(&trx_sys->mutex); + trx_sys_mutex_exit(); trx_rollback_active(trx); trx_free_for_background(trx); return(TRUE); @@ -710,6 +786,7 @@ trx_rollback_resurrected( case TRX_STATE_PREPARED: return(FALSE); case TRX_STATE_NOT_STARTED: + case TRX_STATE_FORCED_ROLLBACK: break; } @@ -722,7 +799,6 @@ 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. */ -UNIV_INTERN void trx_rollback_or_clean_recovered( /*============================*/ @@ -739,9 +815,8 @@ trx_rollback_or_clean_recovered( } if (all) { - fprintf(stderr, - "InnoDB: Starting in background the rollback" - " of uncommitted transactions\n"); + ib::info() << "Starting in background the rollback" + " of uncommitted transactions"; } /* Note: For XA recovered transactions, we rely on MySQL to @@ -753,7 +828,7 @@ trx_rollback_or_clean_recovered( recovered transactions to clean up or recover. */ do { - mutex_enter(&trx_sys->mutex); + trx_sys_mutex_enter(); for (trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list); trx != NULL; @@ -767,21 +842,19 @@ trx_rollback_or_clean_recovered( if (trx_rollback_resurrected(trx, all)) { - mutex_enter(&trx_sys->mutex); + trx_sys_mutex_enter(); break; } } - mutex_exit(&trx_sys->mutex); + trx_sys_mutex_exit(); } while (trx != NULL); if (all) { - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Rollback of non-prepared" - " transactions completed\n"); + ib::info() << "Rollback of non-prepared transactions" + " completed"; } } @@ -791,8 +864,8 @@ 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: this is done in a background thread. -@return a dummy parameter */ -extern "C" UNIV_INTERN +@return a dummy parameter */ +extern "C" os_thread_ret_t DECLARE_THREAD(trx_rollback_or_clean_all_recovered)( /*================================================*/ @@ -803,7 +876,8 @@ DECLARE_THREAD(trx_rollback_or_clean_all_recovered)( ut_ad(!srv_read_only_mode); #ifdef UNIV_PFS_THREAD - pfs_register_thread(trx_rollback_clean_thread_key); + // JAN: TODO: MYSQL 5.7 PSI + // pfs_register_thread(trx_rollback_clean_thread_key); #endif /* UNIV_PFS_THREAD */ trx_rollback_or_clean_recovered(TRUE); @@ -818,208 +892,40 @@ DECLARE_THREAD(trx_rollback_or_clean_all_recovered)( OS_THREAD_DUMMY_RETURN; } -/*******************************************************************//** -Creates an undo number array. -@return own: undo number array */ -static -trx_undo_arr_t* -trx_undo_arr_create( -/*================*/ - ulint n_cells) /*!< Number of cells */ -{ - trx_undo_arr_t* arr; - mem_heap_t* heap; - ulint sz = sizeof(*arr) + sizeof(*arr->infos) * n_cells; - - heap = mem_heap_create(sz); - - arr = static_cast<trx_undo_arr_t*>(mem_heap_zalloc(heap, sz)); - - arr->n_cells = n_cells; - - arr->infos = (trx_undo_inf_t*) (arr + 1); - - arr->heap = heap; - - return(arr); -} - -/*******************************************************************//** -Frees an undo number array. */ -UNIV_INTERN -void -trx_undo_arr_free( -/*==============*/ - trx_undo_arr_t* arr) /*!< in: undo number array */ -{ - mem_heap_free(arr->heap); -} - -/*******************************************************************//** -Stores info of an undo log record to the array if it is not stored yet. -@return FALSE if the record already existed in the array */ -static -ibool -trx_undo_arr_store_info( -/*====================*/ - trx_t* trx, /*!< in: transaction */ - undo_no_t undo_no)/*!< in: undo number */ -{ - ulint i; - trx_undo_arr_t* arr; - ulint n = 0; - ulint n_used; - trx_undo_inf_t* stored_here = NULL; - - arr = trx->undo_no_arr; - n_used = arr->n_used; - - for (i = 0; i < arr->n_cells; i++) { - trx_undo_inf_t* cell; - - cell = trx_undo_arr_get_nth_info(arr, i); - - if (!cell->in_use) { - if (!stored_here) { - /* Not in use, we may store here */ - cell->undo_no = undo_no; - cell->in_use = TRUE; - - arr->n_used++; - - stored_here = cell; - } - } else { - n++; - - if (cell->undo_no == undo_no) { - - if (stored_here) { - stored_here->in_use = FALSE; - ut_ad(arr->n_used > 0); - arr->n_used--; - } - - ut_ad(arr->n_used == n_used); - - return(FALSE); - } - } - - if (n == n_used && stored_here) { - - ut_ad(arr->n_used == 1 + n_used); - - return(TRUE); - } - } - - ut_error; - - return(FALSE); -} - -/*******************************************************************//** -Removes an undo number from the array. */ -static -void -trx_undo_arr_remove_info( -/*=====================*/ - trx_undo_arr_t* arr, /*!< in: undo number array */ - undo_no_t undo_no)/*!< in: undo number */ -{ - ulint i; - - for (i = 0; i < arr->n_cells; i++) { - - trx_undo_inf_t* cell; - - cell = trx_undo_arr_get_nth_info(arr, i); - - if (cell->in_use && cell->undo_no == undo_no) { - cell->in_use = FALSE; - ut_ad(arr->n_used > 0); - --arr->n_used; - break; - } - } -} - -/*******************************************************************//** -Gets the biggest undo number in an array. -@return biggest value, 0 if the array is empty */ -static -undo_no_t -trx_undo_arr_get_biggest( -/*=====================*/ - const trx_undo_arr_t* arr) /*!< in: undo number array */ -{ - ulint i; - undo_no_t biggest = 0; - ulint n_checked = 0; - - for (i = 0; i < arr->n_cells && n_checked < arr->n_used; ++i) { - - const trx_undo_inf_t* cell = &arr->infos[i]; - - if (cell->in_use) { - - ++n_checked; - - if (cell->undo_no > biggest) { - - biggest = cell->undo_no; - } - } - } - - return(biggest); -} - /***********************************************************************//** Tries truncate the undo logs. */ static void trx_roll_try_truncate( /*==================*/ - trx_t* trx) /*!< in/out: transaction */ + trx_t* trx, /*!< in/out: transaction */ + trx_undo_ptr_t* undo_ptr) /*!< in: rollback segment to look + for next undo log record. */ { - undo_no_t limit; - const trx_undo_arr_t* arr; - - ut_ad(mutex_own(&(trx->undo_mutex))); - ut_ad(mutex_own(&((trx->rseg)->mutex))); + ut_ad(mutex_own(&trx->undo_mutex)); + ut_ad(mutex_own(&undo_ptr->rseg->mutex)); trx->pages_undone = 0; - arr = trx->undo_no_arr; - - limit = trx->undo_no; - - if (arr->n_used > 0) { - undo_no_t biggest; - - biggest = trx_undo_arr_get_biggest(arr); - - if (biggest >= limit) { - - limit = biggest + 1; - } + if (undo_ptr->insert_undo) { + trx_undo_truncate_end(trx, undo_ptr->insert_undo, trx->undo_no); } - if (trx->insert_undo) { - trx_undo_truncate_end(trx, trx->insert_undo, limit); + if (undo_ptr->update_undo) { + trx_undo_truncate_end(trx, undo_ptr->update_undo, trx->undo_no); } - if (trx->update_undo) { - trx_undo_truncate_end(trx, trx->update_undo, limit); +#ifdef WITH_WSREP_OUT + if (wsrep_on(trx->mysql_thd)) { + trx->lock.was_chosen_as_deadlock_victim = FALSE; } +#endif /* WITH_WSREP */ } /***********************************************************************//** Pops the topmost undo log record in a single undo log and updates the info about the topmost record in the undo log memory struct. -@return undo log record, the page s-latched */ +@return undo log record, the page s-latched */ static trx_undo_rec_t* trx_roll_pop_top_rec( @@ -1028,23 +934,15 @@ trx_roll_pop_top_rec( trx_undo_t* undo, /*!< in: undo log */ mtr_t* mtr) /*!< in: mtr */ { - page_t* undo_page; - ulint offset; - trx_undo_rec_t* prev_rec; - page_t* prev_rec_page; - ut_ad(mutex_own(&trx->undo_mutex)); - undo_page = trx_undo_page_get_s_latched( - undo->space, undo->zip_size, undo->top_page_no, mtr); - - offset = undo->top_offset; + page_t* undo_page = trx_undo_page_get_s_latched( + page_id_t(undo->space, undo->top_page_no), + undo->page_size, mtr); - /* fprintf(stderr, "Thread %lu undoing trx " TRX_ID_FMT - " undo record " TRX_ID_FMT "\n", - os_thread_get_curr_id(), trx->id, undo->top_undo_no); */ + ulint offset = undo->top_offset; - prev_rec = trx_undo_get_prev_rec( + trx_undo_rec_t* prev_rec = trx_undo_get_prev_rec( undo_page + offset, undo->hdr_page_no, undo->hdr_offset, true, mtr); @@ -1052,7 +950,7 @@ trx_roll_pop_top_rec( undo->empty = TRUE; } else { - prev_rec_page = page_align(prev_rec); + page_t* prev_rec_page = page_align(prev_rec); if (prev_rec_page != undo_page) { @@ -1067,22 +965,21 @@ trx_roll_pop_top_rec( return(undo_page + offset); } + /********************************************************************//** Pops the topmost record when the two undo logs of a transaction are seen -as a single stack of records ordered by their undo numbers. Inserts the -undo number of the popped undo record to the array of currently processed -undo numbers in the transaction. When the query thread finishes processing -of this undo record, it must be released with trx_undo_rec_release. +as a single stack of records ordered by their undo numbers. @return undo log record copied to heap, NULL if none left, or if the undo number of the top record would be less than the limit */ -UNIV_INTERN trx_undo_rec_t* -trx_roll_pop_top_rec_of_trx( -/*========================*/ - trx_t* trx, /*!< in: transaction */ - undo_no_t limit, /*!< in: least undo number we need */ - roll_ptr_t* roll_ptr,/*!< out: roll pointer to undo record */ - mem_heap_t* heap) /*!< in: memory heap where copied */ +trx_roll_pop_top_rec_of_trx_low( +/*============================*/ + trx_t* trx, /*!< in/out: transaction */ + trx_undo_ptr_t* undo_ptr, /*!< in: rollback segment to look + for next undo log record. */ + undo_no_t limit, /*!< in: least undo number we need */ + roll_ptr_t* roll_ptr, /*!< out: roll pointer to undo record */ + mem_heap_t* heap) /*!< in/out: memory heap where copied */ { trx_undo_t* undo; trx_undo_t* ins_undo; @@ -1092,23 +989,22 @@ trx_roll_pop_top_rec_of_trx( undo_no_t undo_no; ibool is_insert; trx_rseg_t* rseg; - ulint progress_pct; mtr_t mtr; - rseg = trx->rseg; -try_again: - mutex_enter(&(trx->undo_mutex)); + rseg = undo_ptr->rseg; + + mutex_enter(&trx->undo_mutex); if (trx->pages_undone >= TRX_ROLL_TRUNC_THRESHOLD) { mutex_enter(&rseg->mutex); - trx_roll_try_truncate(trx); + trx_roll_try_truncate(trx, undo_ptr); mutex_exit(&rseg->mutex); } - ins_undo = trx->insert_undo; - upd_undo = trx->update_undo; + ins_undo = undo_ptr->insert_undo; + upd_undo = undo_ptr->update_undo; if (!ins_undo || ins_undo->empty) { undo = upd_undo; @@ -1121,19 +1017,10 @@ try_again: } if (!undo || undo->empty || limit > undo->top_undo_no) { - - if ((trx->undo_no_arr)->n_used == 0) { - /* Rollback is ending */ - - mutex_enter(&(rseg->mutex)); - - trx_roll_try_truncate(trx); - - mutex_exit(&(rseg->mutex)); - } - - mutex_exit(&(trx->undo_mutex)); - + mutex_enter(&rseg->mutex); + trx_roll_try_truncate(trx, undo_ptr); + mutex_exit(&rseg->mutex); + mutex_exit(&trx->undo_mutex); return(NULL); } @@ -1148,14 +1035,15 @@ try_again: undo_no = trx_undo_rec_get_undo_no(undo_rec); - ut_ad(undo_no + 1 == trx->undo_no); + ut_ad(trx_roll_check_undo_rec_ordering( + undo_no, undo->rseg->space, trx)); /* We print rollback progress info if we are in a crash recovery and the transaction has at least 1000 row operations to undo. */ if (trx == trx_roll_crash_recv_trx && trx_roll_max_undo_no > 1000) { - progress_pct = 100 - (ulint) + ulint progress_pct = 100 - (ulint) ((undo_no * 100) / trx_roll_max_undo_no); if (progress_pct != trx_roll_progress_printed_pct) { if (trx_roll_progress_printed_pct == 0) { @@ -1172,20 +1060,11 @@ try_again: } trx->undo_no = undo_no; - - if (!trx_undo_arr_store_info(trx, undo_no)) { - /* A query thread is already processing this undo log record */ - - mutex_exit(&(trx->undo_mutex)); - - mtr_commit(&mtr); - - goto try_again; - } + trx->undo_rseg_space = undo->rseg->space; undo_rec_copy = trx_undo_rec_copy(undo_rec, heap); - mutex_exit(&(trx->undo_mutex)); + mutex_exit(&trx->undo_mutex); mtr_commit(&mtr); @@ -1193,46 +1072,30 @@ try_again: } /********************************************************************//** -Reserves an undo log record for a query thread to undo. This should be -called if the query thread gets the undo log record not using the pop -function above. -@return TRUE if succeeded */ -UNIV_INTERN -ibool -trx_undo_rec_reserve( -/*=================*/ - trx_t* trx, /*!< in/out: transaction */ - undo_no_t undo_no)/*!< in: undo number of the record */ -{ - ibool ret; - - mutex_enter(&(trx->undo_mutex)); - - ret = trx_undo_arr_store_info(trx, undo_no); - - mutex_exit(&(trx->undo_mutex)); - - return(ret); -} - -/*******************************************************************//** -Releases a reserved undo record. */ -UNIV_INTERN -void -trx_undo_rec_release( -/*=================*/ - trx_t* trx, /*!< in/out: transaction */ - undo_no_t undo_no)/*!< in: undo number */ +Get next undo log record from redo and noredo rollback segments. +@return undo log record copied to heap, NULL if none left, or if the +undo number of the top record would be less than the limit */ +trx_undo_rec_t* +trx_roll_pop_top_rec_of_trx( +/*========================*/ + trx_t* trx, /*!< in: transaction */ + undo_no_t limit, /*!< in: least undo number we need */ + roll_ptr_t* roll_ptr, /*!< out: roll pointer to undo record */ + mem_heap_t* heap) /*!< in: memory heap where copied */ { - trx_undo_arr_t* arr; + trx_undo_rec_t* undo_rec = 0; - mutex_enter(&(trx->undo_mutex)); - - arr = trx->undo_no_arr; + if (trx_is_redo_rseg_updated(trx)) { + undo_rec = trx_roll_pop_top_rec_of_trx_low( + trx, &trx->rsegs.m_redo, limit, roll_ptr, heap); + } - trx_undo_arr_remove_info(arr, undo_no); + if (undo_rec == 0 && trx_is_noredo_rseg_updated(trx)) { + undo_rec = trx_roll_pop_top_rec_of_trx_low( + trx, &trx->rsegs.m_noredo, limit, roll_ptr, heap); + } - mutex_exit(&(trx->undo_mutex)); + return(undo_rec); } /****************************************************************//** @@ -1240,12 +1103,12 @@ Builds an undo 'query' graph for a transaction. The actual rollback is performed by executing this query graph like a query subprocedure call. The reply about the completion of the rollback will be sent by this graph. -@return own: the query graph */ +@return own: the query graph */ static que_t* trx_roll_graph_build( /*=================*/ - trx_t* trx) /*!< in: trx handle */ + trx_t* trx) /*!< in/out: transaction */ { mem_heap_t* heap; que_fork_t* fork; @@ -1277,28 +1140,23 @@ trx_rollback_start( partial undo), 0 if we are rolling back the entire transaction */ { - que_t* roll_graph; - ut_ad(trx_mutex_own(trx)); - ut_ad(trx->undo_no_arr == NULL || trx->undo_no_arr->n_used == 0); - /* Initialize the rollback field in the transaction */ + ut_ad(!trx->roll_limit); + ut_ad(!trx->in_rollback); + trx->roll_limit = roll_limit; + ut_d(trx->in_rollback = true); ut_a(trx->roll_limit <= trx->undo_no); trx->pages_undone = 0; - if (trx->undo_no_arr == NULL) { - /* Single query thread -> 1 */ - trx->undo_no_arr = trx_undo_arr_create(1); - } - /* Build a 'query' graph which will perform the undo operations */ - roll_graph = trx_roll_graph_build(trx); + que_t* roll_graph = trx_roll_graph_build(trx); trx->graph = roll_graph; @@ -1315,17 +1173,16 @@ trx_rollback_finish( /*================*/ trx_t* trx) /*!< in: transaction */ { - ut_a(trx->undo_no_arr == NULL || trx->undo_no_arr->n_used == 0); - trx_commit(trx); + trx->mod_tables.clear(); + trx->lock.que_state = TRX_QUE_RUNNING; } /*********************************************************************//** Creates a rollback command node struct. -@return own: rollback node struct */ -UNIV_INTERN +@return own: rollback node struct */ roll_node_t* roll_node_create( /*=============*/ @@ -1344,8 +1201,7 @@ roll_node_create( /***********************************************************//** Performs an execution step for a rollback command node in a query graph. -@return query thread to run next, or NULL */ -UNIV_INTERN +@return query thread to run next, or NULL */ que_thr_t* trx_rollback_step( /*==============*/ @@ -1363,7 +1219,7 @@ trx_rollback_step( if (node->state == ROLL_NODE_SEND) { trx_t* trx; - ib_id_t roll_limit = 0; + ib_id_t roll_limit; trx = thr_get_trx(thr); |