/** Returns the first extent descriptor for a segment. Wethinkoftheextentlistsofthesegmentcatenatedintheorder FSEG_FULL->FSEG_NOT_FULL->FSEG_FREE. @param[in]inodesegmentinode @param[in]spacetablespace @param[in,out]mtrmini-transaction @param[out]errerrorcode
@return the first extent descriptor, or NULL if none */
MY_ATTRIBUTE((nonnull, warn_unused_result)) static
xdes_t*
fseg_get_first_extent(
fseg_inode_t* inode, const fil_space_t* space,
mtr_t* mtr,
dberr_t* err);
ATTRIBUTE_COLD MY_ATTRIBUTE((nonnull, warn_unused_result)) /** Put new extents to the free list if there are free extents above the free limit.Ifanextenthappenstocontainanextentdescriptorpage,theextent isputtotheFSP_FREE_FRAGlistwiththepagemarkedasused. @param[in]init_spacetrueifthisisasingle-tabletablespace andweareonlyinitializingthefirstextentandthefirstbitmappages; thenwewillnotallocatemoreextents @param[in,out]spacetablespace @param[in,out]headertablespaceheader
@param[in,out] mtr mini-transaction */ static
dberr_t
fsp_fill_free_list( bool init_space,
fil_space_t* space,
buf_block_t* header,
mtr_t* mtr);
/** Allocates a single free page from a segment. Thisfunctionimplementstheintelligentallocationstrategywhichtriesto minimizefilespacefragmentation. @param[in,out]spacetablespace @param[in,out]seg_inodesegmentinode @param[in,out]iblocksegmentinodepage @param[in]hinthintofwhichpagewouldbedesirable @param[in]directionifthenewpageisneededbecauseof anindexpagesplit,andrecordsareinsertedthereinorder,intowhich directiontheygoalphabetically:FSP_DOWN,FSP_UP,FSP_NO_DIR @param[in,out]mtrmini-transaction @param[in,out]init_mtrmtroranothermini-transactionin whichthepageshouldbeinitialized. @param[out]errerrorcode @returntheallocatedpage
@retval nullptr if no page could be allocated */ static
buf_block_t*
fseg_alloc_free_page_low(
fil_space_t* space,
fseg_inode_t* seg_inode,
buf_block_t* iblock,
uint32_t hint,
byte direction, #ifdef UNIV_DEBUG bool has_done_reservation, /*!< whether the space has already been reserved */ #endif/* UNIV_DEBUG */
mtr_t* mtr,
mtr_t* init_mtr,
dberr_t* err)
MY_ATTRIBUTE((nonnull, warn_unused_result));
MY_ATTRIBUTE((nonnull, warn_unused_result)) /** Get the tablespace header block, SX-latched @param[in]spacetablespace @param[in,out]mtrmini-transaction @param[out]errerrorcode @returnpointertothespaceheader,pagex-locked
@retval nullptr if the page cannot be retrieved or is corrupted */ static buf_block_t *fsp_get_header(const fil_space_t *space, mtr_t *mtr,
dberr_t *err)
{ const page_id_t id{space->id, 0};
buf_block_t *block= mtr->get_already_latched(id, MTR_MEMO_PAGE_SX_FIX); if (block)
*err= DB_SUCCESS; else
{
block= buf_page_get_gen(id, space->zip_size(), RW_SX_LATCH,
nullptr, BUF_GET_POSSIBLY_FREED,
mtr, err); if (block &&
space->id != mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_ID +
block->page.frame))
{
*err= DB_CORRUPTION;
block= nullptr;
}
} return block;
}
if (!xdes_get_n_used(descr))
{ /* We move the extent from the free list to the NOT_FULL list */ if (dberr_t err= flst_remove(iblock, uint16_t(FSEG_FREE + ioffset),
xdes, xoffset, limit, mtr)) return err; if (dberr_t err= flst_add_last(iblock, uint16_t(FSEG_NOT_FULL + ioffset),
xdes, xoffset, limit, mtr)) return err;
}
if (UNIV_UNLIKELY(!xdes_is_free(descr, page % FSP_EXTENT_SIZE))) return DB_CORRUPTION;
/* We mark the page as used */
xdes_set_free<false>(*xdes, descr, page % FSP_EXTENT_SIZE, mtr);
byte* p_not_full= seg_inode + FSEG_NOT_FULL_N_USED; const uint32_t not_full_n_used= mach_read_from_4(p_not_full) + 1;
mtr->write<4>(*iblock, p_not_full, not_full_n_used); if (xdes_is_full(descr))
{ /* We move the extent from the NOT_FULL list to the FULL list */ if (dberr_t err= flst_remove(iblock, uint16_t(FSEG_NOT_FULL + ioffset),
xdes, xoffset, limit, mtr)) return err; if (dberr_t err= flst_add_last(iblock, uint16_t(FSEG_FULL + ioffset),
xdes, xoffset, limit, mtr)) return err;
mtr->write<4>(*iblock, seg_inode + FSEG_NOT_FULL_N_USED,
not_full_n_used - FSP_EXTENT_SIZE);
}
return DB_SUCCESS;
}
/** Get pointer to a the extent descriptor of a page. @param[in,out]sp_headertablespaceheaderpage,x-latched @param[in]spacetablespace @param[in]offsetpageoffset @param[in,out]mtrmini-transaction @param[out]errerrorcode @param[out]desc_blockdescriptorblock @param[in]init_spacewhetherthetablespaceisbeinginitialized @returnpointertotheextentdescriptor,NULLifthepagedoesnot
exist in the space or if the offset exceeds free limit */
UNIV_INLINE MY_ATTRIBUTE((warn_unused_result))
xdes_t*
xdes_get_descriptor_with_space_hdr(
buf_block_t* header, const fil_space_t* space,
uint32_t offset,
mtr_t* mtr,
dberr_t* err = nullptr,
buf_block_t** desc_block = nullptr, bool init_space = false)
{
ut_ad(space->is_owner());
ut_ad(mtr->memo_contains_flagged(header, MTR_MEMO_PAGE_SX_FIX
| MTR_MEMO_PAGE_X_FIX)); /* Read free limit and space size */
uint32_t limit = mach_read_from_4(FSP_HEADER_OFFSET + FSP_FREE_LIMIT
+ header->page.frame);
uint32_t size = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SIZE
+ header->page.frame);
ut_ad(limit == space->free_limit
|| (space->free_limit == 0
&& (init_space
|| space->is_temporary()
|| (srv_startup_is_before_trx_rollback_phase
&& (space->id == TRX_SYS_SPACE
|| srv_is_undo_tablespace(space->id))))));
ut_ad(size == space->size_in_header);
MY_ATTRIBUTE((nonnull(3), warn_unused_result)) /** Get a pointer to the extent descriptor. The page where the extentdescriptorresidesisx-locked. @paramspacetablespace @paramlst_nodefileaddressofthelistnodecontainedinthedescriptor @parammtrmini-transaction @paramerrerrorcode @paramblockextentdescriptorblock
@return pointer to the extent descriptor */ staticinline
xdes_t *xdes_lst_get_descriptor(const fil_space_t &space, fil_addr_t lst_node,
mtr_t *mtr, buf_block_t **block= nullptr,
dberr_t *err= nullptr)
{
ut_ad(mtr->memo_contains(space));
ut_ad(lst_node.boffset < space.physical_size());
buf_block_t *b; if (!block)
block= &b;
*block= buf_page_get_gen(page_id_t{space.id, lst_node.page},
space.zip_size(), RW_SX_LATCH,
nullptr, BUF_GET_POSSIBLY_FREED, mtr, err); if (*block) return (*block)->page.frame + lst_node.boffset - XDES_FLST_NODE;
space.set_corrupted(); return nullptr;
}
/********************************************************************//**
Returns page offset of the first page in extent described by a descriptor.
@return offset of the first page in extent */ static uint32_t xdes_get_offset(const xdes_t *descr)
{
ut_ad(descr); const page_t *page= page_align(descr); return page_get_page_no(page) +
uint32_t(((descr - page - XDES_ARR_OFFSET) / XDES_SIZE) *
FSP_EXTENT_SIZE);
}
/** Initialize a file page whose prior contents should be ignored.
@param[in,out] block buffer pool block */ void fsp_apply_init_file_page(buf_block_t *block)
{
memset_aligned<UNIV_PAGE_SIZE_MIN>(block->page.frame, 0, srv_page_size); const page_id_t id(block->page.id());
#ifdef UNIV_DEBUG /** Assert that the mini-transaction is compatible with updatinganallocationbitmappage.
@param[in] mtr mini-transaction */ void fil_space_t::modify_check(const mtr_t& mtr) const
{ switch (mtr.get_log_mode()) { case MTR_LOG_NONE: /* These modes are only allowed within a non-bitmap page
when there is a higher-level redo log record written. */
ut_ad(!is_being_imported()); break; case MTR_LOG_NO_REDO:
ut_ad(is_temporary() || is_being_imported()); break; default: /* We may only write redo log for a persistent tablespace. */
ut_ad(!is_temporary());
ut_ad(!is_being_imported());
ut_ad(mtr.is_named_space(id));
}
} #endif
/* Write encryption metadata to page 0 if tablespace is
encrypted or encryption is disabled by table option. */ if (space->crypt_data &&
(space->crypt_data->should_encrypt() ||
space->crypt_data->not_encrypted())) {
space->crypt_data->write_page0(block, mtr);
}
return DB_SUCCESS;
}
/** Try to extend a single-table tablespace so that a page would fit in the datafile. @param[in,out]spacetablespace @param[in]page_nopagenumber @param[in,out]headertablespaceheader @param[in,out]mtrmini-transaction
@return true if success */ static ATTRIBUTE_COLD __attribute__((warn_unused_result)) bool
fsp_try_extend_data_file_with_pages(
fil_space_t* space,
uint32_t page_no,
buf_block_t* header,
mtr_t* mtr)
{
ut_ad(!is_system_tablespace(space->id));
ut_d(space->modify_check(*mtr));
/** Calculate the number of physical pages in an extent for this file. @param[in]physical_sizepage_sizeofthedatafile
@return number of pages in an extent for this file */ inline uint32_t fsp_get_extent_size_in_pages(ulint physical_size)
{ return uint32_t((FSP_EXTENT_SIZE << srv_page_size_shift) / physical_size);
}
/** Calculate the number of pages to extend a datafile. Weextendsingle-tabletablespacesfirstoneextentatatime, but4atatimeforbiggertablespaces.Itisnotenoughtoextendalways byoneextent,becauseweneedtoaddatleastoneextenttoFSP_FREE. Asingleextentdescriptorpagewilltrackmanyextents.Andtheextent thatusesitsextentdescriptorpageisputontotheFSP_FREE_FRAGlist. ExtentsthatdonotusetheirextentdescriptorpageareaddedtoFSP_FREE. Thephysicalpagesizeisusedtodeterminehowmanyextentsaretracked ononeextentdescriptorpage.Seexdes_calc_descriptor_page(). @param[in]physical_sizepagesizeindatafile @param[in]sizecurrentnumberofpagesinthedatafile
@return number of pages to extend the file. */ static uint32_t fsp_get_pages_to_extend_ibd(unsigned physical_size,
uint32_t size)
{
uint32_t extent_size = fsp_get_extent_size_in_pages(physical_size); /* The threshold is set at 32MiB except when the physical page
size is small enough that it must be done sooner. */
uint32_t threshold = std::min(32 * extent_size, physical_size);
if (size >= threshold) { /* Below in fsp_fill_free_list() we assume thatweaddatmostFSP_FREE_ADDextentsat
a time */
extent_size *= FSP_FREE_ADD;
}
return extent_size;
}
/** Check if tablespace size exceeds warning threshold and emit warning. @paramnew_sizeNewsizeinpages @paramthresholdnonzerowarningthresholdinbytes
@return true if warning was emitted */
ATTRIBUTE_COLD bool fil_space_t::check_size_warning(uint32_t new_size,
ulonglong threshold)
noexcept
{ const uint warning_pct= fil_system.tablespace_size_warning_pct;
/* Reset state if threshold or warning percentage changed */ const uint32_t threshold_pages=
uint32_t(threshold / physical_size()); if ((m_last_warning_threshold ^ threshold_pages) |
(uint{m_last_warning_pct} ^ warning_pct)) {
m_last_size_warning_pct= 0;
m_last_warning_threshold= threshold_pages;
m_last_warning_pct= uint8_t(warning_pct);
}
uint64_t current_bytes=
uint64_t(new_size) * physical_size(); /* new_size is at most 1ULL<<32 and physical_size() at most 1<<16,
so current_bytes * 100 < 1ULL<<55, well within uint64_t range. */
uint64_t current_pct=
(current_bytes * 100) / threshold;
uint8_t display_pct=
uint8_t(std::min(current_pct, uint64_t{100}));
if (display_pct < warning_pct) returnfalse;
if (display_pct <= m_last_size_warning_pct) returnfalse;
/* Warn on every 1% increase */ constauto n= name();
sql_print_warning("InnoDB: Tablespace '%.*s' size %llu bytes" " reached %u%% of configured threshold" " of %llu bytes", int(n.size()), n.data(),
ulonglong{current_bytes}, unsigned{display_pct},
ulonglong{threshold});
m_last_size_warning_pct= display_pct;
returntrue;
}
/** Extend the tablespace, update size_in_header, and emit size warnings. @paramsizedesiredsizeinpages @paramheadertablespaceheaderblock @parammtrmini-transaction
@return whether the extension succeeded */ bool fil_space_t::extend(uint32_t size, buf_block_t *header, mtr_t *mtr)
noexcept
{ if (!fil_space_extend(this, size)) returnfalse;
/* For the system tablespace, we ignore any fragments of a
full megabyte when storing the size to the space header */ constunsigned ps= physical_size();
size_in_header= id
? this->size
: ut_2pow_round(this->size, (1024 * 1024) / ps);
/* recv_sys_t::parse() expects to find a WRITE record that coversall4bytes.Therefore,wemustspecifymtr_t::FORCED inordertoavoidoptimizingawayanyunchangedmost
significant bytes of FSP_SIZE. */
mtr->write<4,mtr_t::FORCED>(*header, FSP_HEADER_OFFSET + FSP_SIZE +
header->page.frame, size_in_header);
if (uint64_t threshold= fil_system.tablespace_size_warning_threshold)
check_size_warning(size_in_header, threshold); returntrue;
}
/** Try to extend the last data file of a tablespace if it is auto-extending. @param[in,out]spacetablespace @param[in,out]headertablespaceheader @param[in,out]mtrmini-transaction @returnnumberofpagesadded
@retval 0 if the tablespace was not extended */
ATTRIBUTE_COLD __attribute__((nonnull)) static
ulint
fsp_try_extend_data_file(fil_space_t *space, buf_block_t *header, mtr_t *mtr)
{ constchar* OUT_OF_SPACE_MSG = "ran out of space. Please add another file or use" " 'autoextend' for the last file in setting";
ut_d(space->modify_check(*mtr));
if (space->id == TRX_SYS_SPACE
&& !srv_sys_space.can_auto_extend_last_file()) {
/* We print the error message only once to avoid spammingtheerrorlog.Notethatwedon'tneed toresettheflagtofalseasdealingwiththis
error requires server restart. */ if (!srv_sys_space.get_tablespace_full_status()) {
sql_print_error("InnoDB: The InnoDB system tablespace " "%s"" innodb_data_file_path.",
OUT_OF_SPACE_MSG);
srv_sys_space.set_tablespace_full_status(true);
} return(0);
} elseif (space->id == SRV_TMP_SPACE_ID
&& !srv_tmp_space.can_auto_extend_last_file()) {
/* We print the error message only once to avoid spammingtheerrorlog.Notethatwedon'tneed toresettheflagtofalseasdealingwiththis
error requires server restart. */ if (!srv_tmp_space.get_tablespace_full_status()) {
sql_print_error("InnoDB: The InnoDB temporary" " tablespace %s" " innodb_temp_data_file_path.",
OUT_OF_SPACE_MSG);
srv_tmp_space.set_tablespace_full_status(true);
} return(0);
}
/** Put new extents to the free list if there are free extents above the free limit.Ifanextenthappenstocontainanextentdescriptorpage,theextent isputtotheFSP_FREE_FRAGlistwiththepagemarkedasused. @param[in]init_spacetrueifthisisasingle-tabletablespace andweareonlyinitializingthefirstextentandthefirstbitmappages; thenwewillnotallocatemoreextents @param[in,out]spacetablespace @param[in,out]headertablespaceheader @param[in,out]mtrmini-transaction
@return error code */ static
dberr_t
fsp_fill_free_list( bool init_space,
fil_space_t* space,
buf_block_t* header,
mtr_t* mtr)
{
ut_d(space->modify_check(*mtr));
/* Check if we can fill free list from above the free list limit */
uint32_t size=
mach_read_from_4(FSP_HEADER_OFFSET + FSP_SIZE + header->page.frame);
uint32_t limit=
mach_read_from_4(FSP_HEADER_OFFSET + FSP_FREE_LIMIT + header->page.frame);
if (xdes_get_state(descr) == XDES_FREE) { /* Ok, we can take this extent */
} else { /* Take the first extent in the free list */
first = flst_get_first(FSP_HEADER_OFFSET + FSP_FREE
+ header->page.frame);
if (first.page >= space->free_limit) { if (first.page != FIL_NULL) { goto flst_corrupted;
}
/** Allocates a single free page from a space. Thepageismarkedasused. @param[in,out]spacetablespace @param[in]hinthintofwhichpagewouldbedesirable @param[in,out]mtrmini-transaction @param[in,out]init_mtrmini-transactioninwhichthepageshouldbe initialized(maybethesameasmtr) @param[out]errerrorcode @returnallocatedblock
@retval nullptr if no page could be allocated */ static MY_ATTRIBUTE((warn_unused_result, nonnull))
buf_block_t *fsp_alloc_free_page(fil_space_t *space, uint32_t hint,
mtr_t *mtr, mtr_t *init_mtr, dberr_t *err)
{
ut_d(space->modify_check(*mtr));
buf_block_t *block= fsp_get_header(space, mtr, err); if (!block) return block;
buf_block_t *xdes; /* Get the hinted descriptor */
xdes_t *descr= xdes_get_descriptor_with_space_hdr(block, space, hint, mtr,
err, &xdes); if (descr && xdes_get_state(descr) == XDES_FREE_FRAG) /* Ok, we can take this extent */; elseif (*err != DB_SUCCESS)
{
err_exit:
space->set_corrupted(); return nullptr;
} else
{ /* Else take the first extent in free_frag list */
fil_addr_t first = flst_get_first(FSP_HEADER_OFFSET + FSP_FREE_FRAG +
block->page.frame); if (first.page >= space->free_limit)
{ if (first.page != FIL_NULL) goto flst_corrupted;
/* There are no partially full fragments: allocate a free extent andaddittotheFREE_FRAGlist.NOTEthattheallocationmay haveasaside-effectthatanextentcontainingadescriptor pageisaddedtotheFREE_FRAGlist.Butwewillallocateour
page from the the free extent anyway. */
descr= fsp_alloc_free_extent(space, hint, &xdes, mtr, err); if (!descr) return nullptr;
*err= flst_add_last(block, FSP_HEADER_OFFSET + FSP_FREE_FRAG, xdes, static_cast<uint16_t>(descr - xdes->page.frame +
XDES_FLST_NODE),
space->free_limit, mtr); if (UNIV_UNLIKELY(*err != DB_SUCCESS)) return nullptr;
xdes_set_state(*xdes, descr, XDES_FREE_FRAG, mtr);
} else
{ if (first.boffset < FSP_HEADER_OFFSET + FSP_HEADER_SIZE ||
first.boffset >= space->physical_size() -
(XDES_SIZE + FIL_PAGE_DATA_END))
{
flst_corrupted:
*err= DB_CORRUPTION; goto err_exit;
}
descr= xdes_lst_get_descriptor(*space, first, mtr, &xdes, err); if (!descr) return nullptr; /* Reset the hint */
hint= 0;
}
}
/* Now we have in descr an extent with at least one free page. Look
for a free page in the extent. */
uint32_t free= xdes_find_free(descr, hint % FSP_EXTENT_SIZE); if (free == FIL_NULL)
{
corrupted:
*err= DB_CORRUPTION; goto err_exit;
}
if (space_size <= page_no)
{ /* It must be that we are extending a single-table tablespace
whose size is still < 64 pages */
ut_ad(!is_system_tablespace(space->id)); if (page_no >= FSP_EXTENT_SIZE)
{
sql_print_error("InnoDB: Trying to extend %s" " by single page(s) though the size is " UINT32PF "." " Page no " UINT32PF ".",
space->chain.start->name, space_size, page_no); goto corrupted;
}
switch (state) { case XDES_FREE_FRAG: case XDES_FULL_FRAG: if (!xdes_is_free(descr, offset % FSP_EXTENT_SIZE)) { break;
} /* fall through */ default:
space->set_corrupted(); return DB_CORRUPTION;
}
if (!xdes_get_n_used(descr)) { /* The extent has become free: move it to another list */
err = flst_remove(header, FSP_HEADER_OFFSET + FSP_FREE_FRAG,
xdes, xoffset, limit, mtr); if (err == DB_SUCCESS) {
err = fsp_free_extent(space, offset, mtr);
}
}
return err;
}
/** @return Number of segment inodes which fit on a single page */ inline ulint FSP_SEG_INODES_PER_PAGE(ulint physical_size)
{ return (physical_size - FSEG_ARR_OFFSET - 10) / FSEG_INODE_SIZE;
}
/** Returns the nth inode slot on an inode page. @param[in]pagesegmentinodepage @param[in]iinodeindexonpage
@return segment inode */ #define fsp_seg_inode_page_get_nth_inode(page, i) \
FSEG_ARR_OFFSET + FSEG_INODE_SIZE * i + page
/** Looks for a used segment inode on a segment inode page. @parampagesegmentinodepage @paramphysical_sizepagesize @returnsegmentinodeindex
@retval ULINT_UNDEFINED if not found */ static
ulint
fsp_seg_inode_page_find_used(const page_t *page, ulint physical_size)
{ for (ulint i= 0; i < FSP_SEG_INODES_PER_PAGE(physical_size); i++)
{ const byte *inode= fsp_seg_inode_page_get_nth_inode(page, i); if (mach_read_from_8(FSEG_ID + inode))
{
ut_ad(!memcmp(FSEG_MAGIC_N_BYTES, FSEG_MAGIC_N + inode, 4)); return i;
}
}
return ULINT_UNDEFINED;
}
/** Looks for an unused segment inode on a segment inode page. @param[in]pagesegmentinodepage @param[in]isearchforwardstartingfromthisindex @param[in]physical_sizepagesize @returnsegmentinodeindex
@retval ULINT_UNDEFINED if not found */ static
ulint
fsp_seg_inode_page_find_free(const page_t *page, ulint i, ulint physical_size)
{ for (; i < FSP_SEG_INODES_PER_PAGE(physical_size); i++)
{ const byte *inode= fsp_seg_inode_page_get_nth_inode(page, i); if (mach_read_from_8(FSEG_ID + inode))
ut_ad(!memcmp(FSEG_MAGIC_N_BYTES, FSEG_MAGIC_N + inode, 4)); else /* This is unused */ return i;
} return ULINT_UNDEFINED;
}
if (ULINT_UNDEFINED == fsp_seg_inode_page_find_free(block->page.frame, n + 1,
physical_size))
{ /* There are no other unused headers left on the page: move it
to another list */ const uint32_t limit= space->free_limit;
*err= flst_remove(header, FSP_HEADER_OFFSET + FSP_SEG_INODES_FREE,
block, FSEG_INODE_PAGE_NODE, limit, mtr); if (UNIV_UNLIKELY(*err != DB_SUCCESS)) return nullptr;
*err= flst_add_last(header, FSP_HEADER_OFFSET + FSP_SEG_INODES_FULL,
block, FSEG_INODE_PAGE_NODE, limit, mtr); if (UNIV_UNLIKELY(*err != DB_SUCCESS)) return nullptr;
}
if (ULINT_UNDEFINED != fsp_seg_inode_page_find_used(iblock->page.frame,
physical_size)) return DB_SUCCESS;
/* There are no other used headers left on the page: free it */
err= flst_remove(header, FSP_HEADER_OFFSET + FSP_SEG_INODES_FREE,
iblock, FSEG_INODE_PAGE_NODE, limit, mtr); if (err != DB_SUCCESS) return err; return fsp_free_page(space, iblock->page.id().page_no(), mtr);
}
MY_ATTRIBUTE((nonnull(1,4,5), warn_unused_result)) /** Returns the file segment inode, page x-latched. @param[in]headersegmentheader @param[in]spacespaceid @param[in]zip_sizeROW_FORMAT=COMPRESSEDpagesize,or0 @param[in,out]mtrmini-transaction @param[out]blockinodeblock @param[out]errerrorcode @returnsegmentinode,pagex-latched
@retval nullptr if the inode is free or corruption was noticed */ static
fseg_inode_t*
fseg_inode_try_get( const fseg_header_t* header,
uint32_t space,
ulint zip_size,
mtr_t* mtr,
buf_block_t** block,
dberr_t* err = nullptr)
{ if (UNIV_UNLIKELY(space != mach_read_from_4(header + FSEG_HDR_SPACE)))
{
corrupted: if (err)
*err= DB_CORRUPTION; return nullptr;
}
/** Get the page number from the nth fragment page slot. @paraminodefilesegmentfindex @paramnslotindex @returnpagenumber
@retval FIL_NULL if not in use */ static uint32_t fseg_get_nth_frag_page_no(const fseg_inode_t *inode, ulint n)
{
ut_ad(inode);
ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
ut_ad(!memcmp(FSEG_MAGIC_N_BYTES, FSEG_MAGIC_N + inode, 4)); return(mach_read_from_4(inode + FSEG_FRAG_ARR
+ n * FSEG_FRAG_SLOT_SIZE));
}
/** Set the page number in the nth fragment page slot. @param[in,out]inodesegmentinode @param[in,out]iblocksegmentinodepage @param[in]nslotindex @param[in]page_nopagenumbertoset
@param[in,out] mtr mini-transaction */ inlinevoid fseg_set_nth_frag_page_no(fseg_inode_t *inode, buf_block_t *iblock,
ulint n, ulint page_no, mtr_t *mtr)
{
ut_ad(n < FSEG_FRAG_ARR_N_SLOTS);
ut_ad(mtr->memo_contains_flagged(iblock, MTR_MEMO_PAGE_SX_FIX));
ut_ad(!memcmp(FSEG_MAGIC_N_BYTES, FSEG_MAGIC_N + inode, 4));
mtr->write<4>(*iblock, inode + FSEG_FRAG_ARR + n * FSEG_FRAG_SLOT_SIZE,
page_no);
}
/**********************************************************************//**
Finds a fragment page slot which is free.
@return slot index; ULINT_UNDEFINED if none found */ static
ulint
fseg_find_free_frag_page_slot( /*==========================*/
fseg_inode_t* inode) /*!< in: segment inode */
{
ulint i;
ulint page_no;
for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
page_no = fseg_get_nth_frag_page_no(inode, i);
if (page_no == FIL_NULL) {
return(i);
}
}
return(ULINT_UNDEFINED);
}
/**********************************************************************//**
Finds a fragment page slot which is used and last in the array.
@return slot index; ULINT_UNDEFINED if none found */ static
ulint
fseg_find_last_used_frag_page_slot( /*===============================*/
fseg_inode_t* inode) /*!< in: segment inode */
{
ulint i;
ulint page_no;
for (i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) {
page_no = fseg_get_nth_frag_page_no(
inode, FSEG_FRAG_ARR_N_SLOTS - i - 1);
if (page_no != FIL_NULL) {
return(FSEG_FRAG_ARR_N_SLOTS - i - 1);
}
}
return(ULINT_UNDEFINED);
}
/** Calculate reserved fragment page slots. @paraminodefilesegmentindex
@return number of fragment pages */ static uint32_t fseg_get_n_frag_pages(const fseg_inode_t *inode) noexcept
{
uint32_t count = 0;
for (ulint i = 0; i < FSEG_FRAG_ARR_N_SLOTS; i++) { if (FIL_NULL != fseg_get_nth_frag_page_no(inode, i)) {
count++;
}
}
return(count);
}
/** Create a new segment. @paramspacetablespace @parambyte_offsetbyteoffsetofthecreatedsegmentheader @parammtrmini-transaction @paramerrerrorcode @paramhas_done_reservationwhetherfsp_reserve_free_extents()wasinvoked @paramblockblockwheresegmentheaderisplaced, orNULLtoallocateanadditionalpageforthat @returntheblockwherethesegmentheaderisplaced,x-latched
@retval nullptr if could not create segment */
buf_block_t*
fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr, dberr_t *err, bool has_done_reservation, buf_block_t *block)
{
fseg_inode_t* inode;
ib_id_t seg_id;
uint32_t n_reserved = 0;
funct_exit: if (n_reserved) {
ut_ad(!has_done_reservation);
space->release_free_extents(n_reserved);
}
DBUG_RETURN(block);
}
/**********************************************************************//**
Calculates the number of pages reserved by a segment, and how many pages are
currently used.
@return number of reserved pages */ static
uint32_t
fseg_n_reserved_pages_low( /*======================*/ const fseg_inode_t* inode, /*!< in: segment inode */
uint32_t* used) /*!< out: number of pages used (not
more than reserved) */
noexcept
{ const uint32_t extent_size = FSP_EXTENT_SIZE;
if (fseg_n_reserved_pages_low(inode, &used) <
FSEG_FREE_LIST_LIMIT * FSP_EXTENT_SIZE) /* The segment is too small to allow extents in free list */ return DB_SUCCESS;
/* Segment free list is not empty, allocate from it */ return xdes_lst_get_descriptor(*space, first, mtr, xdes, err);
}
xdes_t* descr= fsp_alloc_free_extent(space, 0, xdes, mtr, err); if (UNIV_UNLIKELY(!descr)) return descr;
xdes_set_state(**xdes, descr, XDES_FSEG, mtr);
mtr->memcpy<mtr_t::MAYBE_NOP>(**xdes, descr + XDES_ID, inode + FSEG_ID, 8);
*err= flst_add_last(iblock, static_cast<uint16_t>(inode - iblock->page.frame +
FSEG_FREE), *xdes, static_cast<uint16_t>(descr - (*xdes)->page.frame +
XDES_FLST_NODE),
space->free_limit, mtr); if (UNIV_LIKELY(*err != DB_SUCCESS)) return nullptr; /* Try to fill the segment free list */
*err= fseg_fill_free_list(inode, iblock, space,
xdes_get_offset(descr) + FSP_EXTENT_SIZE, mtr); if (UNIV_UNLIKELY(*err != DB_SUCCESS)) return nullptr;
return descr;
}
/** Allocates a single free page from a segment. Thisfunctionimplementstheintelligentallocationstrategywhichtriesto minimizefilespacefragmentation. @param[in,out]spacetablespace @param[in,out]seg_inodesegmentinode @param[in,out]iblocksegmentinodepage @param[in]hinthintofwhichpagewouldbedesirable @param[in]directionifthenewpageisneededbecauseof anindexpagesplit,andrecordsareinsertedthereinorder,intowhich directiontheygoalphabetically:FSP_DOWN,FSP_UP,FSP_NO_DIR @param[in,out]mtrmini-transaction @param[in,out]init_mtrmtroranothermini-transactionin whichthepageshouldbeinitialized. @param[out]errerrorcode @returntheallocatedpage
@retval nullptr if no page could be allocated */ static
buf_block_t*
fseg_alloc_free_page_low(
fil_space_t* space,
fseg_inode_t* seg_inode,
buf_block_t* iblock,
uint32_t hint,
byte direction, #ifdef UNIV_DEBUG bool has_done_reservation, /*!< whether the space has already been reserved */ #endif/* UNIV_DEBUG */
mtr_t* mtr,
mtr_t* init_mtr,
dberr_t* err)
{
ib_id_t seg_id;
uint32_t used, reserved;
xdes_t* descr; /*!< extent of the hinted page */
uint32_t ret_page; /*!< the allocated page offset, FIL_NULL
if could not be allocated */
xdes_t* ret_descr; /*!< the extent of the allocated page */
buf_block_t* xdes;
ulint n;
descr = xdes_get_descriptor_with_space_hdr(header, space, hint, mtr,
err, &xdes); if (!descr) { if (*err != DB_SUCCESS) { return nullptr;
} /* Hint outside space or too high above free limit: reset
hint */ /* The file space header page is always allocated. */
hint = 0;
descr = xdes_get_descriptor(space, hint, mtr, err, &xdes); if (!descr) { return nullptr;
}
}
const uint32_t extent_size = FSP_EXTENT_SIZE;
ret_descr = descr; /* Try to get the page from extent which belongs to segment */ if (xdes_get_state(descr) == XDES_FSEG
&& mach_read_from_8(descr + XDES_ID) == seg_id) { /* Get the page from the segment extent */ if (xdes_is_free(descr, hint % extent_size)) {
take_hinted_page:
ret_page = hint; goto got_hinted_page;
} elseif (!xdes_is_full(descr)) { /* Take the page from the same extent as the hintedpage(andtheextentalreadybelongsto
the segment) */
ret_page = xdes_find_free(descr, hint % extent_size); if (ret_page == FIL_NULL) {
ut_ad(!has_done_reservation); return nullptr;
}
ret_page += xdes_get_offset(ret_descr); goto alloc_done;
}
}
/** If the number of unused but reserved pages in a segment is lessthanminimumvalueof1/8ofreservedpagesor 4*FSP_EXTENT_SIZEandthereareatleasthalfofextentsize usedpages,thenweallowanewemptyextenttobeaddedto thesegmentinfseg_alloc_free_page_general().Otherwise,weuse
unused pages of the segment. */ if (used < extent_size / 2 ||
reserved - used >= reserved / 8 ||
reserved - used >= extent_size * 4) {
} elseif (xdes_get_state(descr) == XDES_FREE) { /* Allocate the free extent from space and can
take the hinted page */
ret_descr = fsp_alloc_free_extent(space, hint, &xdes,
mtr, err);
if (UNIV_UNLIKELY(ret_descr != descr)) { if (*err != DB_SUCCESS) {
*err = DB_CORRUPTION;
} return nullptr;
}
if (!ret_descr) {
ut_ad(*err != DB_SUCCESS); return nullptr;
} /* Take any free extent (which was already assigned aboveintheif-conditiontoret_descr)andtakethe
lowest or highest page in it, depending on the direction */
ret_page = xdes_get_offset(ret_descr);
/* Try to take individual page from the segment or tablespace */ if (reserved - used > 0) { /* Take any unused page from the segment */
fil_addr_t first;
} elseif (used < extent_size / 2) { /* Allocate an individual page from the space */
buf_block_t* block = fsp_alloc_free_page(
space, hint, mtr, init_mtr, err);
if (block) { /* Put the page in the fragment page array of the
segment */
n = fseg_find_free_frag_page_slot(seg_inode); if (UNIV_UNLIKELY(n == ULINT_UNDEFINED)) {
*err = DB_CORRUPTION; return nullptr;
}
/* fsp_alloc_free_page() invoked fsp_init_file_page()
already. */ return(block);
} else { /* In worst case, try to allocate a new extent
and take its first page */
ret_descr = fseg_alloc_free_extent(seg_inode, iblock, &xdes,
space, mtr, err); if (!ret_descr) {
ut_ad(!has_done_reservation || *err); return nullptr;
} else {
ret_page = xdes_get_offset(ret_descr);
}
}
if (ret_page == FIL_NULL) { /* Page could not be allocated */
ut_ad(!has_done_reservation); return nullptr;
}
alloc_done: if (space->size <= ret_page && !is_predefined_tablespace(space->id)) { /* It must be that we are extending a single-table
tablespace whose size is still < 64 pages */ if (ret_page >= extent_size) {
sql_print_error("InnoDB: Trying to extend '%s'" " by single page(s) though the" " space size " UINT32PF "." " Page no " UINT32PF ".",
space->chain.start->name, space->size,
ret_page);
ut_ad(!has_done_reservation); return nullptr;
}
if (!fsp_try_extend_data_file_with_pages(
space, ret_page, header, mtr)) { /* No disk space left */
ut_ad(!has_done_reservation); return nullptr;
}
}
/* Skip the check for extending the tablespace. Ifthepagehintwerenotwithinthesizeofthetablespace, descrsettonullptraboveandresetthehintandtheblock
was allocated from free_frag (XDES_FREE_FRAG) */ if (ret_descr != NULL) {
got_hinted_page: /* At this point we know the extent and the page offset. Theextentisstillintheappropriatelist(FSEG_NOT_FULL
or FSEG_FREE), and the page is not yet marked as used. */
ut_d(buf_block_t* xxdes);
ut_ad(xdes_get_descriptor(space, ret_page, mtr, err, &xxdes)
== ret_descr);
ut_ad(xdes == xxdes);
ut_ad(xdes_is_free(ret_descr, ret_page % extent_size));
/**********************************************************************//**
Allocates a single free page from a segment. This function implements
the intelligent allocation strategy which tries to minimize file space
fragmentation.
@retval NULL if no page could be allocated */
buf_block_t*
fseg_alloc_free_page_general( /*=========================*/
fseg_header_t* seg_header,/*!< in/out: segment header */
uint32_t hint, /*!< in: hint of which page would be
desirable */
byte direction,/*!< in: if the new page is needed because ofanindexpagesplit,andrecordsare insertedthereinorder,intowhich directiontheygoalphabetically:FSP_DOWN,
FSP_UP, FSP_NO_DIR */ bool has_done_reservation, /*!< in: true if the caller has alreadydonethereservationforthepage withfsp_reserve_free_extents,thenthere isnoneedtodothecheckforthisindividual
page */
mtr_t* mtr, /*!< in/out: mini-transaction */
mtr_t* init_mtr,/*!< in/out: mtr or another mini-transaction
in which the page should be initialized. */
dberr_t* err) /*!< out: error code */
{
fseg_inode_t* inode;
fil_space_t* space;
buf_block_t* iblock;
buf_block_t* block;
uint32_t n_reserved;
const uint32_t space_id = page_get_space_id(page_align(seg_header));
space = mtr->x_lock_space(space_id);
inode = fseg_inode_try_get(seg_header, space_id, space->zip_size(),
mtr, &iblock, err); if (!inode) { return nullptr;
} if (!space->full_crc32()) {
fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr);
}
if (!has_done_reservation) {
*err = fsp_reserve_free_extents(&n_reserved, space, 2,
FSP_NORMAL, mtr); if (*err != DB_SUCCESS) { return nullptr;
}
}
/** Reserves free pages from a tablespace. All mini-transactions which may useseveralpagesfromthetablespaceshouldcallthisfunctionbeforehand andreserveenoughfreeextentssothattheycertainlywillbeable todotheiroperation,likeaB-treepagesplit,fully.Reservations mustbereleasedwithfunctionfil_space_t::release_free_extents()!
/* Below we play safe when counting free extents above the free limit: someofthemwillcontainextentdescriptorpages,andtherefore
will not be free extents */
switch (alloc_type) { case FSP_NORMAL: /* We reserve 1 extent + 0.5 % of the space size to undo logs and1extent+0.5%tocleaningoperations;NOTE:thissource
code is duplicated in the function below! */
reserve = 2 + ((size / extent_size) * 2) / 200;
if (n_free <= reserve + n_ext) {
goto try_to_extend;
} break; case FSP_UNDO: /* We reserve 0.5 % of the space size to cleaning operations */
reserve = 1 + ((size / extent_size) * 1) / 200;
if (n_free <= reserve + n_ext) {
goto try_to_extend;
} break; case FSP_CLEANING: case FSP_BLOB:
reserve = 0; break; default:
ut_error;
}
if (space->reserve_free_extents(n_free, n_ext)) { return DB_SUCCESS;
}
try_to_extend: if (fsp_try_extend_data_file(space, header, mtr)) { goto try_again;
}
if (!descr) { return err;
} if (UNIV_UNLIKELY(xdes_is_free(descr, offset & (extent_size - 1)))) {
corrupted:
space->set_corrupted(); return DB_CORRUPTION;
}
if (xdes_get_state(descr) != XDES_FSEG) { /* The page is in the fragment pages of the segment */ for (ulint i = 0;; i++) { if (fseg_get_nth_frag_page_no(seg_inode, i)
!= offset) { continue;
}
#ifdef BTR_CUR_HASH_ADAPT if (ahi) { for (uint32_t i = 0; i < FSP_EXTENT_SIZE; i++) { if (!xdes_is_free(descr, i)) { /* Drop search system page hash index ifthepageisfoundinthepooland
is hashed */
btr_search_drop_page_hash_when_freed(
mtr,
page_id_t(space->id,
first_page_in_extent + i));
}
}
} #endif/* BTR_CUR_HASH_ADAPT */
for (uint32_t i : going_to_free) {
mtr->free(*space, first_page_in_extent + i);
buf_page_free(space, first_page_in_extent + i, mtr);
}
return DB_SUCCESS;
}
/** Free the extent and fragment page associated with thesegment. @paramspacetablespacewheresegmentresides @paraminodeindexnodeinformation @paramiblockpagewheresegmentheaderareplaced @parammtrmini-transaction @paramhdr_pagesegmentheaderpage @paramahiadaptivehashindex @returnDB_SUCCESS_LOCKED_RECwhenfreeingwasn'tcompleted
@return DB_SUCCESS or other error code when freeing was completed */ static
dberr_t fseg_free_step_low(fil_space_t *space, fseg_inode_t *inode,
buf_block_t *iblock, mtr_t *mtr, const page_t *hdr_page #ifdef BTR_CUR_HASH_ADAPT
, bool ahi=false #endif/* BTR_CUR_HASH_ADAPT */
)
{
dberr_t err= DB_SUCCESS; if (xdes_t *descr= fseg_get_first_extent(inode, space, mtr, &err))
{
err= fseg_free_extent(inode, iblock, space,
xdes_get_offset(descr), mtr #ifdef BTR_CUR_HASH_ADAPT
, ahi #endif/* BTR_CUR_HASH_ADAPT */
); return err == DB_SUCCESS ? DB_SUCCESS_LOCKED_REC : err;
}
if (err != DB_SUCCESS) return err;
/* Free a fragment page. If there are no fragment pages
exist in the array then free the file segment inode */
ulint n = fseg_find_last_used_frag_page_slot(inode); if (UNIV_UNLIKELY(n == ULINT_UNDEFINED)) return hdr_page
? DB_SUCCESS
: fsp_free_seg_inode(space, inode, iblock, mtr);
if (hdr_page &&
!memcmp_aligned<2>(hdr_page + FIL_PAGE_OFFSET,
inode + FSEG_FRAG_ARR + n * FSEG_FRAG_SLOT_SIZE, 4)) /* hdr_page is only passed by fseg_free_step_not_header(). Inthatcase,theheaderpagemustbepreserved,tobefreed
when we're finally called by fseg_free_step(). */ return DB_SUCCESS;
/** Get the latched page page or acquire the page. @parampage_idpageidentifiertobeacquired @parammtrmini-transaction @paramerrerrorcode
@return block descriptor */ static
buf_block_t *fsp_get_latched_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"tobemodified"extentdescriptorpageandits
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) {}
uint32_t n_pages() noexcept
{
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;
}
if (!cur) return err; if (last_valid_addr.page == FIL_NULL)
{ /* First node, so update the FIRST pointer of base withcurrentextentdescriptorandupdate thePREVpointeroflastvaliddescriptorwith
FIL_NULL */
flst_write_addr(
*header,
header->page.frame + hdr_offset + FLST_FIRST,
cur_addr.page, cur_addr.boffset, mtr);
/* 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);
}
if (find_last_used_extent)
{ /* A descriptor extent (one that starts with an extent descriptorpage)alwayskeeps2pagespermanentlyallocated: 1)Descriptor/headerpage 2)Changebufferbitmappage Inthatcase,wecouldconsiderthisextentasempty.
if (err == DB_SUCCESS)
err= add_index_root_pages(inodes, dict_sys.sys_tables); if (err == DB_SUCCESS)
err= add_index_root_pages(inodes, dict_sys.sys_indexes); if (err == DB_SUCCESS)
err= add_index_root_pages(inodes, dict_sys.sys_columns); if (err == DB_SUCCESS)
err= add_index_root_pages(inodes, dict_sys.sys_fields); return err;
}
/* Get the used inode from the system tablespace @paraminodesinodeinformationusedfoundinsystemtablespace @parammtrmini-transaction
@return error code */ static dberr_t fsp_get_sys_used_segment(inode_info *inodes, mtr_t *mtr)
{
dberr_t err= DB_SUCCESS;
buf_block_t *block= nullptr; /* Get TRX_SYS_FSEG_HEADER, TRX_SYS_DOUBLEWRITE_FSEG from
TRX_SYS_PAGE */
block= buf_pool.page_fix(page_id_t{0, TRX_SYS_PAGE_NO}, &err, nullptr,
buf_pool_t::FIX_WAIT_READ); if (!block) return err;
fil_addr_t sys_fseg_addr= flst_read_addr(block->page.frame +
TRX_SYS + TRX_SYS_FSEG_HEADER + 4); if (sys_fseg_addr.page == 0 && sys_fseg_addr.boffset == 0)
{ /* While reinitializing the undo tablespace, InnoDB fail
to reset the TRX_SYS_FSEG_HEADER offset in TRX_SYS page */
} elseif (!inodes->insert_inode(sys_fseg_addr.page, sys_fseg_addr.boffset))
err= DB_CORRUPTION;
if (!inodes->insert_seg(block->page.frame + TRX_SYS_DOUBLEWRITE +
TRX_SYS_DOUBLEWRITE_FSEG))
err= DB_CORRUPTION;
block->page.unfix();
if (err) return err;
block= buf_pool.page_fix(page_id_t{0, DICT_HDR_PAGE_NO}, &err, nullptr,
buf_pool_t::FIX_WAIT_READ); if (!block) return err;
if (!inodes->insert_seg(block->page.frame + DICT_HDR + DICT_HDR_FSEG_HEADER))
err= DB_CORRUPTION;
block->page.unfix();
if (err) return err;
block= buf_pool.page_fix(page_id_t{0, FSP_IBUF_HEADER_PAGE_NO},
&err, nullptr, buf_pool_t::FIX_WAIT_READ); if (!block) return err; if (!inodes->insert_seg(block->page.frame + PAGE_DATA))
err= DB_CORRUPTION;
block->page.unfix();
/* Get rollback segment header page */ for (ulint rseg_id= 0; rseg_id < TRX_SYS_N_RSEGS && err == DB_SUCCESS;
rseg_id++)
{
trx_rseg_t *rseg= &trx_sys.rseg_array[rseg_id]; if (rseg->space->id == 0)
{
block= buf_pool.page_fix(rseg->page_id(), &err,
nullptr, buf_pool_t::FIX_WAIT_READ); if (!block) break; if (!inodes->insert_seg(block->page.frame + TRX_RSEG +
TRX_RSEG_FSEG_HEADER))
err= DB_CORRUPTION;
block->page.unfix();
}
}
if (err == DB_SUCCESS)
err= fsp_table_inodes(inodes, mtr); return err;
}
/** Free the extents, fragment page from the given inode @parampage_noindexnodepagenumber @paramoffsetindexnodeoffsetwithinpage
@return error code */ static dberr_t fseg_inode_free(uint32_t page_no, uint16_t offset)
{
fil_space_t *space= fil_system.sys_space;
dberr_t err= DB_SUCCESS;
mtr_t mtr{nullptr};
mtr.start();
mtr.x_lock_space(space);
buf_block_t *iblock= buf_page_get_gen(page_id_t{0, page_no}, 0,
RW_X_LATCH, nullptr, BUF_GET,
&mtr, &err);
mtr.start();
mtr.x_lock_space(space);
iblock->page.lock.x_lock();
mtr.memo_push(iblock, MTR_MEMO_PAGE_X_FIX);
} /* These are all leaked undo log segments. That means there is no waytoaccesstheseundologsegmentsotherthantraversing theindexnodepage.Abovefseg_free_step_low()clears
the undo segment header page as well. */
func_exit:
mtr.commit(); return err;
}
/** Free the unused segment
@return error code */
dberr_t inode_info::free_segs()
{ for (auto i : *this)
{
uint32_t page= uint32_t(i >> 32);
uint16_t offset= uint16_t(i); if (dberr_t err= fseg_inode_free(page, offset))
{
sql_print_error("InnoDB: :autoshrink failed to free the " "segment %u in page " UINT32PF, unsigned{offset},
page); return err;
}
sql_print_information("InnoDB: :autoshrink freed the segment " "%u in page " UINT32PF, unsigned{offset}, page);
} return DB_SUCCESS;
}
bool trx_sys_t::is_xa_exist() noexcept
{ for (const trx_rseg_t &rseg : trx_sys.rseg_array)
{ if (rseg.page_no == FIL_NULL) continue; const trx_undo_t *undo= UT_LIST_GET_FIRST(rseg.undo_list); while (undo)
{ if (undo->state == TRX_UNDO_PREPARED) returntrue;
undo= UT_LIST_GET_NEXT(undo_list, undo);
}
} returnfalse;
}
/** Remove the unused segment in tablespace. This function usedonlyduringshrinkingofsystemtablespace @paramshutdowncalledduringslowshutdown
@return error code */
dberr_t fil_space_t::garbage_collect(bool shutdown)
{ if ((shutdown && trx_sys_t::is_xa_exist()) ||
(!shutdown && !trx_sys.is_undo_empty()))
{
sql_print_warning("InnoDB: Cannot free the unused segments" " in system tablespace because a previous" " shutdown was not with innodb_fast_shutdown=0" " or XA PREPARE transactions exist"); return DB_SUCCESS;
}
ut_a(id == 0); /* Collect all the used segment inode entries */
mtr_t mtr{nullptr};
mtr.start();
inode_info used_inodes, unused_inodes;
dberr_t err= fsp_get_sys_used_segment(&used_inodes, &mtr);
DBUG_EXECUTE_IF("unused_undo_free_fail_1", err= DB_CORRUPTION;); if (err)
{
sql_print_error("InnoDB: :autoshrink failed to read the " "used segment due to %s", ut_strerr(err));
mtr.commit(); return err;
}
/* All callers of remove_prepare() does check the FLST_LEN of
the list */
byte *len= list + FLST_LEN;
mtr->write<4>(*base, len, mach_read_from_4(len) - 1);
}
/** Prepare the steps for adding the block into last of the list @parambaseblockwhereliststarts @paramboffsetoffsettofindthelist @paramfree_limitmaximumfreelimitinthetablespace @parammtrmini-transaction @paramlast_block_listlastblockinthelist
@return error code */ static dberr_t append_prepare(const buf_block_t &base, uint16_t boffset,
uint32_t free_limit, mtr_t *mtr,
buf_block_t **last_block_list) noexcept
{
ut_ad(!*last_block_list); if (!flst_get_len(base.page.frame + boffset)) return DB_SUCCESS;
/** Validate the system tablespace list */
__attribute__((warn_unused_result)) static dberr_t fseg_validate(fil_space_t *space,
dict_index_t *index) noexcept
{ /* Validate all FSP list in system tablespace */
mtr_t mtr{nullptr};
mtr.start();
dberr_t err= fseg_validate_low(space, index, &mtr);
mtr.commit(); return err;
}
/** Prepare the associate pages of the current block and modify
the associated pages */ class AssociatedPages final
{
buf_block_t *m_left_block= nullptr;
buf_block_t *m_right_block= nullptr;
buf_block_t *m_parent_block= nullptr;
buf_block_t *const m_cur_block;
mtr_t *const m_mtr;
/** Fetch the left, right and parent page for the respective
current block and make sure that there is no issue exist */
dberr_t prepare(uint32_t parent_page) noexcept
{
uint32_t left_page_no= btr_page_get_prev(m_cur_block->page.frame);
dberr_t err= DB_SUCCESS; if (left_page_no != FIL_NULL)
{
m_left_block= fsp_get_latched_page(page_id_t{0, left_page_no},
m_mtr, &err);
ut_ad(!m_left_block == (err != DB_SUCCESS)); if (!m_left_block) return err;
}
/** Modify the FIL_PAGE_NEXT, FIL_PAGE_PREV, CHILD_PAGE of
respective left, right and parent block to new page number */ void complete(uint32_t new_page_no, uint32_t parent_offset) noexcept
{ if (m_left_block)
m_mtr->write<4>(*m_left_block,
m_left_block->page.frame + FIL_PAGE_NEXT,
new_page_no);
if (m_right_block)
m_mtr->write<4>(*m_right_block,
m_right_block->page.frame + FIL_PAGE_PREV,
new_page_no);
Aboveallscenariodoneby2stepstomakesurethatthere willbenoerrorscenariooncethemodificationofthepages hasstarted. 1)prepare-Basicallyvalidatesthenecessarycondition andmakesurethatpagesarebeinglatched 2)Complete-Completestheactionbyusingthelatched
pages in prepare step */ class PageOperator final
{ /** Header block for the tablespace */
buf_block_t *const m_header_block= nullptr; /** Index node block */
buf_block_t *const m_iblock= nullptr; /** Index node */
fseg_inode_t *const m_inode= nullptr; /** offset of index node within index node page*/
uint16_t m_ioffset= 0; /** Maximum free limit of the tablespace */
uint32_t m_free_limit= 0; /** Segment id */
uint64_t m_seg_id= 0; /** Extent size */
uint32_t m_extent_size= 0;
/** New block to be allocated */
buf_block_t *m_new_block= nullptr; /** New block extent descriptor */
buf_block_t *m_new_xdes= nullptr; /** New block descriptor */
xdes_t *m_new_descr= nullptr; /** New block descriptor offset within xdes page */
uint16_t m_xoffset= 0; /** New extent descriptor state */
uint32_t m_new_state= 0; /** Need segment allocation */ bool m_need_segment= false; /** Old pages during allocation to be saved */
buf_block_t *m_old_pages[8]= {nullptr}; /** Page to be removed */
byte m_old_page_no[4]= {0}; /** Old block extent descriptor page */
buf_block_t *m_old_xdes= nullptr; /** Old block descriptor */
xdes_t *m_old_descr= nullptr; /** Old block descriptor offset with descriptor page */
uint16_t m_old_xoffset= 0; /** Old descriptor state */
uint32_t m_old_state= 0; /** Mini-transaction to allocate & free a page */
mtr_t *const m_mtr;
/** Save the old page state of the block before allocatingapage @paramblockblocktobestored
@return error code */
dberr_t save_old_page(buf_block_t *block) noexcept
{ if (!block) return DB_SUCCESS;
size_t first_free; for (first_free= 0; first_free < array_elements(m_old_pages); first_free++)
{ const buf_block_t *b= m_old_pages[first_free]; if (!b) goto found; if (b->page.hash == &block->page) return DB_SUCCESS;
} return DB_CORRUPTION;
found:
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);
m_old_pages[first_free]= old;
old->page.hash= &block->page; return DB_SUCCESS;
}
/** Prepare the steps for free extent allocation by validating FLST_PREV,FLST_NEXTofchoosenextentdescriptor andtheirFLST_LENofFSP_FREElistinFSP_HEADER_PAGE.
@return error code or DB_SUCCESS */
dberr_t free_extent_prepare() noexcept
{ /* At least there should be 1 element in FSP_FREE list */
byte *len=
&m_header_block->page.frame[FSP_HEADER_OFFSET + FSP_FREE +
FLST_LEN]; if (mach_read_from_4(len) == 0) return DB_CORRUPTION;
/** Prepare the steps to do the following 1)freeextentallocation 2)AddtheextenttoFSEG_NOT_FULLlistbyvalidatingthe lastextentdescriptorinFSEG_NOT_FULLlistofsegmentinode
@return error code */
dberr_t initialize_segment_prepare() noexcept
{
dberr_t err= free_extent_prepare(); if (err) return err;
/** This function does the following 1)Allocatingthefreeextent 2)AppendingtheextenttoFSEG_NOT_FULLlistinsegmentinode
3) Mark the extent state as XDES_FSEG */ void initialize_segment_complete() noexcept
{
free_extent_complete();
flst::append_complete(m_iblock,
uint16_t(m_ioffset + FSEG_NOT_FULL),
m_new_xdes, m_xoffset, m_mtr);
if (err == DB_SUCCESS)
err= save_old_page(fsp_free_frag_last); return err;
}
/** This function does the following 1)Allocatingthefreeextent 2)AppendingtheextenttoFSP_FREE_FRAGlistinFSP_HEADERpage
3) Mark the extent state as XDES_FREE_FRAG */ void initialize_free_frag_complete() noexcept
{
free_extent_complete();
flst::append_complete(m_header_block,
FSP_HEADER_OFFSET + FSP_FREE_FRAG,
m_new_xdes, m_xoffset, m_mtr);
/** Does the following 1)Completethepageallocationfromfilesegment. 2)IftheextentsizeisFSP_EXTENT_SIZEthen i)RemovetheextentfromFSEG_NOT_FULLlist
ii) Add the extent to FSEG_FULL */ void alloc_from_fseg_complete() noexcept
{
xdes_set_free<false>(*m_new_xdes, m_new_descr,
m_new_block->page.id().page_no() % m_extent_size,
m_mtr);
if (err == DB_SUCCESS)
{
err= save_old_page(fsp_free_frag_prev); if (err == DB_SUCCESS)
err= save_old_page(fsp_free_frag_next); if (err == DB_SUCCESS)
err= save_old_page(fsp_full_frag_last);
} return err;
}
/** Does the following 1)Allocatethepagefromfragmentextent 2)IftheextentsizeisFSP_EXTENT_SIZEthen i)removetheextentdescriptorfromFSP_FREE_FRAGlist
ii) Add the extent descriptor in FSP_FULL_FRAG list */ void alloc_from_free_frag_complete() noexcept
{
xdes_set_free<false>(*m_new_xdes, m_new_descr,
m_new_block->page.id().page_no() % m_extent_size,
m_mtr);
/* Allocate the new extent and initialize the extent state
with XDES_FSEG/XDES_FREE_FRAG */ if (m_new_state == XDES_FREE)
{ if (segment) err= initialize_segment_prepare(); else err= initialize_free_frag_prepare();
if (err) return err;
new_page:
new_page= xdes_find_free(m_new_descr); if (new_page == FIL_NULL) return DB_CORRUPTION;
new_page+= new_extent;
m_new_block= fsp_page_create(fil_system.sys_space, new_page, m_mtr);
err= save_old_page(m_header_block); if (err == DB_SUCCESS)
err= save_old_page(m_iblock); if (err == DB_SUCCESS)
err= save_old_page(m_new_xdes); if (err == DB_SUCCESS)
err= save_old_page(m_new_block); return err;
}
if (n_used != m_extent_size - 1); /* After allocating the page from extent, it doesn't get
full. There will be no change in other pages */ elseif (m_new_state == XDES_FSEG && m_seg_id != FIL_NULL &&
mach_read_from_8(m_new_descr + XDES_ID) == m_seg_id)
err= alloc_from_fseg_prepare(); elseif (m_new_state == XDES_FREE_FRAG || m_new_state == XDES_FULL_FRAG)
err= alloc_from_free_frag_prepare(); else err= DB_CORRUPTION;
if (err) return err; goto new_page;
}
/** Complete the page allocation from FREE extent descriptor
or XDES_FSEG/XDES_FREE_FRAG extent list */ void complete_new_page() noexcept
{ if (m_new_state == XDES_FREE)
{ if (m_need_segment) return initialize_segment_complete(); return initialize_free_frag_complete();
} if (m_new_state == XDES_FSEG) return alloc_from_fseg_complete(); return alloc_from_free_frag_complete();
}
/** Assign the fragment slot of the index node. Thisstepshouldbedoneafterremovingtheoldpage becausethereisapossiblitythatFRAGMENTARRAY
could be full. */ void assign_frag_slot() noexcept
{ if ((!m_need_segment && m_new_state == XDES_FREE) ||
m_new_state == XDES_FULL_FRAG ||
m_new_state == XDES_FREE_FRAG)
fseg_set_nth_frag_page_no(m_inode, m_iblock,
fseg_find_free_frag_page_slot(m_inode),
m_new_block->page.id().page_no(), m_mtr);
}
/** Restore the page modified during page allocation */ void restore_old_pages() noexcept
{ for (buf_block_t *old : m_old_pages) if (old)
memcpy_aligned<UNIV_PAGE_SIZE_MIN>(
old->page.hash->frame, old->page.frame, srv_page_size);
}
/** Prepare the steps to remove the page from file segment (or)fragmentextent.
@return error code */
dberr_t prepare_old_page() noexcept
{
uint32_t old_page_no= mach_read_from_4(m_old_page_no);
uint32_t old_descr_page_no=
xdes_calc_descriptor_page(0, old_page_no);
dberr_t err= DB_SUCCESS;
m_old_xdes= fsp_get_latched_page(page_id_t{0, old_descr_page_no},
m_mtr, &err); if (!m_old_xdes) return err;
class IndexDefragmenter final
{ /** Parent block and its associate offset where westorethechildpagenumber.Thisisstored
in the form of <child_page_no, parent_page_no + parent_offset> */
std::unordered_map<uint32_t, uint64_t> m_parent_pages;
dict_index_t &m_index;
buf_block_t *m_root; /** Iterate through the page and map the child_page_no withtheparentpageandtheirassociateoffset inm_parent_pages
@param block block to be traversed */
dberr_t get_child_pages(buf_block_t *block) noexcept;
/** Get the first block for the given level @paramlevellevel @parammtrmini-transaction @paramcur_page_nofirstpagenumberforthegivenlevel
@return error code or DB_SUCCESS */
dberr_t get_level_block(uint16_t level, mtr_t *mtr,
uint32_t *cur_page_no) noexcept;
/** Defragment the level of the index @paramlevelleveltobedefragmented @parammtrmini-transaction @paramspace_defragspacedefragmenterinformation andalsoresponsibleforallocatingnew segmentorpagefromtablespace
@return error code or DB_SUCCESS */
dberr_t defragment_level(uint16_t level, mtr_t *mtr,
SpaceDefragmenter *space_defrag) noexcept;
/** Defragment the index with the help of space defragmenter. 1)Iteratethrougheachleveloftheindex 2)Findoutwhatarethepages/segment tobemodifiedfortheindex. 3)Allocatethepagefromthenewsegment/extent 4)Copythetobechangedpagecontenttonewpage 5)Changetheassociativepagesinthetreewith newpage(left,right,parentblock) 6)Dostep(4),(5)withinsinglemini-transaction andcommitthemini-transaction
@return error code or DB_SUCCESS */
dberr_t defragment(SpaceDefragmenter *space_defrag) noexcept;
};
class SpaceDefragmenter final
{ /** Extent is already allocated for defragmentation */ static constexpr uint32_t XDES_USED= ~0U; /** Store the extent information in the tablespace <extent, state>*/
std::map<uint32_t, uint32_t> m_extent_info; /** Map of last used extent with early unused extent within
the tablespace */
std::map<uint32_t, uint32_t> m_extent_map;
/** Find the earlier free extent for the given used extent @parammax_limitFindtheextentbelowmaxlimitextent @returnvalue
@retval FIL_NULL if there is no extent */
uint32_t find_free_extent(uint32_t max_limit) noexcept
{ for (auto &extent_info : m_extent_info)
{ if (max_limit <= extent_info.first) return FIL_NULL;
if (extent_info.second == XDES_FREE)
{ /* Mark the extent as used one */
extent_info.second = XDES_USED; return extent_info.first;
}
} return FIL_NULL;
}
/** A B-tree root page cannot be relocated: its page number isrecordedinSYS_INDEXESandthedictionarycache,andit storestheFSEGsegmentheaders(PAGE_BTR_SEG_LEAFand PAGE_BTR_SEG_TOP).Thesystemtablespacecannotshrinkbelow thehighestrootpage,andrelocatinganypageatorbelow itwouldnotreducethetablespacesize.
@return the highest extent that holds such a root page, or 0 */
uint32_t max_root_extent() noexcept
{
uint32_t max_extent= 0; for (dict_table_t *table :
{dict_sys.sys_tables, dict_sys.sys_columns, dict_sys.sys_indexes,
dict_sys.sys_fields, dict_sys.sys_foreign,
dict_sys.sys_foreign_cols, dict_sys.sys_virtual})
{ if (!table) continue; for (dict_index_t *index= dict_table_get_first_index(table);
index; index= dict_table_get_next_index(index)) if (index->page != FIL_NULL)
max_extent= std::max(max_extent,
(index->page / m_extent_size) * m_extent_size);
} return max_extent;
}
/** Find the new extent for the existing last used extent Iteratethetablespacefromlastandfindoutthefree
extent in the beginning of the tablespace */
dberr_t find_new_extents() noexcept
{
dberr_t err= extract_extent_state(); if (err) return err;
uint32_t free_limit= fil_system.sys_space->free_limit;
uint32_t fixed_size= srv_sys_space.get_min_size(); /* A root page cannot be relocated, so the tablespace cannot shrinkbelowthehighestroot.Nevermapanextentator
below that floor */
uint32_t floor= std::max(fixed_size, max_root_extent()); while (free_limit > floor)
{
uint32_t state= m_extent_info[free_limit];
switch (state) { case XDES_USED: goto func_exit; case XDES_FREE: goto prev_extent; case XDES_FSEG: case XDES_FULL_FRAG: case XDES_FREE_FRAG:
uint32_t dest= find_free_extent(free_limit); if (dest == FIL_NULL) goto func_exit;
m_extent_map[free_limit]= dest; break;
}
prev_extent:
free_limit-= FSP_EXTENT_SIZE;
}
func_exit: if (m_extent_map.empty()) return DB_SUCCESS_LOCKED_REC;
sql_print_information("InnoDB: System tablespace defragmentation " "process starts");
if (m_extent_map.size() == 1)
{ auto it= m_extent_map.begin();
sql_print_information("InnoDB: Moving the data from extent " "%" PRIu32 " to extent %" PRIu32, it->first,
it->second);
} else
{
sql_print_information("InnoDB: Moving the data from extents " "%" PRIu32 " through %" PRIu32,
m_extent_map.begin()->first,
m_extent_map.rbegin()->first);
/** Defragment the system tables */
dberr_t defragment_system_tables() noexcept
{
dberr_t err= defragment_table(dict_sys.sys_tables); if (err == DB_SUCCESS)
err= defragment_table(dict_sys.sys_columns); if (err == DB_SUCCESS)
err= defragment_table(dict_sys.sys_indexes); if (err == DB_SUCCESS)
err= defragment_table(dict_sys.sys_fields); if (err == DB_SUCCESS)
err= defragment_table(dict_sys.sys_foreign); if (err == DB_SUCCESS)
err= defragment_table(dict_sys.sys_foreign_cols); if (err == DB_SUCCESS)
err= defragment_table(dict_sys.sys_virtual);
if (err == DB_SUCCESS)
sql_print_information("InnoDB: Defragmentation of system " "tablespace is successful"); return err;
}
/** @return extent which replaces the later extent
or same extent if there is no replacement exist */
uint32_t get_new_extent(uint32_t old_extent) const noexcept
{ auto it= m_extent_map.find(old_extent); if (it != m_extent_map.end()) return it->second; return old_extent;
}
/** @return state for the given extent */
uint32_t get_state(uint32_t extent) noexcept
{ return m_extent_info[extent];
}
};
uint32_t new_extent= space_defrag->get_new_extent(cur_extent); /* The root page has no parent node pointer cannot be relocated. find_new_extents()nevermapsanextentatorbelowthehighest
root, so a root is never scheduled for relocation. */
ut_ad(cur_page_no != m_index.page || new_extent == cur_extent);
/* There is no need for extent to be changed */ if (new_extent == cur_extent)
{ if (level)
{ /* Store the child page number and their offset
exist in the parent block records */
err= get_child_pages(block); if (err) return err;
} goto fetch_next_page;
}
buf_block_t *header_block=
fsp_get_latched_page(page_id_t{0, 0}, mtr, &err); if (!header_block) return err;
/* After allocating the new page, try to prepare the steps ofpageremovalfunction.Becausethereisapossiblitythat lastblockinFSEG_NOT_FULL/FSP_FREE_FRAG/FSP_FREElastblock
could've changed while allocating the new block. */
err= operation.prepare_old_page();
DBUG_EXECUTE_IF("remover_prepare_fail", err= DB_CORRUPTION;); if (err) goto err_exit;
/* Copy the data from old block to new block */
buf_block_t *new_block= operation.get_new_block();
uint32_t new_page_no= new_block->page.id().page_no(); /* Copy FIL_PAGE_PREV, FIL_PAGE_NEXT */
mtr->memcpy<mtr_t::MAYBE_NOP>(*new_block,
new_block->page.frame + FIL_PAGE_PREV,
block->page.frame + FIL_PAGE_PREV,
page_has_next(block->page.frame) ? 8 : 4);
mtr->memcpy(*new_block, new_block->page.frame + FIL_PAGE_TYPE,
block->page.frame + FIL_PAGE_TYPE,
srv_page_size - FIL_PAGE_TYPE - 8);
if (level)
{
err= get_child_pages(new_block); if (err) goto err_exit;
}
/* Assign the new block page number in left, right
and parent block */
related_pages.complete(new_page_no, parent_offset);
/* Complete the page free operation */
operation.complete_free_old_page(); /* Add the new page in inode fragment array */
operation.assign_frag_slot();
field= rec_get_nth_field_old(rec, field_no, &len); if (len != 8) goto corrupt;
table_id= mach_read_from_8(field); if (dict_sys.is_sys_table(table_id)) continue;
if (scan_indexes)
err= callback(table_id, {}); else
{ /* For tables, get the name */
field= rec_get_nth_field_old(rec, DICT_FLD__SYS_TABLES__NAME, &len); if (len == UNIV_SQL_NULL || len == 0) goto corrupt;
/** Identify the legacy tables in the system tablespace anddeletethetableidfrominnodbsystemtables.Atableis consideredalegacy/unknowntableifthetablenamedoes notcontain'/'(indicatingit'snotaproperdatabase/tablename). AlsodeletetheorphanedindexesfromSYS_INDEXESinsystem tablespace. @retvalDB_SUCCESS_LOCKED_RECiflegacytableexists
@return error code or DB_SUCCESS */ static dberr_t drop_all_orphaned_tables()
{ /* SELECT table_id,to_drop FROM SYS_TABLES WHERE SPACE=0 AND ... */
std::unordered_map<table_id_t,bool> system_tables;
/* Step 1: Scan SYS_TABLES for legacy tables */
dberr_t err= scan_system_tablespace_metadata(dict_sys.sys_tables,
[&](table_id_t table_id, st_::span<constchar> name) -> dberr_t
{ constbool to_drop{!memchr(name.data(), '/', name.size())}; if (to_drop)
sql_print_information("InnoDB: Found an unknown table %.*s", int(name.size()), name.data());
system_tables.emplace(table_id, to_drop); return DB_SUCCESS;
});
if (err != DB_SUCCESS) return err;
/* Step 2: Scan SYS_INDEXES for orphaned indexes not in orphaned_tbl */
err= scan_system_tablespace_metadata(dict_sys.sys_indexes,
[&](table_id_t table_id, st_::span<constchar>) -> dberr_t
{ if (!system_tables.emplace(table_id, true).second)
sql_print_information("InnoDB: Found orphaned index for table_id "
UINT64PF, table_id); return DB_SUCCESS;
});
if (err != DB_SUCCESS) return err;
size_t count{0}; for (constauto &td : system_tables)
count+= td.second; if (!count) return DB_SUCCESS;
/* Check whether any user table exists in system tablespace */
err= scan_system_tablespace_metadata(dict_sys.sys_tables,
[](table_id_t, st_::span<constchar> name) -> dberr_t
{
sql_print_information( "InnoDB: Found unexpected table in system tablespace: %.*s", int(name.size()), name.data()); return DB_SUCCESS_LOCKED_REC;
});
if (err == DB_SUCCESS_LOCKED_REC)
{
sql_print_information( "InnoDB: Unexpected table exists in the system tablespace." "Please try to move the data from system tablespace " "to separate tablespace before defragment the " "system tablespace."); return DB_SUCCESS;
} elseif (err) { return err; }
SpaceDefragmenter defragmenter;
err= defragmenter.find_new_extents(); /* There is no free extent exist */ if (err == DB_SUCCESS_LOCKED_REC) return DB_SUCCESS;
if (err == DB_SUCCESS)
err= defragmenter.defragment_system_tables(); return err;
}
if (!shutdown)
{
err= drop_all_orphaned_tables(); if (err == DB_SUCCESS_LOCKED_REC)
{ /* Skip defragmentation when orphaned tables are delete-marked. Bothdefragment()andpurgewouldattempttodroplegacytables,
causing double-free of segments. Proceed to shrinking only. */
sql_print_information("InnoDB: Defragmentation before " "autoshrink was skipped due to orphaned " "table removal.");
err= DB_SUCCESS;
} elseif (err == DB_SUCCESS)
err= space->defragment();
if (err)
{
srv_sys_space.set_shrink_fail(); return;
}
}
mtr_t mtr{nullptr};
mtr.start();
mtr.x_lock_space(space);
err= fsp_traverse_extents(space, &last_used_extent, &mtr);
DBUG_EXECUTE_IF("traversal_extent_fail", err= DB_CORRUPTION;); if (err != DB_SUCCESS)
{
err_exit:
mtr.commit();
sql_print_warning("InnoDB: Cannot shrink the system tablespace " "due to %s", ut_strerr(err));
srv_sys_space.set_shrink_fail(); 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 usedoublewritebufferanddisabledblwrandthereshould
be enough space in redo log */
log_make_checkpoint();
fil_system.set_use_doublewrite(false);
{ /* 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_page(
page_id_t(space->id, 0), &mtr, &err); if (!header) goto err_exit;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.