summaryrefslogtreecommitdiffstats
path: root/storage/innobase/fsp
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/fsp')
-rw-r--r--storage/innobase/fsp/fsp0fsp.cc748
-rw-r--r--storage/innobase/fsp/fsp0sysspace.cc14
2 files changed, 752 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");
+}
diff --git a/storage/innobase/fsp/fsp0sysspace.cc b/storage/innobase/fsp/fsp0sysspace.cc
index 4ac9da50..be52e7e4 100644
--- a/storage/innobase/fsp/fsp0sysspace.cc
+++ b/storage/innobase/fsp/fsp0sysspace.cc
@@ -101,6 +101,7 @@ SysTablespace::parse_params(
ut_ad(m_last_file_size_max == 0);
ut_ad(!m_auto_extend_last_file);
+ ut_ad(!m_auto_shrink);
char* new_str = mem_strdup(filepath_spec);
char* str = new_str;
@@ -147,6 +148,11 @@ SysTablespace::parse_params(
str = parse_units(str, &size);
}
+ if (0 == strncmp(str, ":autoshrink",
+ (sizeof ":autoshrink") - 1)) {
+ str += (sizeof ":autoshrink") - 1;
+ }
+
if (*str != '\0') {
ut_free(new_str);
ib::error()
@@ -267,6 +273,12 @@ SysTablespace::parse_params(
str = parse_units(str, &m_last_file_size_max);
}
+ if (0 == strncmp(str, ":autoshrink",
+ (sizeof ":autoshrink") - 1)) {
+ str += (sizeof ":autoshrink") - 1;
+ m_auto_shrink = true;
+ }
+
if (*str != '\0') {
ut_free(new_str);
ib::error() << "syntax error in file path or"
@@ -334,6 +346,7 @@ SysTablespace::shutdown()
m_created_new_raw = 0;
m_is_tablespace_full = false;
m_sanity_checks_done = false;
+ m_auto_shrink = false;
}
/** Verify the size of the physical file.
@@ -992,6 +1005,7 @@ SysTablespace::normalize_size()
for (files_t::iterator it = m_files.begin(); it != end; ++it) {
it->m_size <<= (20U - srv_page_size_shift);
+ it->m_user_param_size = it->m_size;
}
m_last_file_size_max <<= (20U - srv_page_size_shift);