/*******************************************************************//** Rollback a transaction used in MySQL. @return error code or DB_SUCCESS */ UNIV_INTERN int trx_general_rollback_for_mysql( /*===========================*/ trx_t* trx, /*!< in: transaction handle */ trx_savept_t* savept) /*!< in: pointer to savepoint undo number, if partial rollback requested, or NULL for complete rollback */ { mem_heap_t* heap; que_thr_t* thr; roll_node_t* roll_node; /* Tell Innobase server that there might be work for utility threads: */ srv_active_wake_master_thread(); trx_start_if_not_started(trx); heap = mem_heap_create(512); roll_node = roll_node_create(heap); if (savept) { roll_node->partial = TRUE; roll_node->savept = *savept; } trx->error_state = DB_SUCCESS; thr = pars_complete_graph_for_exec(roll_node, trx, heap); ut_a(thr == que_fork_start_command(que_node_get_parent(thr))); que_run_threads(thr); mutex_enter(&kernel_mutex); while (trx->que_state != TRX_QUE_RUNNING) { mutex_exit(&kernel_mutex); os_thread_sleep(100000); mutex_enter(&kernel_mutex); } mutex_exit(&kernel_mutex); mem_heap_free(heap); ut_a(trx->error_state == DB_SUCCESS); /* Tell Innobase server that there might be work for utility threads: */ srv_active_wake_master_thread(); return((int) trx->error_state); }
/*******************************************************************//** This function runs a purge batch. @return number of undo log pages handled in the batch */ UNIV_INTERN ulint trx_purge( /*======*/ ulint limit) /*!< in: the maximum number of records to purge in one batch */ { que_thr_t* thr; ulint old_pages_handled; if (srv_fake_write) return(0); ut_a(purge_sys->trx->n_active_thrs == 0); rw_lock_x_lock(&purge_sys->latch); mutex_enter(&kernel_mutex); /* Close and free the old purge view */ read_view_close(purge_sys->view); purge_sys->view = NULL; mem_heap_empty(purge_sys->heap); /* Determine how much data manipulation language (DML) statements need to be delayed in order to reduce the lagging of the purge thread. */ srv_dml_needed_delay = 0; /* in microseconds; default: no delay */ /* If we cannot advance the 'purge view' because of an old 'consistent read view', then the DML statements cannot be delayed. Also, srv_max_purge_lag <= 0 means 'infinity'. */ if (srv_max_purge_lag > 0) { float ratio = (float) trx_sys->rseg_history_len / srv_max_purge_lag; if (ratio > ULINT_MAX / 10000) { /* Avoid overflow: maximum delay is 4295 seconds */ srv_dml_needed_delay = ULINT_MAX; } else if (ratio > 1) { /* If the history list length exceeds the innodb_max_purge_lag, the data manipulation statements are delayed by at least 5000 microseconds. */ srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000); } } purge_sys->view = read_view_oldest_copy_or_open_new( 0, purge_sys->heap); mutex_exit(&kernel_mutex); rw_lock_x_unlock(&(purge_sys->latch)); purge_sys->state = TRX_PURGE_ON; purge_sys->handle_limit = purge_sys->n_pages_handled + limit; old_pages_handled = purge_sys->n_pages_handled; mutex_enter(&kernel_mutex); thr = que_fork_start_command(purge_sys->query); ut_ad(thr); mutex_exit(&kernel_mutex); if (srv_print_thread_releases) { fputs("Starting purge\n", stderr); } que_run_threads(thr); if (srv_print_thread_releases) { fprintf(stderr, "Purge ends; pages handled %lu\n", (ulong) purge_sys->n_pages_handled); } return(purge_sys->n_pages_handled - old_pages_handled); }
/*******************************************************************//** This function runs a purge batch. @return number of undo log pages handled in the batch */ UNIV_INTERN ulint trx_purge(void) /*===========*/ { que_thr_t* thr; /* que_thr_t* thr2; */ ulonglong old_pages_handled; mutex_enter(&(purge_sys->mutex)); if (purge_sys->trx->n_active_thrs > 0) { mutex_exit(&(purge_sys->mutex)); /* Should not happen */ ut_error; return(0); } rw_lock_x_lock(&(purge_sys->latch)); mutex_enter(&kernel_mutex); /* Close and free the old purge view */ read_view_close(purge_sys->view); purge_sys->view = NULL; mem_heap_empty(purge_sys->heap); /* Determine how much data manipulation language (DML) statements need to be delayed in order to reduce the lagging of the purge thread. */ srv_dml_needed_delay = 0; /* in microseconds; default: no delay */ /* If we cannot advance the 'purge view' because of an old 'consistent read view', then the DML statements cannot be delayed. Also, srv_max_purge_lag <= 0 means 'infinity'. */ if (srv_max_purge_lag > 0 && !UT_LIST_GET_LAST(trx_sys->view_list)) { float ratio = (float) trx_sys->rseg_history_len / srv_max_purge_lag; if (ratio > ULINT_MAX / 10000) { /* Avoid overflow: maximum delay is 4295 seconds */ srv_dml_needed_delay = ULINT_MAX; } else if (ratio > 1) { /* If the history list length exceeds the innodb_max_purge_lag, the data manipulation statements are delayed by at least 5000 microseconds. */ srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000); } } purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero, purge_sys->heap); mutex_exit(&kernel_mutex); rw_lock_x_unlock(&(purge_sys->latch)); #ifdef UNIV_DEBUG if (srv_purge_view_update_only_debug) { mutex_exit(&(purge_sys->mutex)); return(0); } #endif purge_sys->state = TRX_PURGE_ON; /* Handle at most 20 undo log pages in one purge batch */ purge_sys->handle_limit = purge_sys->n_pages_handled + 20; old_pages_handled = purge_sys->n_pages_handled; mutex_exit(&(purge_sys->mutex)); mutex_enter(&kernel_mutex); thr = que_fork_start_command(purge_sys->query); ut_ad(thr); /* thr2 = que_fork_start_command(purge_sys->query); ut_ad(thr2); */ mutex_exit(&kernel_mutex); /* srv_que_task_enqueue(thr2); */ if (srv_print_thread_releases) { fputs("Starting purge\n", stderr); } que_run_threads(thr); if (srv_print_thread_releases) { fprintf(stderr, "Purge ends; pages handled %lu\n", (ulong) purge_sys->n_pages_handled); } return((ulint) (purge_sys->n_pages_handled - old_pages_handled)); }
/*******************************************************************//** Roll back an active transaction. */ static void trx_rollback_active( /*================*/ trx_t* trx) /*!< in/out: transaction */ { mem_heap_t* heap; que_fork_t* fork; que_thr_t* thr; roll_node_t* roll_node; dict_table_t* table; ib_int64_t rows_to_undo; const char* unit = ""; ibool dictionary_locked = FALSE; heap = mem_heap_create(512); fork = que_fork_create(NULL, NULL, QUE_FORK_RECOVERY, heap); fork->trx = trx; thr = que_thr_create(fork, heap); roll_node = roll_node_create(heap); thr->child = roll_node; roll_node->common.parent = thr; mutex_enter(&kernel_mutex); trx->graph = fork; ut_a(thr == que_fork_start_command(fork)); trx_roll_crash_recv_trx = trx; trx_roll_max_undo_no = trx->undo_no; trx_roll_progress_printed_pct = 0; rows_to_undo = trx_roll_max_undo_no; 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", (ullint) trx->id, (ulong) rows_to_undo, unit); mutex_exit(&kernel_mutex); trx->mysql_thread_id = os_thread_get_curr_id(); trx->mysql_process_no = os_proc_get_number(); if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { row_mysql_lock_data_dictionary(trx); dictionary_locked = TRUE; } que_run_threads(thr); mutex_enter(&kernel_mutex); while (trx->que_state != TRX_QUE_RUNNING) { mutex_exit(&kernel_mutex); fprintf(stderr, "InnoDB: Waiting for rollback of trx id " TRX_ID_FMT " to end\n", (ullint) trx->id); os_thread_sleep(100000); mutex_enter(&kernel_mutex); } mutex_exit(&kernel_mutex); if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE && trx->table_id != 0) { /* If the transaction was for a dictionary operation, we drop the relevant table, if it still exists */ fprintf(stderr, "InnoDB: Dropping table with id %llu" " in recovery if it exists\n", (ullint) trx->table_id); table = dict_table_get_on_id_low(trx->table_id); if (table) { ulint err; fputs("InnoDB: Table found: dropping table ", stderr); ut_print_name(stderr, trx, TRUE, table->name); fputs(" in recovery\n", stderr); err = row_drop_table_for_mysql(table->name, trx, TRUE); trx_commit_for_mysql(trx); ut_a(err == (int) DB_SUCCESS); } } if (dictionary_locked) { row_mysql_unlock_data_dictionary(trx); } fprintf(stderr, "\nInnoDB: Rolling back of trx id " TRX_ID_FMT " completed\n", (ullint) trx->id); mem_heap_free(heap); trx_roll_crash_recv_trx = NULL; }
enum db_err ib_trx_lock_table_with_retry( /*=========================*/ trx_t* trx, /*!< in/out: transaction */ dict_table_t* table, /*!< in: table to lock */ enum lock_mode mode) /*!< in: LOCK_X or LOCK_S */ { que_thr_t* thr; enum db_err err; mem_heap_t* heap; sel_node_t* node; ut_ad(trx->client_thread_id == os_thread_get_curr_id()); heap = mem_heap_create(512); trx->op_info = "setting table lock"; node = sel_node_create(heap); thr = pars_complete_graph_for_exec(node, trx, heap); thr->graph->state = QUE_FORK_ACTIVE; /* We use the select query graph as the dummy graph needed in the lock module call */ thr = que_fork_get_first_thr(que_node_get_parent(thr)); que_thr_move_to_run_state(thr); run_again: thr->run_node = thr; thr->prev_node = thr->common.parent; err = lock_table(0, table, mode, thr); trx->error_state = err; if (UNIV_LIKELY(err == DB_SUCCESS)) { que_thr_stop_for_client_no_error(thr, trx); } else { que_thr_stop_client(thr); if (err != DB_QUE_THR_SUSPENDED) { ibool was_lock_wait; was_lock_wait = ib_handle_errors(&err, trx, thr, NULL); if (was_lock_wait) { goto run_again; } } else { que_thr_t* run_thr; que_node_t* parent; parent = que_node_get_parent(thr); run_thr = que_fork_start_command(parent); ut_a(run_thr == thr); /* There was a lock wait but the thread was not in a ready to run or running state. */ trx->error_state = DB_LOCK_WAIT; goto run_again; } } que_graph_free(thr->graph); trx->op_info = ""; return(err); }