diff options
Diffstat (limited to 'storage/innobase/fsp/fsp0fsp.cc')
-rw-r--r-- | storage/innobase/fsp/fsp0fsp.cc | 748 |
1 files changed, 738 insertions, 10 deletions
diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index 5f34fe93..1793ff44 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -33,7 +33,6 @@ Created 11/29/1995 Heikki Tuuri #include "page0page.h" #include "srv0srv.h" #include "srv0start.h" -#include "ibuf0ibuf.h" #include "btr0btr.h" #include "btr0sea.h" #include "dict0boot.h" @@ -508,7 +507,7 @@ dberr_t fsp_header_init(fil_space_t *space, uint32_t size, mtr_t *mtr) const page_id_t page_id(space->id, 0); const ulint zip_size = space->zip_size(); - buf_block_t *free_block = buf_LRU_get_free_block(false); + buf_block_t *free_block = buf_LRU_get_free_block(have_no_mutex); mtr->x_lock_space(space); @@ -842,7 +841,7 @@ fsp_fill_free_list( if (i) { - buf_block_t *f= buf_LRU_get_free_block(false); + buf_block_t *f= buf_LRU_get_free_block(have_no_mutex); buf_block_t *block= buf_page_create(space, i, zip_size, mtr, f); if (UNIV_UNLIKELY(block != f)) buf_pool.free_block(f); @@ -853,11 +852,18 @@ fsp_fill_free_list( if (space->purpose != FIL_TYPE_TEMPORARY) { - buf_block_t *f= buf_LRU_get_free_block(false); + buf_block_t *f= buf_LRU_get_free_block(have_no_mutex); buf_block_t *block= - buf_page_create(space, i + FSP_IBUF_BITMAP_OFFSET, zip_size, mtr, f); + buf_page_create(space, i + 1, zip_size, mtr, f); if (UNIV_UNLIKELY(block != f)) buf_pool.free_block(f); + /* The zero-initialization will reset the change buffer bitmap bits + to safe values for possible import to an earlier version that + supports change buffering: + + IBUF_BITMAP_FREE = 0 (no space left for buffering inserts) + IBUF_BITMAP_BUFFERED = 0 (no changes have been buffered) + IBUF_BITMAP_IBUF = 0 (not part of the change buffer) */ fsp_init_file_page(space, block, mtr); mtr->write<2>(*block, FIL_PAGE_TYPE + block->page.frame, FIL_PAGE_IBUF_BITMAP); @@ -882,9 +888,9 @@ fsp_fill_free_list( if (UNIV_UNLIKELY(init_xdes)) { /* The first page in the extent is a descriptor page and the - second is an ibuf bitmap page: mark them used */ + second was reserved for change buffer bitmap: mark them used */ xdes_set_free<false>(*xdes, descr, 0, mtr); - xdes_set_free<false>(*xdes, descr, FSP_IBUF_BITMAP_OFFSET, mtr); + xdes_set_free<false>(*xdes, descr, 1, mtr); xdes_set_state(*xdes, descr, XDES_FREE_FRAG, mtr); if (dberr_t err= flst_add_last(header, FSP_HEADER_OFFSET + FSP_FREE_FRAG, xdes, xoffset, space->free_limit, mtr)) @@ -1046,7 +1052,7 @@ fsp_alloc_from_free_frag(buf_block_t *header, buf_block_t *xdes, xdes_t *descr, static buf_block_t* fsp_page_create(fil_space_t *space, uint32_t offset, mtr_t *mtr) { - buf_block_t *free_block= buf_LRU_get_free_block(false), + buf_block_t *free_block= buf_LRU_get_free_block(have_no_mutex), *block= buf_page_create(space, offset, space->zip_size(), mtr, free_block); if (UNIV_UNLIKELY(block != free_block)) buf_pool.free_block(free_block); @@ -1679,6 +1685,7 @@ fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr, dberr_t *err, ut_ad(byte_offset >= FIL_PAGE_DATA); ut_ad(byte_offset + FSEG_HEADER_SIZE <= srv_page_size - FIL_PAGE_DATA_END); + buf_block_t* iblock= 0; mtr->x_lock_space(space); ut_d(space->modify_check(*mtr)); @@ -1691,8 +1698,6 @@ fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr, dberr_t *err, goto funct_exit; } - buf_block_t* iblock; - inode_alloc: inode = fsp_alloc_seg_inode(space, header, &iblock, mtr, err); @@ -3092,3 +3097,726 @@ std::ostream &fseg_header::to_stream(std::ostream &out) const return out; } #endif /* UNIV_DEBUG */ + +/** Get the latched extent descriptor page or +acquire the extent descriptor page. +@param page_id page identifier to be acquired +@param mtr mini-transaction +@param err error code +@return block descriptor */ +static +buf_block_t *fsp_get_latched_xdes_page( + page_id_t page_id, mtr_t *mtr, dberr_t *err) +{ + buf_block_t *block= nullptr; + block= mtr->get_already_latched( + page_id, MTR_MEMO_PAGE_SX_FIX); + if (block) + return block; + return buf_page_get_gen( + page_id, 0, RW_SX_LATCH, nullptr, + BUF_GET_POSSIBLY_FREED, mtr, err); +} + +/** Used during system tablespace truncation. Stores +the "to be modified" extent descriptor page and its +old page state */ +class fsp_xdes_old_page +{ + std::vector<buf_block_t*> m_old_xdes_pages; + const uint32_t m_space; +public: + fsp_xdes_old_page(uint32_t space):m_space(space) {} + ulint n_pages() + { + uint32_t count=0; + for (uint32_t i= 0; i < m_old_xdes_pages.size(); i++) + if (m_old_xdes_pages[i]) count++; + return count; + } + + __attribute__((warn_unused_result)) + dberr_t insert(uint32_t page_no, mtr_t *mtr) + { + uint32_t m_index= page_no >> srv_page_size_shift; + if (m_old_xdes_pages.size() > m_index && + m_old_xdes_pages[m_index] != nullptr) + return DB_SUCCESS; + + DBUG_EXECUTE_IF("shrink_buffer_pool_full", + return DB_OUT_OF_MEMORY;); + dberr_t err= DB_SUCCESS; + buf_block_t *block= fsp_get_latched_xdes_page( + page_id_t(m_space, page_no), mtr, &err); + if (block) + { + buf_block_t *old= buf_LRU_get_free_block(have_no_mutex_soft); + if (!old) return DB_OUT_OF_MEMORY; + + memcpy_aligned<UNIV_PAGE_SIZE_MIN>( + old->page.frame, block->page.frame, srv_page_size); + + if (m_index >= m_old_xdes_pages.size()) + m_old_xdes_pages.resize(m_index + 1); + m_old_xdes_pages[m_index] = old; + } + return err; + } + + buf_block_t *search(uint32_t page_no) + { + uint32_t m_index= page_no >> srv_page_size_shift; + if (m_index > m_old_xdes_pages.size()) + return nullptr; + return m_old_xdes_pages[m_index]; + } + + void restore(mtr_t *mtr) + { + for (uint32_t i= 0; i < m_old_xdes_pages.size(); i++) + { + if (m_old_xdes_pages[i] == nullptr) continue; + buf_block_t *block= mtr->get_already_latched( + page_id_t{m_space, i << srv_page_size_shift}, + MTR_MEMO_PAGE_SX_FIX); + ut_ad(block); + memcpy_aligned<UNIV_PAGE_SIZE_MIN>( + block->page.frame, m_old_xdes_pages[i]->page.frame, srv_page_size); + } + } + + ~fsp_xdes_old_page() + { + for (uint32_t i= 0; i < m_old_xdes_pages.size(); i++) + if (m_old_xdes_pages[i]) + buf_block_free(m_old_xdes_pages[i]); + } +}; + +/** Update the current descriptor entry with last valid +descriptor entry with skipped descriptor pages +@param header File segment header +@param hdr_offset FSP_FREE or FSP_FREE_FRAG +@param cur_addr current descriptor +@param last_valid_addr last valid descriptor +@param skip_len number of truncated extent descriptor entry +@param mtr mini-transaction +@return error code or DB_SUCCESS */ +__attribute__((warn_unused_result)) +static +dberr_t fsp_lst_update_skip( + buf_block_t *header, uint16_t hdr_offset, + fil_addr_t cur_addr, fil_addr_t last_valid_addr, + uint32_t skip_len, mtr_t *mtr) +{ + dberr_t err= DB_SUCCESS; + uint32_t space_id= header->page.id().space(); + buf_block_t *cur= fsp_get_latched_xdes_page( + page_id_t(space_id, cur_addr.page), mtr, &err); + + if (!cur) return err; + if (last_valid_addr.page == FIL_NULL) + { + /* First node, so update the FIRST pointer of base + with current extent descriptor and update + the PREV pointer of last valid descriptor with + FIL_NULL */ + flst_write_addr( + *header, + header->page.frame + hdr_offset + FLST_FIRST, + cur_addr.page, cur_addr.boffset, mtr); + + flst_write_addr( + *cur, + cur->page.frame + cur_addr.boffset + FLST_PREV, + last_valid_addr.page, last_valid_addr.boffset, mtr); + } + else + { + buf_block_t *prev= nullptr; + if (cur->page.id().page_no() == last_valid_addr.page) + prev= cur; + else + { + prev= fsp_get_latched_xdes_page( + page_id_t(space_id, last_valid_addr.page), + mtr, &err); + if (!prev) return err; + } + + /* Update the NEXT pointer of last valid extent + descriptor entry with current extent descriptor */ + flst_write_addr( + *prev, + prev->page.frame + last_valid_addr.boffset + FLST_NEXT, + cur_addr.page, cur_addr.boffset, mtr); + + /* Update the PREV pointer of current extent + descriptor entry with last valid extent descriptor */ + flst_write_addr( + *cur, + cur->page.frame + cur_addr.boffset + FLST_PREV, + last_valid_addr.page, last_valid_addr.boffset, mtr); + } + + byte *len_bytes= &header->page.frame[hdr_offset + FLST_LEN]; + uint32_t len= mach_read_from_4(len_bytes); + ut_ad(len > skip_len); + mtr->write<4>(*header, len_bytes, len - skip_len); + return DB_SUCCESS; +} + +/** Write the FLST_NEXT pointer of the last valid node with FIL_NULL +@param header File segment header +@param hdr_offset FSP_HEADER_OFFSET + FSP_FREE or FSP_FREE_FRAG +@param cur_addr current descriptor +@param skip_len number of truncated extent descriptor entry +@param orig_len original length of the list +@param mtr mini-transaction +@return error code or DB_SUCCESS */ +__attribute__((warn_unused_result)) +dberr_t +fsp_lst_write_end( + buf_block_t *header, uint16_t hdr_offset, + fil_addr_t cur_addr, uint32_t skip_len, uint32_t orig_len, + mtr_t *mtr) +{ + dberr_t err= DB_SUCCESS; + byte *len_bytes= &header->page.frame[hdr_offset + FLST_LEN]; + uint32_t len= mach_read_from_4(len_bytes); + if (skip_len == 0) + { +func_exit: + if (hdr_offset == FSP_FREE_FRAG + FSP_HEADER_OFFSET) + { + byte *frag_used_byte= &header->page.frame[ + FSP_HEADER_OFFSET + FSP_FRAG_N_USED]; + uint32_t n_used_frag= mach_read_from_4(frag_used_byte); + /* Update the FSP_FRAG_N_USED value after removing + the truncated pages from FSP_FREE_FRAG list */ + if (len != orig_len) + mtr->write<4>(*header, frag_used_byte, + n_used_frag - ((orig_len - len) * 2)); + } + return DB_SUCCESS; + } + + if (cur_addr.page == FIL_NULL) + { + /* There is no list, so reset base node */ + mtr->memset( + header, + FLST_FIRST + FIL_ADDR_PAGE + hdr_offset, 4, 0xff); + mtr->memset( + header, + FLST_LAST + FIL_ADDR_PAGE + hdr_offset, 4, 0xff); + } + else + { + /* Update the FLST_LAST pointer in base node with current + valid extent descriptor and mark the FIL_NULL as next in + current extent descriptr */ + flst_write_addr( + *header, + header->page.frame + hdr_offset + FLST_LAST, + cur_addr.page, cur_addr.boffset, mtr); + + buf_block_t *cur_block= fsp_get_latched_xdes_page( + page_id_t(header->page.id().space(), cur_addr.page), + mtr, &err); + + if (!cur_block) return err; + + flst_write_addr( + *cur_block, + cur_block->page.frame + cur_addr.boffset + FLST_NEXT, + FIL_NULL, 0, mtr); + } + + ut_ad(len >= skip_len); + len-= skip_len; + mtr->write<4>(*header, len_bytes, len); + goto func_exit; +} + +/** Remove the truncated extents from the FSP_FREE list +@param header tablespace header +@param hdr_offset FSP_FREE or FSP_FREE_FRAG +@param threshold Remove the pages from the list which is + greater than threshold +@param mtr mini-transaction to remove the extents +@return DB_SUCCESS on success or error code */ +__attribute__((warn_unused_result)) +static +dberr_t fsp_shrink_list(buf_block_t *header, uint16_t hdr_offset, + uint32_t threshold, mtr_t *mtr) +{ + ut_ad(mach_read_from_4(header->page.frame + FIL_PAGE_OFFSET) == 0); + const uint32_t len= flst_get_len(hdr_offset + header->page.frame); + if (len == 0) + return DB_SUCCESS; + + buf_block_t *descr_block= nullptr; + dberr_t err= DB_SUCCESS; + uint32_t skip_len= 0; + fil_addr_t last_valid_addr {FIL_NULL, 0}, next_addr{FIL_NULL, 0}; + fil_addr_t addr= flst_get_first(header->page.frame + hdr_offset); + + for (uint32_t i= len; i > 0; i--) + { + ut_d(fil_space_t *space= header->page.id().space() == 0 + ? fil_system.sys_space + : fil_system.temp_space); + ut_ad(addr.page < space->size); + ut_ad(!(addr.page & (srv_page_size - 1))); + if (!descr_block || descr_block->page.id().page_no() != addr.page) + { + descr_block= fsp_get_latched_xdes_page( + page_id_t(header->page.id().space(), addr.page), mtr, &err); + if (!descr_block) + return err; + } + + if (addr.page < threshold) + { + /* Update only if only non-truncated page */ + if (skip_len) + { + err= fsp_lst_update_skip( + header, hdr_offset, addr, last_valid_addr, skip_len, mtr); + if (err) return err; + skip_len= 0; + } + + if (threshold <= xdes_get_offset( + descr_block->page.frame + addr.boffset - XDES_FLST_NODE)) + skip_len++; + else last_valid_addr= addr; + } + else skip_len++; + + next_addr= flst_get_next_addr( + descr_block->page.frame + addr.boffset); + if (next_addr.page != addr.page && addr.page >= threshold) + { + mtr->release_last_page(); + descr_block= nullptr; + } + + if (next_addr.page == FIL_NULL) + { + err= fsp_lst_write_end(header, hdr_offset, last_valid_addr, + skip_len, len, mtr); + break; + } + addr= next_addr; + } + ut_d(if (err == DB_SUCCESS) flst_validate(header, hdr_offset, mtr);); + return err; +} + +/** Reset the XDES_BITMAP for the truncated extents +@param space tablespace to be truncated +@param threshold truncated size +@param mtr mini-transaction to reset XDES_BITMAP +@return DB_SUCCESS or error code on failure */ +__attribute__((warn_unused_result)) +static +dberr_t fsp_xdes_reset(uint32_t space_id, uint32_t threshold, mtr_t *mtr) +{ + if (!(threshold & (srv_page_size - 1))) + return DB_SUCCESS; + + uint32_t cur_descr_page= xdes_calc_descriptor_page(0, threshold); + ulint descr_offset= XDES_ARR_OFFSET + XDES_SIZE + * xdes_calc_descriptor_index(0, threshold); + ulint last_descr_offset= XDES_ARR_OFFSET + XDES_SIZE + * xdes_calc_descriptor_index( + 0, (cur_descr_page + srv_page_size - 1)); + last_descr_offset+= XDES_SIZE; + dberr_t err= DB_SUCCESS; + buf_block_t *block= fsp_get_latched_xdes_page( + page_id_t(space_id, cur_descr_page), mtr, &err); + if (!block) + return err; + mtr->memset( + block, descr_offset, (last_descr_offset - descr_offset), 0); + return err; +} + +/** This function does 2 things by traversing all the used +extents in the system tablespace +1) Find the last used extent +2) Store the old page frame of the "to be modified" extent +descriptor pages. +@param space system tablespace +@param last_used_extent value is 0 in case of finding the last used + extent; else it could be last used extent +@param old_xdes_entry nullptr or object to store the + old page content of "to be modified" + extent descriptor pages +@return DB_SUCCESS or error code */ +__attribute__((warn_unused_result)) +dberr_t fsp_traverse_extents( + fil_space_t *space, uint32_t *last_used_extent, mtr_t *mtr, + fsp_xdes_old_page *old_xdes_entry= nullptr) +{ + dberr_t err= DB_SUCCESS; + bool find_last_used_extent= (old_xdes_entry == nullptr); + uint32_t threshold= *last_used_extent; + uint32_t last_descr_page_no= xdes_calc_descriptor_page( + 0, space->free_limit - 1); + + if (find_last_used_extent) + *last_used_extent= space->free_limit; + else + { + err= old_xdes_entry->insert(0, mtr); + if (err) return err; + if (threshold & (srv_page_size - 1)) + err= old_xdes_entry->insert( + xdes_calc_descriptor_page(0, threshold), mtr); + } + + buf_block_t *block= nullptr; + std::vector<uint32_t> modified_xdes; + + for (uint32_t cur_extent= + ((space->free_limit - 1)/ FSP_EXTENT_SIZE) * FSP_EXTENT_SIZE; + cur_extent >= threshold;) + { + if (!block) + { + block= fsp_get_latched_xdes_page( + page_id_t(space->id, last_descr_page_no), + mtr, &err); + if (!block) return err; + } + + xdes_t *descr= XDES_ARR_OFFSET + XDES_SIZE + * xdes_calc_descriptor_index(0, cur_extent) + + block->page.frame; + + if (find_last_used_extent) + { + ulint state= xdes_get_state(descr); + if (state == XDES_FREE) + *last_used_extent= cur_extent; + else if (state == XDES_FREE_FRAG && + !(cur_extent & (srv_page_size - 1)) && + xdes_get_n_used(descr) == 2) + /* Extent Descriptor Page */ + *last_used_extent= cur_extent; + else return DB_SUCCESS; + } + else + { + fil_addr_t prev_addr= flst_get_prev_addr( + descr + XDES_FLST_NODE); + ut_ad(prev_addr.page < space->size || + prev_addr.page == FIL_NULL); + ut_ad(prev_addr.page == FIL_NULL || + !(prev_addr.page & (srv_page_size - 1))); + + fil_addr_t next_addr= flst_get_next_addr( + descr + XDES_FLST_NODE); + ut_ad(next_addr.page < space->size || + next_addr.page == FIL_NULL); + ut_ad(next_addr.page == FIL_NULL || + !(next_addr.page & (srv_page_size - 1))); + + if (prev_addr.page < threshold) + modified_xdes.push_back(prev_addr.page); + + if (next_addr.page < threshold) + modified_xdes.push_back(next_addr.page); + } + + cur_extent-= FSP_EXTENT_SIZE; + uint32_t cur_descr_page= xdes_calc_descriptor_page(0, cur_extent); + if (last_descr_page_no != cur_descr_page) + { + if (last_descr_page_no >= threshold) + mtr->release_last_page(); + last_descr_page_no= cur_descr_page; + block= nullptr; + } + } + + if (!find_last_used_extent) + { + for (auto it : modified_xdes) + { + err= old_xdes_entry->insert(it, mtr); + if (err) return err; + } + modified_xdes.clear(); + } + return err; +} + +#ifdef UNIV_DEBUG +/** Validate the system tablespace list */ +__attribute__((warn_unused_result)) +dberr_t fsp_tablespace_validate(fil_space_t *space) +{ + /* Validate all FSP list in system tablespace */ + mtr_t local_mtr; + dberr_t err= DB_SUCCESS; + local_mtr.start(); + if (buf_block_t *header= fsp_get_header( + space, &local_mtr, &err)) + { + flst_validate(header, FSP_FREE + FSP_HEADER_OFFSET, &local_mtr); + flst_validate(header, FSP_FREE_FRAG + FSP_HEADER_OFFSET, + &local_mtr); + flst_validate(header, FSP_HEADER_OFFSET + FSP_FULL_FRAG, + &local_mtr); + flst_validate(header, FSP_HEADER_OFFSET + FSP_SEG_INODES_FULL, + &local_mtr); + flst_validate(header, FSP_HEADER_OFFSET + FSP_SEG_INODES_FREE, + &local_mtr); + } + local_mtr.commit(); + return err; +} +#endif /* UNIV_DEBUG */ + +void fsp_system_tablespace_truncate() +{ + uint32_t last_used_extent= 0; + fil_space_t *space= fil_system.sys_space; + mtr_t mtr; + mtr.start(); + mtr.x_lock_space(space); + dberr_t err= fsp_traverse_extents(space, &last_used_extent, &mtr); + if (err != DB_SUCCESS) + { +func_exit: + sql_print_warning("InnoDB: Cannot shrink the system tablespace " + "due to %s", ut_strerr(err)); + mtr.commit(); + return; + } + uint32_t fixed_size= srv_sys_space.get_min_size(), + header_size= space->size_in_header; + mtr.commit(); + + if (last_used_extent >= header_size || fixed_size >= header_size) + /* Tablespace is being used within fixed size */ + return; + + /* Set fixed size as threshold to truncate */ + if (fixed_size > last_used_extent) + last_used_extent= fixed_size; + + bool old_dblwr_buf= buf_dblwr.in_use(); + /* Flush all pages in buffer pool, so that it doesn't have to + use doublewrite buffer and disable dblwr and there should + be enough space in redo log */ + log_make_checkpoint(); + fil_system.set_use_doublewrite(false); + + buf_block_t *header= nullptr; + ut_ad(!fsp_tablespace_validate(space)); + + mtr.start(); + mtr.x_lock_space(space); + + { + /* Take the rough estimation of modified extent + descriptor page and store their old state */ + fsp_xdes_old_page old_xdes_list(space->id); + err= fsp_traverse_extents(space, &last_used_extent, &mtr, &old_xdes_list); + + if (err == DB_OUT_OF_MEMORY) + { + mtr.commit(); + sql_print_warning("InnoDB: Cannot shrink the system " + "tablespace from " UINT32PF" to " + UINT32PF " pages due to insufficient " + "innodb_buffer_pool_size", space->size, + last_used_extent); + return; + } + + sql_print_information("InnoDB: Truncating system tablespace from " + UINT32PF " to " UINT32PF " pages", space->size, + last_used_extent); + + header= fsp_get_latched_xdes_page( + page_id_t(space->id, 0), &mtr, &err); + if (!header) + goto func_exit; + + mtr.write<4, mtr_t::FORCED>( + *header, FSP_HEADER_OFFSET + FSP_SIZE + header->page.frame, + last_used_extent); + + if (space->free_limit > last_used_extent) + mtr.write<4,mtr_t::MAYBE_NOP>(*header, FSP_HEADER_OFFSET + + FSP_FREE_LIMIT + header->page.frame, + last_used_extent); + err= fsp_shrink_list( + header, FSP_HEADER_OFFSET + FSP_FREE, last_used_extent, &mtr); + if (err != DB_SUCCESS) + goto func_exit; + + err= fsp_shrink_list( + header, FSP_HEADER_OFFSET + FSP_FREE_FRAG, last_used_extent, &mtr); + if (err != DB_SUCCESS) + goto func_exit; + + err= fsp_xdes_reset(space->id, last_used_extent, &mtr); + if (err != DB_SUCCESS) + goto func_exit; + + mtr.trim_pages(page_id_t(0, last_used_extent)); + size_t shrink_redo_size= mtr.get_log_size(); + + DBUG_EXECUTE_IF("mtr_log_max_size", goto mtr_max;); + if (shrink_redo_size > + (2 << 20) - 8 /* encryption nonce */ - 5 /* EOF, checksum */) + { +#ifndef DBUG_OFF +mtr_max: +#endif + /* Replace the modified copy from buffer pool with + original copy of the pages. */ + old_xdes_list.restore(&mtr); + mtr.discard_modifications(); + mtr.commit(); + ut_ad(!fsp_tablespace_validate(space)); + sql_print_error( + "InnoDB: Cannot shrink the system tablespace " + "because the mini-transaction log size (%zu bytes) " + "exceeds 2 MiB", shrink_redo_size + 8 + 5); + return; + } + } + + if (space->free_limit > last_used_extent) + space->free_limit= last_used_extent; + space->free_len= flst_get_len(FSP_HEADER_OFFSET + FSP_FREE + + header->page.frame); + + mtr.commit_shrink(*space, last_used_extent); + sql_print_information("InnoDB: System tablespace truncated successfully"); + fil_system.set_use_doublewrite(old_dblwr_buf); +} + +inline void fil_space_t::clear_freed_ranges(uint32_t threshold) +{ + ut_ad(id == SRV_TMP_SPACE_ID); + std::lock_guard<std::mutex> freed_lock(freed_range_mutex); + range_set current_ranges; + for (const auto &range : freed_ranges) + { + if (range.first >= threshold) + continue; + else if (range.last > threshold) + { + range_t new_range{range.first, threshold - 1}; + current_ranges.add_range(new_range); + continue; + } + current_ranges.add_range(range); + } + freed_ranges= std::move(current_ranges); +} + +void fsp_shrink_temp_space() +{ + uint32_t last_used_extent= 0; + fil_space_t *space= fil_system.temp_space; + mtr_t mtr; + mtr.start(); + mtr.set_log_mode(MTR_LOG_NO_REDO); + mtr.x_lock_space(space); + dberr_t err= fsp_traverse_extents(space, &last_used_extent, &mtr); + if (err != DB_SUCCESS) + { +func_exit: + sql_print_warning("InnoDB: Cannot shrink the temporary tablespace " + "due to %s", ut_strerr(err)); + mtr.commit(); + return; + } + uint32_t fixed_size= srv_tmp_space.get_min_size(), + header_size= space->size_in_header; + + if (last_used_extent >= header_size || fixed_size >= header_size) + { + /* Tablespace is being used within fixed size */ + mtr.commit(); + return; + } + + /* Set fixed size as threshold to truncate */ + if (fixed_size > last_used_extent) + last_used_extent= fixed_size; + + sql_print_information("InnoDB: Truncating temporary tablespace from " + UINT32PF " to " UINT32PF " pages", space->size, + last_used_extent); + + buf_block_t *header= fsp_get_latched_xdes_page( + page_id_t(space->id, 0), &mtr, &err); + if (!header) + goto func_exit; + + mach_write_to_4( + FSP_HEADER_OFFSET + FSP_SIZE + header->page.frame, + last_used_extent); + + if (space->free_limit > last_used_extent) + mach_write_to_4( + FSP_HEADER_OFFSET + FSP_FREE_LIMIT + header->page.frame, + last_used_extent); + + mtr.set_modified(*header); + + err= fsp_shrink_list(header, FSP_HEADER_OFFSET + FSP_FREE, + last_used_extent, &mtr); + + if (err != DB_SUCCESS) + goto func_exit; + + err= fsp_shrink_list( + header, FSP_HEADER_OFFSET + FSP_FREE_FRAG, + last_used_extent, &mtr); + DBUG_EXECUTE_IF("fail_temp_truncate", err= DB_ERROR;); + if (err != DB_SUCCESS) + goto func_exit; + + err= fsp_xdes_reset(space->id, last_used_extent, &mtr); + if (err != DB_SUCCESS) + goto func_exit; + + space->clear_freed_ranges(last_used_extent); + buf_LRU_truncate_temp(last_used_extent); + mysql_mutex_lock(&fil_system.mutex); + + space->size= last_used_extent; + if (space->free_limit > last_used_extent) + space->free_limit= space->size; + + space->free_len= flst_get_len( + FSP_HEADER_OFFSET + FSP_FREE+ header->page.frame); + + /* Last file new size after truncation */ + uint32_t new_last_file_size= + last_used_extent - + (fixed_size - srv_tmp_space.m_files.at( + srv_tmp_space.m_files.size() - 1).param_size()); + + space->size_in_header= space->size; + space->chain.end->size= new_last_file_size; + srv_tmp_space.set_last_file_size(new_last_file_size); + mysql_mutex_unlock(&fil_system.mutex); + os_file_truncate( + space->chain.end->name, space->chain.end->handle, + os_offset_t{space->chain.end->size} << srv_page_size_shift, true); + mtr.commit(); + sql_print_information("InnoDB: Temporary tablespace truncated successfully"); +} |