diff --git a/storage/innobase/include/trx0purge.h b/storage/innobase/include/trx0purge.h index 523b9c11990..f2ddb0b7646 100644 --- a/storage/innobase/include/trx0purge.h +++ b/storage/innobase/include/trx0purge.h @@ -73,7 +73,7 @@ void trx_purge_sys_close(void); /************************************************************************ Adds the update undo log as the first log in the history list. Removes the update undo log segment from the rseg slot if it is too big for reuse. */ -void trx_purge_add_update_undo_to_history( +void trx_purge_add_undo_to_history( trx_t *trx, /*!< in: transaction */ trx_undo_ptr_t *undo_ptr, /*!< in: update undo log. */ page_t *undo_page, /*!< in: update undo log header page, diff --git a/storage/innobase/include/trx0rseg.h b/storage/innobase/include/trx0rseg.h index 56772cda78a..92b3cb5fb55 100644 --- a/storage/innobase/include/trx0rseg.h +++ b/storage/innobase/include/trx0rseg.h @@ -230,6 +230,8 @@ The size is 8 bytes. */ /* Use to track max transaction id */ #define TRX_RSEG_MAX_TRX_ID (TRX_RSEG_MAX_TRX_NO + 8) + +#define TRX_RSEG_INSERT_HISTORY (TRX_RSEG_MAX_TRX_ID + 8) /*-------------------------------------------------------------*/ /** The offset of the Rollback Segment Directory header on an RSEG_ARRAY diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index 03d6db12642..332b6badd36 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -319,7 +319,7 @@ void trx_purge_sys_close() { /** Adds the update undo log as the first log in the history list. Removes the update undo log segment from the rseg slot if it is too big for reuse. */ -void trx_purge_add_update_undo_to_history( +void trx_purge_add_undo_to_history( trx_t *trx, /*!< in: transaction */ trx_undo_ptr_t *undo_ptr, /*!< in/out: update undo log. */ page_t *undo_page, /*!< in: update undo log header page, @@ -377,8 +377,13 @@ void trx_purge_add_update_undo_to_history( } /* Add the log as the first in the history list */ - flst_add_first(rseg_header + TRX_RSEG_HISTORY, - undo_header + TRX_UNDO_HISTORY_NODE, mtr); + if (is_insert) { + flst_add_first(rseg_header + TRX_RSEG_INSERT_HISTORY, + undo_header + TRX_UNDO_HISTORY_NODE, mtr); + } else { + flst_add_first(rseg_header + TRX_RSEG_HISTORY, + undo_header + TRX_UNDO_HISTORY_NODE, mtr); + } if (update_rseg_history_len) { trx_sys->rseg_history_len.fetch_add(n_added_logs); @@ -406,7 +411,7 @@ void trx_purge_add_update_undo_to_history( /* Write GTID information if there. */ trx_undo_gtid_write(trx, undo_header, undo, mtr, false); - if (rseg->last_page_no == FIL_NULL) { + if (!is_insert && rseg->last_page_no == FIL_NULL) { rseg->last_page_no = undo->hdr_page_no; rseg->last_offset = undo->hdr_offset; rseg->last_trx_no = trx->no; @@ -419,9 +424,14 @@ void trx_purge_add_update_undo_to_history( @param[in] log_hdr Undo log segment header @param[in,out] mtr Mini-transaction. */ static void trx_purge_remove_log_hdr(trx_rsegf_t *rseg_hdr, - trx_ulogf_t *log_hdr, mtr_t *mtr) { - flst_remove(rseg_hdr + TRX_RSEG_HISTORY, log_hdr + TRX_UNDO_HISTORY_NODE, - mtr); + trx_ulogf_t *log_hdr, mtr_t *mtr, bool is_insert) { + if (is_insert) { + flst_remove(rseg_hdr + TRX_RSEG_INSERT_HISTORY, log_hdr + TRX_UNDO_HISTORY_NODE, + mtr); + } else { + flst_remove(rseg_hdr + TRX_RSEG_HISTORY, log_hdr + TRX_UNDO_HISTORY_NODE, + mtr); + } trx_sys->rseg_history_len.fetch_sub(1); } @@ -432,7 +442,7 @@ Removes the rseg hdr from the history list. @param[in] hdr_addr file address of log_hdr @param[in] noredo skip redo logging. */ static void trx_purge_free_segment(trx_rseg_t *rseg, fil_addr_t hdr_addr, - bool noredo) { + bool noredo, bool is_insert) { mtr_t mtr; trx_rsegf_t *rseg_hdr; trx_ulogf_t *log_hdr; @@ -493,7 +503,7 @@ static void trx_purge_free_segment(trx_rseg_t *rseg, fil_addr_t hdr_addr, history list: otherwise, in case of a database crash, the segment could become inaccessible garbage in the file space. */ - trx_purge_remove_log_hdr(rseg_hdr, log_hdr, &mtr); + trx_purge_remove_log_hdr(rseg_hdr, log_hdr, &mtr, is_insert); do { /* Here we assume that a file segment with just the header @@ -541,10 +551,27 @@ static void trx_purge_truncate_rseg_history( rseg_hdr = trx_rsegf_get(rseg->space_id, rseg->page_no, rseg->page_size, &mtr); + bool is_insert_history = false; + hdr_addr = trx_purge_get_log_from_hist( flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr)); loop: if (hdr_addr.page == FIL_NULL) { + if (!is_insert_history + && !is_temp + && flst_get_len(rseg_hdr + TRX_RSEG_INSERT_HISTORY) > 0) { + is_insert_history = true; + mtr_commit(&mtr); + rseg->unlatch(); + mtr_start(&mtr); + rseg->latch(); + rseg_hdr = + trx_rsegf_get(rseg->space_id, rseg->page_no, rseg->page_size, &mtr); + hdr_addr = trx_purge_get_log_from_hist( + flst_get_last(rseg_hdr + TRX_RSEG_INSERT_HISTORY, &mtr)); + goto loop; + } + rseg->unlatch(); mtr_commit(&mtr); @@ -568,6 +595,11 @@ loop: limit->undo_no); } + if (!is_insert_history && !is_temp) { + hdr_addr.page = FIL_NULL; + goto loop; + } + rseg->unlatch(); mtr_commit(&mtr); @@ -588,12 +620,12 @@ loop: /* calls the trx_purge_remove_log_hdr() inside trx_purge_free_segment(). */ - trx_purge_free_segment(rseg, hdr_addr, is_temp); + trx_purge_free_segment(rseg, hdr_addr, is_temp, is_insert_history); } else { /* Remove the log hdr from the rseg history. */ - trx_purge_remove_log_hdr(rseg_hdr, log_hdr, &mtr); + trx_purge_remove_log_hdr(rseg_hdr, log_hdr, &mtr, is_insert_history); rseg->unlatch(); mtr_commit(&mtr); diff --git a/storage/innobase/trx/trx0rseg.cc b/storage/innobase/trx/trx0rseg.cc index c64b11d6eee..8b638be12b9 100644 --- a/storage/innobase/trx/trx0rseg.cc +++ b/storage/innobase/trx/trx0rseg.cc @@ -102,6 +102,8 @@ page_no_t trx_rseg_header_create(space_id_t space_id, mlog_write_ull(rsegf + TRX_RSEG_MAX_TRX_ID, 0, mtr); + flst_init(rsegf + TRX_RSEG_INSERT_HISTORY, mtr); + if (space_id == TRX_SYS_SPACE) { /* All rollback segments in the system tablespace need to be found in the TRX_SYS page in the rseg_id slot. @@ -248,6 +250,23 @@ static trx_rseg_t *trx_rseg_mem_initialize(ulint id, space_id_t space_id, return rseg; } +/* In case it's upgraded from older version, we need to +create history list for insert undo */ +static void trx_rseg_create_insert_history_if_needed( + trx_rseg_t *rseg, trx_rsegf_t *rseg_header, mtr_t *mtr) { + if (fsp_is_system_temporary(rseg->space_id)) { + return; + } + + flst_base_node_t* insert_base_node = rseg_header + TRX_RSEG_INSERT_HISTORY; + fil_faddr_t* faddr = insert_base_node + FLST_LAST; + auto page = mach_read_ulint(faddr + FIL_ADDR_PAGE, MLOG_4BYTES); + /* If newly created rseg it should be page no or FIL_NULL, but never be page zero */ + if (page == 0) { + flst_init(insert_base_node, mtr); + } +} + void trx_update_max_trx_id_startup(trx_id_t new_id) { trx_id_t old_id = trx_sys->resurrect_max_trx_id.load(); while (old_id < new_id @@ -276,6 +295,8 @@ static trx_rseg_t *trx_rseg_physical_initialize(trx_rseg_t *rseg, trx_update_max_trx_id_startup(rseg_max_trx_id); + trx_rseg_create_insert_history_if_needed(rseg, rseg_header, mtr); + auto len = flst_get_len(rseg_header + TRX_RSEG_HISTORY); if (len > 0) { @@ -419,6 +440,8 @@ trx_rseg_t *trx_rseg_mem_create(ulint id, space_id_t space_id, trx_update_max_trx_id_startup(rseg_max_trx_id); + trx_rseg_create_insert_history_if_needed(rseg, rseg_header, mtr); + if (len > 0) { trx_sys->rseg_history_len += len; diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 9b470944674..313a356479f 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -1564,7 +1564,7 @@ static bool trx_write_serialisation_history( rollback segments. */ trx_undo_ptr_t *redo_rseg_undo_ptr = - trx_is_redo_rseg_updated(trx) ? &trx->rsegs.m_redo : nullptr; + trx->rsegs.m_redo.update_undo != nullptr ? &trx->rsegs.m_redo : nullptr; trx_undo_ptr_t *temp_rseg_undo_ptr = trx->rsegs.m_noredo.update_undo != nullptr ? &trx->rsegs.m_noredo @@ -1595,7 +1595,7 @@ static bool trx_write_serialisation_history( auto undo_ptr = &trx->rsegs.m_redo; trx_undo_gtid_set(trx, undo_ptr->update_undo, false); - trx_undo_update_cleanup(trx, undo_ptr, undo_hdr_page, update_rseg_len, false, + trx_undo_update_cleanup(trx, undo_ptr, undo_hdr_page, update_rseg_len, false, (update_rseg_len ? added_log : 0), mtr); } @@ -1608,7 +1608,7 @@ static bool trx_write_serialisation_history( undo_hdr_page = trx_undo_set_state_at_finish(trx->rsegs.m_redo.insert_undo, mtr); - bool update_rseg_len = !(trx->rsegs.m_noredo.update_undo != nullptr); + bool update_rseg_len = (trx->rsegs.m_noredo.update_undo == nullptr); auto undo_ptr = &trx->rsegs.m_redo; trx_undo_update_cleanup(trx, undo_ptr, undo_hdr_page, update_rseg_len, true, (update_rseg_len ? added_log : 0), mtr); diff --git a/storage/innobase/trx/trx0undo.cc b/storage/innobase/trx/trx0undo.cc index f48ce62203a..acd86771b9f 100644 --- a/storage/innobase/trx/trx0undo.cc +++ b/storage/innobase/trx/trx0undo.cc @@ -1951,7 +1951,7 @@ void trx_undo_update_cleanup(trx_t *trx, trx_undo_ptr_t *undo_ptr, ut_ad(mutex_own(&(rseg->mutex))); - trx_purge_add_update_undo_to_history( + trx_purge_add_undo_to_history( trx, undo_ptr, undo_page, update_rseg_history_len, is_insert, n_added_logs, mtr); if (is_insert) {