diff options
Diffstat (limited to 'storage/innobase/trx/trx0undo.cc')
-rw-r--r-- | storage/innobase/trx/trx0undo.cc | 794 |
1 files changed, 414 insertions, 380 deletions
diff --git a/storage/innobase/trx/trx0undo.cc b/storage/innobase/trx/trx0undo.cc index 9b0c43e4609..88ac0257540 100644 --- a/storage/innobase/trx/trx0undo.cc +++ b/storage/innobase/trx/trx0undo.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2014, 2017, MariaDB Corporation. +Copyright (c) 2014, 2018, MariaDB Corporation. 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 @@ -117,6 +117,53 @@ trx_undo_mem_create( ulint page_no,/*!< in: undo log header page number */ ulint offset);/*!< in: undo log header byte offset on page */ +/** Determine the start offset of undo log records of an undo log page. +@param[in] undo_page undo log page +@param[in] page_no undo log header page number +@param[in] offset undo log header offset +@return start offset */ +static +uint16_t +trx_undo_page_get_start(const page_t* undo_page, ulint page_no, ulint offset) +{ + return page_no == page_get_page_no(undo_page) + ? mach_read_from_2(offset + TRX_UNDO_LOG_START + undo_page) + : TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE; +} + +/** Get the first undo log record on a page. +@param[in] page undo log page +@param[in] page_no undo log header page number +@param[in] offset undo log header page offset +@return pointer to first record +@retval NULL if none exists */ +static +trx_undo_rec_t* +trx_undo_page_get_first_rec(page_t* page, ulint page_no, ulint offset) +{ + ulint start = trx_undo_page_get_start(page, page_no, offset); + return start == trx_undo_page_get_end(page, page_no, offset) + ? NULL + : page + start; +} + +/** Get the last undo log record on a page. +@param[in] page undo log page +@param[in] page_no undo log header page number +@param[in] offset undo log header page offset +@return pointer to last record +@retval NULL if none exists */ +static +trx_undo_rec_t* +trx_undo_page_get_last_rec(page_t* page, ulint page_no, ulint offset) +{ + ulint end = trx_undo_page_get_end(page, page_no, offset); + + return trx_undo_page_get_start(page, page_no, offset) == end + ? NULL + : page + mach_read_from_2(page + end - 2); +} + /***********************************************************************//** Gets the previous record in an undo log from the previous page. @return undo log record, the page s-latched, NULL if none */ @@ -159,6 +206,31 @@ trx_undo_get_prev_rec_from_prev_page( return(trx_undo_page_get_last_rec(prev_page, page_no, offset)); } +/** Get the previous undo log record. +@param[in] rec undo log record +@param[in] page_no undo log header page number +@param[in] offset undo log header page offset +@return pointer to record +@retval NULL if none */ +static +trx_undo_rec_t* +trx_undo_page_get_prev_rec(trx_undo_rec_t* rec, ulint page_no, ulint offset) +{ + page_t* undo_page; + ulint start; + + undo_page = (page_t*) ut_align_down(rec, UNIV_PAGE_SIZE); + + start = trx_undo_page_get_start(undo_page, page_no, offset); + + if (start + undo_page == rec) { + + return(NULL); + } + + return(undo_page + mach_read_from_2(rec - 2)); +} + /***********************************************************************//** Gets the previous record in an undo log. @return undo log record, the page s-latched, NULL if none */ @@ -309,43 +381,84 @@ trx_undo_get_first_rec( /*============== UNDO LOG FILE COPY CREATION AND FREEING ==================*/ -/**********************************************************************//** -Writes the mtr log entry of an undo log page initialization. */ -UNIV_INLINE -void -trx_undo_page_init_log( -/*===================*/ - page_t* undo_page, /*!< in: undo log page */ - mtr_t* mtr) /*!< in: mtr */ +/** Parse MLOG_UNDO_INIT for crash-upgrade from MariaDB 10.2. +@param[in] ptr log record +@param[in] end_ptr end of log record buffer +@param[in,out] page page or NULL +@param[in,out] mtr mini-transaction +@return end of log record +@retval NULL if the log record is incomplete */ +byte* +trx_undo_parse_page_init( + const byte* ptr, + const byte* end_ptr, + page_t* page, + mtr_t* mtr) { - mlog_write_initial_log_record(undo_page, MLOG_UNDO_INIT, mtr); + ulint type = mach_parse_compressed(&ptr, end_ptr); - mlog_catenate_ulint_compressed(mtr, 0); + if (!ptr) { + } else if (type != 1 && type != 2) { + recv_sys->found_corrupt_log = true; + } else if (page) { + mach_write_to_2(FIL_PAGE_TYPE + page, FIL_PAGE_UNDO_LOG); + mach_write_to_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE + page, + type); + mach_write_to_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_START + page, + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE); + mach_write_to_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE + page, + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE); + } + + return(const_cast<byte*>(ptr)); } -/***********************************************************//** -Parses the redo log entry of an undo log page initialization. +/** Parse MLOG_UNDO_HDR_REUSE for crash-upgrade from MariaDB 10.2. +@param[in] ptr redo log record +@param[in] end_ptr end of log buffer +@param[in,out] page undo log page or NULL +@param[in,out] mtr mini-transaction @return end of log record or NULL */ byte* -trx_undo_parse_page_init( -/*=====================*/ - const byte* ptr, /*!< in: buffer */ - const byte* end_ptr,/*!< in: buffer end */ - page_t* page, /*!< in: page or NULL */ - mtr_t* mtr) /*!< in: mtr or NULL */ +trx_undo_parse_page_header_reuse( + const byte* ptr, + const byte* end_ptr, + page_t* undo_page, + mtr_t* mtr) { - if (mach_parse_compressed(&ptr, end_ptr)) { - recv_sys->found_corrupt_log = true; + trx_id_t trx_id = mach_u64_parse_compressed(&ptr, end_ptr); + + if (!ptr || !undo_page) { + return(const_cast<byte*>(ptr)); } - if (ptr == NULL) { + compile_time_assert(TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE + + TRX_UNDO_LOG_XA_HDR_SIZE + < UNIV_PAGE_SIZE_MIN - 100); - return(NULL); - } + const ulint new_free = TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE + + TRX_UNDO_LOG_OLD_HDR_SIZE; - if (page) { - trx_undo_page_init(page, mtr); - } + /* Insert undo data is not needed after commit: we may free all + the space on the page */ + + ut_ad(mach_read_from_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE + + undo_page) + == TRX_UNDO_INSERT); + + byte* page_hdr = undo_page + TRX_UNDO_PAGE_HDR; + mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, new_free); + mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, new_free); + mach_write_to_2(TRX_UNDO_SEG_HDR + TRX_UNDO_STATE + undo_page, + TRX_UNDO_ACTIVE); + + byte* log_hdr = undo_page + TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE; + + mach_write_to_8(log_hdr + TRX_UNDO_TRX_ID, trx_id); + mach_write_to_2(log_hdr + TRX_UNDO_LOG_START, new_free); + + mach_write_to_1(log_hdr + TRX_UNDO_XID_EXISTS, FALSE); + mach_write_to_1(log_hdr + TRX_UNDO_DICT_TRANS, FALSE); return(const_cast<byte*>(ptr)); } @@ -361,59 +474,46 @@ trx_undo_page_init( { trx_upagef_t* page_hdr; - page_hdr = undo_page + TRX_UNDO_PAGE_HDR; - - *reinterpret_cast<uint16*>(page_hdr + TRX_UNDO_PAGE_TYPE) = 0; - - mach_write_to_2(page_hdr + TRX_UNDO_PAGE_START, - TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE); - mach_write_to_2(page_hdr + TRX_UNDO_PAGE_FREE, - TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE); + mlog_write_ulint(undo_page + FIL_PAGE_TYPE, + FIL_PAGE_UNDO_LOG, MLOG_2BYTES, mtr); + compile_time_assert(TRX_UNDO_PAGE_TYPE == 0); + compile_time_assert(TRX_UNDO_PAGE_START == 2); + compile_time_assert(TRX_UNDO_PAGE_NODE == TRX_UNDO_PAGE_FREE + 2); - fil_page_set_type(undo_page, FIL_PAGE_UNDO_LOG); - - trx_undo_page_init_log(undo_page, mtr); + page_hdr = undo_page + TRX_UNDO_PAGE_HDR; + mlog_write_ulint(page_hdr, TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE, + MLOG_4BYTES, mtr); + mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_FREE, + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE, + MLOG_2BYTES, mtr); } -/***************************************************************//** -Creates a new undo log segment in file. -@return DB_SUCCESS if page creation OK possible error codes are: -DB_TOO_MANY_CONCURRENT_TRXS DB_OUT_OF_FILE_SPACE */ -static MY_ATTRIBUTE((warn_unused_result)) -dberr_t -trx_undo_seg_create( -/*================*/ - trx_rseg_t* rseg MY_ATTRIBUTE((unused)),/*!< in: rollback segment */ - trx_rsegf_t* rseg_hdr,/*!< in: rollback segment header, page - x-latched */ - ulint* id, /*!< out: slot index within rseg header */ - page_t** undo_page, - /*!< out: segment header page x-latched, NULL - if there was an error */ - mtr_t* mtr) /*!< in: mtr */ +/** Create an undo log segment. +@param[in,out] rseg_hdr rollback segment header (x-latched) +@param[out] id undo slot number +@param[out] err error code +@param[in,out] mtr mini-transaction +@return undo log block +@retval NULL on failure */ +static MY_ATTRIBUTE((nonnull, warn_unused_result)) +buf_block_t* +trx_undo_seg_create(trx_rsegf_t* rseg_hdr, ulint* id, dberr_t* err, mtr_t* mtr) { ulint slot_no; ulint space; buf_block_t* block; - trx_upagef_t* page_hdr; - trx_usegf_t* seg_hdr; ulint n_reserved; bool success; - dberr_t err = DB_SUCCESS; - ut_ad(mtr != NULL); - ut_ad(id != NULL); - ut_ad(rseg_hdr != NULL); - ut_ad(mutex_own(&(rseg->mutex))); - - slot_no = trx_rsegf_undo_find_free(rseg_hdr, mtr); + slot_no = trx_rsegf_undo_find_free(rseg_hdr); if (slot_no == ULINT_UNDEFINED) { ib::warn() << "Cannot find a free slot for an undo log. Do" " you have too many active transactions running" " concurrently?"; - return(DB_TOO_MANY_CONCURRENT_TRXS); + *err = DB_TOO_MANY_CONCURRENT_TRXS; + return NULL; } space = page_get_space_id(page_align(rseg_hdr)); @@ -421,8 +521,8 @@ trx_undo_seg_create( success = fsp_reserve_free_extents(&n_reserved, space, 2, FSP_UNDO, mtr); if (!success) { - - return(DB_OUT_OF_FILE_SPACE); + *err = DB_OUT_OF_FILE_SPACE; + return NULL; } /* Allocate a new file segment for the undo log */ @@ -433,38 +533,35 @@ trx_undo_seg_create( fil_space_release_free_extents(space, n_reserved); if (block == NULL) { - /* No space left */ - - return(DB_OUT_OF_FILE_SPACE); + *err = DB_OUT_OF_FILE_SPACE; + return NULL; } buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE); - *undo_page = buf_block_get_frame(block); + trx_undo_page_init(block->frame, mtr); - page_hdr = *undo_page + TRX_UNDO_PAGE_HDR; - seg_hdr = *undo_page + TRX_UNDO_SEG_HDR; - - trx_undo_page_init(*undo_page, mtr); - - mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_FREE, + mlog_write_ulint(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_FREE + block->frame, TRX_UNDO_SEG_HDR + TRX_UNDO_SEG_HDR_SIZE, MLOG_2BYTES, mtr); - mlog_write_ulint(seg_hdr + TRX_UNDO_LAST_LOG, 0, MLOG_2BYTES, mtr); + mlog_write_ulint(TRX_UNDO_SEG_HDR + TRX_UNDO_LAST_LOG + block->frame, + 0, MLOG_2BYTES, mtr); - flst_init(seg_hdr + TRX_UNDO_PAGE_LIST, mtr); + flst_init(TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST + block->frame, mtr); - flst_add_last(seg_hdr + TRX_UNDO_PAGE_LIST, - page_hdr + TRX_UNDO_PAGE_NODE, mtr); + flst_add_last(TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST + block->frame, + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE + block->frame, + mtr); - trx_rsegf_set_nth_undo(rseg_hdr, slot_no, - page_get_page_no(*undo_page), mtr); *id = slot_no; + trx_rsegf_set_nth_undo(rseg_hdr, slot_no, block->page.id.page_no(), + mtr); MONITOR_INC(MONITOR_NUM_UNDO_SLOT_USED); - return(err); + *err = DB_SUCCESS; + return block; } /**********************************************************************//** @@ -587,10 +684,7 @@ trx_undo_write_xid( Read X/Open XA Transaction Identification (XID) from undo log header */ static void -trx_undo_read_xid( -/*==============*/ - trx_ulogf_t* log_hdr,/*!< in: undo log header */ - XID* xid) /*!< out: X/Open XA Transaction Identification */ +trx_undo_read_xid(const trx_ulogf_t* log_hdr, XID* xid) { xid->formatID=static_cast<long>(mach_read_from_4( log_hdr + TRX_UNDO_XA_FORMAT)); @@ -686,9 +780,6 @@ trx_undo_add_page(trx_t* trx, trx_undo_t* undo, mtr_t* mtr) counterpart of the tree latch, which is the rseg mutex. */ mutex_enter(&rseg->mutex); - if (rseg->curr_size == rseg->max_size) { - goto func_exit; - } header_page = trx_undo_page_get( page_id_t(undo->space, undo->hdr_page_no), mtr); @@ -799,34 +890,6 @@ trx_undo_free_last_page(trx_undo_t* undo, mtr_t* mtr) undo->size--; } -/** Empties an undo log header page of undo records for that undo log. -Other undo logs may still have records on that page, if it is an update -undo log. -@param[in] space space -@param[in] hdr_page_no header page number -@param[in] hdr_offset header offset -@param[in,out] mtr mini-transaction */ -static -void -trx_undo_empty_header_page( - ulint space, - ulint hdr_page_no, - ulint hdr_offset, - mtr_t* mtr) -{ - page_t* header_page; - trx_ulogf_t* log_hdr; - ulint end; - - header_page = trx_undo_page_get(page_id_t(space, hdr_page_no), mtr); - - log_hdr = header_page + hdr_offset; - - end = trx_undo_page_get_end(header_page, hdr_page_no, hdr_offset); - - mlog_write_ulint(log_hdr + TRX_UNDO_LOG_START, end, MLOG_2BYTES, mtr); -} - /** Truncate the tail of an undo log during rollback. @param[in,out] undo undo log @param[in] limit all undo logs after this limit will be discarded @@ -938,9 +1001,16 @@ loop: page_no = page_get_page_no(undo_page); if (page_no == hdr_page_no) { - trx_undo_empty_header_page(rseg->space, - hdr_page_no, hdr_offset, - &mtr); + uint16_t end = mach_read_from_2(hdr_offset + TRX_UNDO_NEXT_LOG + + undo_page); + if (end == 0) { + end = mach_read_from_2(TRX_UNDO_PAGE_HDR + + TRX_UNDO_PAGE_FREE + + undo_page); + } + + mlog_write_ulint(undo_page + hdr_offset + TRX_UNDO_LOG_START, + end, MLOG_2BYTES, &mtr); } else { trx_undo_free_page(rseg, TRUE, rseg->space, hdr_page_no, page_no, &mtr); @@ -1005,66 +1075,58 @@ trx_undo_seg_free( /*========== UNDO LOG MEMORY COPY INITIALIZATION =====================*/ -/********************************************************************//** -Creates and initializes an undo log memory object according to the values -in the header in file, when the database is started. The memory object is -inserted in the appropriate list of rseg. -@return own: the undo log memory object */ -static -trx_undo_t* -trx_undo_mem_create_at_db_start( -/*============================*/ - trx_rseg_t* rseg, /*!< in: rollback segment memory object */ - ulint id, /*!< in: slot index within rseg */ - ulint page_no,/*!< in: undo log segment page number */ - mtr_t* mtr) /*!< in: mtr */ +/** Read an undo log when starting up the database. +@param[in,out] rseg rollback segment +@param[in] id rollback segment slot +@param[in] page_no undo log segment page number +@param[in,out] max_trx_id the largest observed transaction ID +@return size of the undo log in pages */ +ulint +trx_undo_mem_create_at_db_start(trx_rseg_t* rseg, ulint id, ulint page_no, + trx_id_t& max_trx_id) { - page_t* undo_page; - trx_usegf_t* seg_header; - trx_ulogf_t* undo_header; - trx_undo_t* undo; - ulint state; - trx_id_t trx_id; - ulint offset; + mtr_t mtr; XID xid; - ut_a(id < TRX_RSEG_N_SLOTS); + ut_ad(id < TRX_RSEG_N_SLOTS); - undo_page = trx_undo_page_get(page_id_t(rseg->space, page_no), mtr); + mtr.start(); + const page_t* undo_page = trx_undo_page_get( + page_id_t(rseg->space, page_no), &mtr); const ulint type = mach_read_from_2( TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE + undo_page); ut_ad(type == 0 || type == TRX_UNDO_INSERT || type == TRX_UNDO_UPDATE); - seg_header = undo_page + TRX_UNDO_SEG_HDR; - - state = mach_read_from_2(seg_header + TRX_UNDO_STATE); - - offset = mach_read_from_2(seg_header + TRX_UNDO_LAST_LOG); - - undo_header = undo_page + offset; - trx_id = mach_read_from_8(undo_header + TRX_UNDO_TRX_ID); + uint state = mach_read_from_2(TRX_UNDO_SEG_HDR + TRX_UNDO_STATE + + undo_page); + uint offset = mach_read_from_2(TRX_UNDO_SEG_HDR + TRX_UNDO_LAST_LOG + + undo_page); - const bool xid_exists = mtr_read_ulint( - undo_header + TRX_UNDO_XID_EXISTS, MLOG_1BYTE, mtr); + const trx_ulogf_t* undo_header = undo_page + offset; /* Read X/Open XA transaction identification if it exists, or set it to NULL. */ - xid.null(); - if (xid_exists) { + if (undo_header[TRX_UNDO_XID_EXISTS]) { trx_undo_read_xid(undo_header, &xid); + } else { + xid.null(); } - mutex_enter(&(rseg->mutex)); - - undo = trx_undo_mem_create(rseg, id, trx_id, &xid, page_no, offset); - mutex_exit(&(rseg->mutex)); + trx_id_t trx_id = mach_read_from_8(undo_header + TRX_UNDO_TRX_ID); + if (trx_id > max_trx_id) { + max_trx_id = trx_id; + } - undo->dict_operation = mtr_read_ulint( - undo_header + TRX_UNDO_DICT_TRANS, MLOG_1BYTE, mtr); + mutex_enter(&rseg->mutex); + trx_undo_t* undo = trx_undo_mem_create( + rseg, id, trx_id, &xid, page_no, offset); + mutex_exit(&rseg->mutex); + undo->dict_operation = undo_header[TRX_UNDO_DICT_TRANS]; undo->table_id = mach_read_from_8(undo_header + TRX_UNDO_TABLE_ID); - undo->size = flst_get_len(seg_header + TRX_UNDO_PAGE_LIST); + undo->size = flst_get_len(TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST + + undo_page); if (UNIV_UNLIKELY(state == TRX_UNDO_TO_FREE)) { /* This is an old-format insert_undo log segment that @@ -1072,14 +1134,24 @@ trx_undo_mem_create_at_db_start( ut_ad(type == TRX_UNDO_INSERT); state = TRX_UNDO_TO_PURGE; } else { + if (state == TRX_UNDO_TO_PURGE + || state == TRX_UNDO_CACHED) { + trx_id_t id = mach_read_from_8(TRX_UNDO_TRX_NO + + undo_header); + if (id > max_trx_id) { + max_trx_id = id; + } + } + fil_addr_t last_addr = flst_get_last( - seg_header + TRX_UNDO_PAGE_LIST, mtr); + TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST + undo_page, + &mtr); undo->last_page_no = last_addr.page; undo->top_page_no = last_addr.page; page_t* last_page = trx_undo_page_get( - page_id_t(rseg->space, undo->last_page_no), mtr); + page_id_t(rseg->space, undo->last_page_no), &mtr); const trx_undo_rec_t* rec = trx_undo_page_get_last_rec( last_page, page_no, offset); @@ -1102,63 +1174,8 @@ trx_undo_mem_create_at_db_start( MONITOR_INC(MONITOR_NUM_UNDO_SLOT_CACHED); } - return(undo); -} - -/********************************************************************//** -Initializes the undo log lists for a rollback segment memory copy. This -function is only called when the database is started or a new rollback -segment is created. -@return the combined size of undo log segments in pages */ -ulint -trx_undo_lists_init( -/*================*/ - trx_rseg_t* rseg) /*!< in: rollback segment memory object */ -{ - ulint size = 0; - trx_rsegf_t* rseg_header; - ulint i; - mtr_t mtr; - - mtr_start(&mtr); - - rseg_header = trx_rsegf_get_new(rseg->space, rseg->page_no, &mtr); - - for (i = 0; i < TRX_RSEG_N_SLOTS; i++) { - ulint page_no; - - page_no = trx_rsegf_get_nth_undo(rseg_header, i, &mtr); - - /* In forced recovery: try to avoid operations which look - at database pages; undo logs are rapidly changing data, and - the probability that they are in an inconsistent state is - high */ - - if (page_no != FIL_NULL - && srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) { - - trx_undo_t* undo; - - undo = trx_undo_mem_create_at_db_start( - rseg, i, page_no, &mtr); - - size += undo->size; - - mtr_commit(&mtr); - - mtr_start(&mtr); - - rseg_header = trx_rsegf_get( - rseg->space, rseg->page_no, &mtr); - - /* Found a used slot */ - MONITOR_INC(MONITOR_NUM_UNDO_SLOT_USED); - } - } - - mtr_commit(&mtr); - - return(size); + mtr.commit(); + return undo->size; } /********************************************************************//** @@ -1238,158 +1255,181 @@ trx_undo_mem_init_for_reuse( undo->empty = TRUE; } -/********************************************************************//** -Frees an undo log memory copy. */ -void -trx_undo_mem_free( -/*==============*/ - trx_undo_t* undo) /*!< in: the undo object to be freed */ -{ - ut_a(undo->id < TRX_RSEG_N_SLOTS); - - ut_free(undo); -} - -/**********************************************************************//** -Creates a new undo log. -@return DB_SUCCESS if successful in creating the new undo lob object, -possible error codes are: DB_TOO_MANY_CONCURRENT_TRXS -DB_OUT_OF_FILE_SPACE DB_OUT_OF_MEMORY */ +/** Create an undo log. +@param[in,out] trx transaction +@param[in,out] rseg rollback segment +@param[out] undo undo log object +@param[out] err error code +@param[in,out] mtr mini-transaction +@return undo log block +@retval NULL on failure */ static MY_ATTRIBUTE((nonnull, warn_unused_result)) -dberr_t -trx_undo_create( -/*============*/ - trx_t* trx, /*!< in: transaction */ - trx_rseg_t* rseg, /*!< in: rollback segment memory copy */ - trx_id_t trx_id, /*!< in: id of the trx for which the undo log - is created */ - const XID* xid, /*!< in: X/Open transaction identification*/ - trx_undo_t** undo, /*!< out: the new undo log object, undefined - * if did not succeed */ - mtr_t* mtr) /*!< in: mtr */ +buf_block_t* +trx_undo_create(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** undo, + dberr_t* err, mtr_t* mtr) { - trx_rsegf_t* rseg_header; - ulint page_no; - ulint offset; ulint id; - page_t* undo_page; - dberr_t err; ut_ad(mutex_own(&(rseg->mutex))); - if (rseg->curr_size == rseg->max_size) { + buf_block_t* block = trx_undo_seg_create( + trx_rsegf_get(rseg->space, rseg->page_no, mtr), &id, err, mtr); - return(DB_OUT_OF_FILE_SPACE); + if (!block) { + return block; } rseg->curr_size++; - rseg_header = trx_rsegf_get(rseg->space, rseg->page_no, mtr); - - err = trx_undo_seg_create(rseg, rseg_header, &id, &undo_page, mtr); + ulint offset = trx_undo_header_create(block->frame, trx->id, mtr); - if (err != DB_SUCCESS) { - /* Did not succeed */ + trx_undo_header_add_space_for_xid(block->frame, block->frame + offset, + mtr); - rseg->curr_size--; - - return(err); - } - - page_no = page_get_page_no(undo_page); - - offset = trx_undo_header_create(undo_page, trx_id, mtr); - - trx_undo_header_add_space_for_xid(undo_page, undo_page + offset, mtr); - - *undo = trx_undo_mem_create(rseg, id, trx_id, xid, page_no, offset); + *undo = trx_undo_mem_create(rseg, id, trx->id, trx->xid, + block->page.id.page_no(), offset); if (*undo == NULL) { + *err = DB_OUT_OF_MEMORY; + /* FIXME: this will not free the undo block to the file */ + return NULL; + } else if (rseg != trx->rsegs.m_redo.rseg) { + return block; + } - err = DB_OUT_OF_MEMORY; + switch (trx_get_dict_operation(trx)) { + case TRX_DICT_OP_NONE: + break; + case TRX_DICT_OP_INDEX: + /* Do not discard the table on recovery. */ + trx->table_id = 0; + /* fall through */ + case TRX_DICT_OP_TABLE: + (*undo)->table_id = trx->table_id; + (*undo)->dict_operation = TRUE; + mlog_write_ulint(block->frame + offset + TRX_UNDO_DICT_TRANS, + TRUE, MLOG_1BYTE, mtr); + mlog_write_ull(block->frame + offset + TRX_UNDO_TABLE_ID, + trx->table_id, mtr); } - return(err); + *err = DB_SUCCESS; + return block; } /*================ UNDO LOG ASSIGNMENT AND CLEANUP =====================*/ -/********************************************************************//** -Reuses a cached undo log. -@return the undo log memory object, NULL if none cached */ +/** Reuse a cached undo log block. +@param[in,out] trx transaction +@param[in,out] rseg rollback segment +@param[out] pundo the undo log memory object +@param[in,out] mtr mini-transaction +@return the undo log block +@retval NULL if none cached */ static -trx_undo_t* -trx_undo_reuse_cached( -/*==================*/ - trx_t* trx, /*!< in: transaction */ - trx_rseg_t* rseg, /*!< in: rollback segment memory object */ - trx_id_t trx_id, /*!< in: id of the trx for which the undo log - is used */ - const XID* xid, /*!< in: X/Open XA transaction identification */ - mtr_t* mtr) /*!< in: mtr */ +buf_block_t* +trx_undo_reuse_cached(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** pundo, + mtr_t* mtr) { - trx_undo_t* undo; - page_t* undo_page; - ulint offset; + ut_ad(mutex_own(&rseg->mutex)); - ut_ad(mutex_own(&(rseg->mutex))); - - undo = UT_LIST_GET_FIRST(rseg->undo_cached); - if (undo == NULL) { - return(NULL); + trx_undo_t* undo = UT_LIST_GET_FIRST(rseg->undo_cached); + if (!undo) { + return NULL; } - UT_LIST_REMOVE(rseg->undo_cached, undo); - MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED); ut_ad(undo->size == 1); - ut_a(undo->id < TRX_RSEG_N_SLOTS); + ut_ad(undo->id < TRX_RSEG_N_SLOTS); - undo_page = trx_undo_page_get( - page_id_t(undo->space, undo->hdr_page_no), mtr); + buf_block_t* block = buf_page_get(page_id_t(undo->space, + undo->hdr_page_no), + univ_page_size, RW_X_LATCH, mtr); + if (!block) { + return NULL; + } - offset = trx_undo_header_create(undo_page, trx_id, mtr); + buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE); - trx_undo_header_add_space_for_xid(undo_page, undo_page + offset, mtr); - trx_undo_mem_init_for_reuse(undo, trx_id, xid, offset); + UT_LIST_REMOVE(rseg->undo_cached, undo); + MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED); - return(undo); -} + *pundo = undo; -/**********************************************************************//** -Marks an undo log header as a header of a data dictionary operation -transaction. */ -static -void -trx_undo_mark_as_dict_operation( -/*============================*/ - trx_t* trx, /*!< in: dict op transaction */ - trx_undo_t* undo, /*!< in: assigned undo log */ - mtr_t* mtr) /*!< in: mtr */ -{ - page_t* hdr_page; + ulint offset = trx_undo_header_create(block->frame, trx->id, mtr); - hdr_page = trx_undo_page_get( - page_id_t(undo->space, undo->hdr_page_no), mtr); + trx_undo_header_add_space_for_xid(block->frame, block->frame + offset, + mtr); + + trx_undo_mem_init_for_reuse(undo, trx->id, trx->xid, offset); + + if (rseg != trx->rsegs.m_redo.rseg) { + return block; + } switch (trx_get_dict_operation(trx)) { case TRX_DICT_OP_NONE: - ut_error; + return block; case TRX_DICT_OP_INDEX: /* Do not discard the table on recovery. */ - undo->table_id = 0; - break; + trx->table_id = 0; + /* fall through */ case TRX_DICT_OP_TABLE: undo->table_id = trx->table_id; - break; + undo->dict_operation = TRUE; + mlog_write_ulint(block->frame + offset + TRX_UNDO_DICT_TRANS, + TRUE, MLOG_1BYTE, mtr); + mlog_write_ull(block->frame + offset + TRX_UNDO_TABLE_ID, + trx->table_id, mtr); } - mlog_write_ulint(hdr_page + undo->hdr_offset - + TRX_UNDO_DICT_TRANS, - TRUE, MLOG_1BYTE, mtr); + return block; +} - mlog_write_ull(hdr_page + undo->hdr_offset + TRX_UNDO_TABLE_ID, - undo->table_id, mtr); +/** Assign an undo log for a persistent transaction. +A new undo log is created or a cached undo log reused. +@param[in,out] trx transaction +@param[out] err error code +@param[in,out] mtr mini-transaction +@return the undo log block +@retval NULL on error */ +buf_block_t* +trx_undo_assign(trx_t* trx, dberr_t* err, mtr_t* mtr) +{ + ut_ad(mutex_own(&trx->undo_mutex)); + ut_ad(mtr->get_log_mode() == MTR_LOG_ALL); + + trx_undo_t* undo = trx->rsegs.m_redo.undo; + + if (undo) { + return buf_page_get_gen( + page_id_t(undo->space, undo->last_page_no), + univ_page_size, RW_X_LATCH, + buf_pool_is_obsolete(undo->withdraw_clock) + ? NULL : undo->guess_block, + BUF_GET, __FILE__, __LINE__, mtr, err); + } + + trx_rseg_t* rseg = trx->rsegs.m_redo.rseg; + + mutex_enter(&rseg->mutex); + buf_block_t* block = trx_undo_reuse_cached( + trx, rseg, &trx->rsegs.m_redo.undo, mtr); + + if (!block) { + block = trx_undo_create(trx, rseg, &trx->rsegs.m_redo.undo, + err, mtr); + ut_ad(!block == (*err != DB_SUCCESS)); + if (!block) { + goto func_exit; + } + } else { + *err = DB_SUCCESS; + } + + UT_LIST_ADD_FIRST(rseg->undo_list, trx->rsegs.m_redo.undo); - undo->dict_operation = TRUE; +func_exit: + mutex_exit(&rseg->mutex); + return block; } /** Assign an undo log for a transaction. @@ -1397,17 +1437,15 @@ A new undo log is created or a cached undo log reused. @param[in,out] trx transaction @param[in] rseg rollback segment @param[out] undo the undo log -@retval DB_SUCCESS on success -@retval DB_TOO_MANY_CONCURRENT_TRXS -@retval DB_OUT_OF_FILE_SPACE -@retval DB_READ_ONLY -@retval DB_OUT_OF_MEMORY */ -dberr_t -trx_undo_assign_undo(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** undo) +@param[out] err error code +@param[in,out] mtr mini-transaction +@return the undo log block +@retval NULL on error */ +buf_block_t* +trx_undo_assign_low(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** undo, + dberr_t* err, mtr_t* mtr) { const bool is_temp = rseg == trx->rsegs.m_noredo.rseg; - mtr_t mtr; - dberr_t err = DB_SUCCESS; ut_ad(mutex_own(&trx->undo_mutex)); ut_ad(rseg == trx->rsegs.m_redo.rseg @@ -1415,41 +1453,42 @@ trx_undo_assign_undo(trx_t* trx, trx_rseg_t* rseg, trx_undo_t** undo) ut_ad(undo == (is_temp ? &trx->rsegs.m_noredo.undo : &trx->rsegs.m_redo.undo)); - - mtr.start(trx); - - if (is_temp) { - mtr.set_log_mode(MTR_LOG_NO_REDO); + ut_ad(mtr->get_log_mode() + == (is_temp ? MTR_LOG_NO_REDO : MTR_LOG_ALL)); + + if (*undo) { + return buf_page_get_gen( + page_id_t((*undo)->space, (*undo)->last_page_no), + univ_page_size, RW_X_LATCH, + buf_pool_is_obsolete((*undo)->withdraw_clock) + ? NULL : (*undo)->guess_block, + BUF_GET, __FILE__, __LINE__, mtr, err); } - mutex_enter(&rseg->mutex); - DBUG_EXECUTE_IF( "ib_create_table_fail_too_many_trx", - err = DB_TOO_MANY_CONCURRENT_TRXS; - goto func_exit; + *err = DB_TOO_MANY_CONCURRENT_TRXS; return NULL; ); - *undo = trx_undo_reuse_cached(trx, rseg, trx->id, trx->xid, &mtr); - if (*undo == NULL) { - err = trx_undo_create(trx, rseg, trx->id, trx->xid, - undo, &mtr); - if (err != DB_SUCCESS) { + mutex_enter(&rseg->mutex); + + buf_block_t* block = trx_undo_reuse_cached(trx, rseg, undo, mtr); + + if (!block) { + block = trx_undo_create(trx, rseg, undo, err, mtr); + ut_ad(!block == (*err != DB_SUCCESS)); + if (!block) { goto func_exit; } + } else { + *err = DB_SUCCESS; } UT_LIST_ADD_FIRST(rseg->undo_list, *undo); - if (!is_temp && trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { - trx_undo_mark_as_dict_operation(trx, *undo, &mtr); - } - func_exit: mutex_exit(&rseg->mutex); - mtr.commit(); - - return(err); + return block; } /******************************************************************//** @@ -1575,21 +1614,16 @@ trx_undo_commit_cleanup(trx_undo_t* undo, bool is_temp) ut_ad(rseg->curr_size > undo->size); rseg->curr_size -= undo->size; - trx_undo_mem_free(undo); + ut_free(undo); } 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: @@ -1602,17 +1636,18 @@ trx_undo_free_prepared( /* fall through */ case TRX_UNDO_ACTIVE: /* lock_trx_release_locks() assigns - trx->is_recovered=false */ + trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */ ut_a(!srv_was_started || srv_read_only_mode - || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO + || srv_fast_shutdown); break; default: ut_error; } UT_LIST_REMOVE(trx->rsegs.m_redo.rseg->undo_list, undo); - trx_undo_mem_free(undo); + ut_free(undo); undo = NULL; } @@ -1628,17 +1663,18 @@ trx_undo_free_prepared( /* fall through */ case TRX_UNDO_ACTIVE: /* lock_trx_release_locks() assigns - trx->is_recovered=false */ + trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */ ut_a(!srv_was_started || srv_read_only_mode - || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO + || srv_fast_shutdown); break; default: ut_error; } UT_LIST_REMOVE(trx->rsegs.m_redo.rseg->old_insert_list, undo); - trx_undo_mem_free(undo); + ut_free(undo); undo = NULL; } @@ -1646,7 +1682,7 @@ trx_undo_free_prepared( ut_a(undo->state == TRX_UNDO_PREPARED); UT_LIST_REMOVE(trx->rsegs.m_noredo.rseg->undo_list, undo); - trx_undo_mem_free(undo); + ut_free(undo); undo = NULL; } } @@ -1684,6 +1720,7 @@ trx_undo_truncate_tablespace( mtr_start(&mtr); mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO); mtr_x_lock(fil_space_get_latch(space_id, NULL), &mtr); + buf_block_t* sys_header = trx_sysf_get(&mtr); for (ulint i = 0; i < undo_trunc->rsegs_size(); ++i) { trx_rsegf_t* rseg_header; @@ -1691,7 +1728,7 @@ trx_undo_truncate_tablespace( trx_rseg_t* rseg = undo_trunc->get_ith_rseg(i); rseg->page_no = trx_rseg_header_create( - space_id, ULINT_MAX, rseg->id, &mtr); + space_id, rseg->id, sys_header, &mtr); rseg_header = trx_rsegf_get_new(space_id, rseg->page_no, &mtr); @@ -1708,15 +1745,12 @@ trx_undo_truncate_tablespace( next_undo = UT_LIST_GET_NEXT(undo_list, undo); UT_LIST_REMOVE(rseg->undo_cached, undo); MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED); - trx_undo_mem_free(undo); + ut_free(undo); } UT_LIST_INIT(rseg->undo_list, &trx_undo_t::undo_list); UT_LIST_INIT(rseg->undo_cached, &trx_undo_t::undo_list); - rseg->max_size = mtr_read_ulint( - rseg_header + TRX_RSEG_MAX_SIZE, MLOG_4BYTES, &mtr); - /* Initialize the undo log lists according to the rseg header */ rseg->curr_size = mtr_read_ulint( rseg_header + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, &mtr) @@ -1727,7 +1761,7 @@ trx_undo_truncate_tablespace( rseg->trx_ref_count = 0; rseg->last_page_no = FIL_NULL; rseg->last_offset = 0; - rseg->last_trx_no = 0; + rseg->last_commit = 0; rseg->needs_purge = false; } mtr_commit(&mtr); |