Commit a81aec15 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-25491 preparation: Clean up tablespace destruction

fil_check_pending_ops(), fil_check_pending_io(): Remove.
These functions were actually duplicating each other ever since
commit 118e258a (MDEV-23855).

fil_space_t::check_pending_operations(): Replaces
fil_check_pending_operations() and incorporates the logic of
fil_check_pending_ops(). Avoid unnecessary lookups for the tablespace.
Just wait for the reference count to drop to zero.

fil_space_t::io(): Remove an unnecessary condition. We can (and
probably better should) refuse asynchronous reads of undo tablespaces
that are being truncated.

fil_truncate_prepare(): Remove.

trx_purge_truncate_history(): Implement the necessary steps that used
to be in fil_truncate_prepare().
parent b3d963fe
...@@ -1561,149 +1561,43 @@ fil_name_write( ...@@ -1561,149 +1561,43 @@ fil_name_write(
mtr->log_file_op(FILE_MODIFY, space_id, name); mtr->log_file_op(FILE_MODIFY, space_id, name);
} }
/** Check for pending operations. fil_space_t *fil_space_t::check_pending_operations(ulint id)
@param[in] space tablespace
@param[in] count number of attempts so far
@return 0 if no operations else count + 1. */
static ulint fil_check_pending_ops(const fil_space_t* space, ulint count)
{
mysql_mutex_assert_owner(&fil_system.mutex);
if (!space) {
return 0;
}
if (auto n_pending_ops = space->referenced()) {
/* Give a warning every 10 second, starting after 1 second */
if ((count % 500) == 50) {
ib::warn() << "Trying to delete"
" tablespace '" << space->chain.start->name
<< "' but there are " << n_pending_ops
<< " pending operations on it.";
}
return(count + 1);
}
return(0);
}
/*******************************************************************//**
Check for pending IO.
@return 0 if no pending else count + 1. */
static
ulint
fil_check_pending_io(
/*=================*/
fil_space_t* space, /*!< in/out: Tablespace to check */
fil_node_t** node, /*!< out: Node in space list */
ulint count) /*!< in: number of attempts so far */
{ {
mysql_mutex_assert_owner(&fil_system.mutex); ut_a(!is_system_tablespace(id));
mysql_mutex_lock(&fil_system.mutex);
/* The following code must change when InnoDB supports fil_space_t *space= fil_space_get_by_id(id);
multiple datafiles per tablespace. */
ut_ad(UT_LIST_GET_LEN(space->chain) == 1);
*node = UT_LIST_GET_FIRST(space->chain);
if (const uint32_t p = space->referenced()) {
ut_a(!(*node)->being_extended);
/* Give a warning every 10 second, starting after 1 second */
if ((count % 500) == 50) {
ib::info() << "Trying to delete"
" tablespace '" << space->chain.start->name
<< "' but there are " << p
<< " pending i/o's on it.";
}
return(count + 1);
}
return(0);
}
/*******************************************************************//**
Check pending operations on a tablespace.
@return tablespace */
static
fil_space_t*
fil_check_pending_operations(
/*=========================*/
ulint id, /*!< in: space id */
bool truncate, /*!< in: whether to truncate a file */
char** path) /*!< out/own: tablespace path */
{
ulint count = 0;
ut_a(!is_system_tablespace(id));
mysql_mutex_lock(&fil_system.mutex);
fil_space_t* sp = fil_space_get_by_id(id);
if (sp) {
sp->set_stopping(true);
if (sp->crypt_data) {
sp->reacquire();
mysql_mutex_unlock(&fil_system.mutex);
fil_space_crypt_close_tablespace(sp);
mysql_mutex_lock(&fil_system.mutex);
sp->release();
}
}
/* Check for pending operations. */
do {
count = fil_check_pending_ops(sp, count);
mysql_mutex_unlock(&fil_system.mutex);
if (count) {
std::this_thread::sleep_for(
std::chrono::milliseconds(20));
} else if (!sp) {
return nullptr;
}
mysql_mutex_lock(&fil_system.mutex);
sp = fil_space_get_by_id(id);
} while (count);
/* Check for pending IO. */
for (;;) {
if (truncate) {
sp->is_being_truncated = true;
}
fil_node_t* node;
count = fil_check_pending_io(sp, &node, count);
if (count == 0 && path) {
*path = mem_strdup(node->name);
}
mysql_mutex_unlock(&fil_system.mutex);
if (count == 0) { if (space)
break; {
} const uint32_t n= space->acquire_low();
ut_ad(!(n & STOPPING));
std::this_thread::sleep_for(std::chrono::milliseconds(20)); if (space->crypt_data)
mysql_mutex_lock(&fil_system.mutex); {
sp = fil_space_get_by_id(id); mysql_mutex_unlock(&fil_system.mutex);
fil_space_crypt_close_tablespace(space);
mysql_mutex_lock(&fil_system.mutex);
}
space->set_stopping(true);
space->release();
}
mysql_mutex_unlock(&fil_system.mutex);
if (!sp) { if (!space)
mysql_mutex_unlock(&fil_system.mutex); return nullptr;
break;
}
}
return sp; for (ulint count= 0;; count++)
{
auto pending= space->referenced();
if (!pending)
return space;
/* Give a warning every 10 second, starting after 1 second */
if ((count % 500) == 50)
ib::warn() << "Trying to delete tablespace '"
<< space->chain.start->name << "' but there are "
<< pending << " pending operations on it.";
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
} }
/** Close a single-table tablespace on failed IMPORT TABLESPACE. /** Close a single-table tablespace on failed IMPORT TABLESPACE.
...@@ -1712,8 +1606,7 @@ Free all pages used by the tablespace. */ ...@@ -1712,8 +1606,7 @@ Free all pages used by the tablespace. */
void fil_close_tablespace(ulint id) void fil_close_tablespace(ulint id)
{ {
ut_ad(!is_system_tablespace(id)); ut_ad(!is_system_tablespace(id));
char* path = nullptr; fil_space_t* space = fil_space_t::check_pending_operations(id);
fil_space_t* space = fil_check_pending_operations(id, false, &path);
if (!space) { if (!space) {
return; return;
} }
...@@ -1730,23 +1623,22 @@ void fil_close_tablespace(ulint id) ...@@ -1730,23 +1623,22 @@ void fil_close_tablespace(ulint id)
os_aio_wait_until_no_pending_writes(); os_aio_wait_until_no_pending_writes();
ut_ad(space->is_stopping()); ut_ad(space->is_stopping());
/* If the free is successful, the wrlock will be released before
the space memory data structure is freed. */
if (!fil_space_free(id, true)) {
space->x_unlock();
}
/* If it is a delete then also delete any generated files, otherwise /* If it is a delete then also delete any generated files, otherwise
when we drop the database the remove directory will fail. */ when we drop the database the remove directory will fail. */
if (char* cfg_name = fil_make_filepath(path, fil_space_t::name_type{}, if (char* cfg_name = fil_make_filepath(space->chain.start->name,
fil_space_t::name_type{},
CFG, false)) { CFG, false)) {
os_file_delete_if_exists(innodb_data_file_key, cfg_name, NULL); os_file_delete_if_exists(innodb_data_file_key, cfg_name, NULL);
ut_free(cfg_name); ut_free(cfg_name);
} }
ut_free(path); /* If the free is successful, the wrlock will be released before
the space memory data structure is freed. */
if (!fil_space_free(id, true)) {
space->x_unlock();
}
} }
/** Delete a tablespace and associated .ibd file. /** Delete a tablespace and associated .ibd file.
...@@ -1757,12 +1649,11 @@ void fil_close_tablespace(ulint id) ...@@ -1757,12 +1649,11 @@ void fil_close_tablespace(ulint id)
dberr_t fil_delete_tablespace(ulint id, bool if_exists, dberr_t fil_delete_tablespace(ulint id, bool if_exists,
std::vector<pfs_os_file_t>* detached_handles) std::vector<pfs_os_file_t>* detached_handles)
{ {
char* path = NULL;
ut_ad(!is_system_tablespace(id)); ut_ad(!is_system_tablespace(id));
ut_ad(!detached_handles || detached_handles->empty()); ut_ad(!detached_handles || detached_handles->empty());
dberr_t err; dberr_t err;
fil_space_t *space = fil_check_pending_operations(id, false, &path); fil_space_t *space = fil_space_t::check_pending_operations(id);
if (!space) { if (!space) {
err = DB_TABLESPACE_NOT_FOUND; err = DB_TABLESPACE_NOT_FOUND;
...@@ -1771,8 +1662,9 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists, ...@@ -1771,8 +1662,9 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists,
<< " because it is not found" << " because it is not found"
" in the tablespace memory cache."; " in the tablespace memory cache.";
} }
func_exit:
goto func_exit; ibuf_delete_for_discarded_space(id);
return err;
} }
/* IMPORTANT: Because we have set space::stop_new_ops there /* IMPORTANT: Because we have set space::stop_new_ops there
...@@ -1808,7 +1700,7 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists, ...@@ -1808,7 +1700,7 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists,
mtr_t mtr; mtr_t mtr;
mtr.start(); mtr.start();
mtr.log_file_op(FILE_DELETE, id, path); mtr.log_file_op(FILE_DELETE, id, space->chain.start->name);
mtr.commit(); mtr.commit();
/* Even if we got killed shortly after deleting the /* Even if we got killed shortly after deleting the
tablespace file, the record must have already been tablespace file, the record must have already been
...@@ -1816,7 +1708,8 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists, ...@@ -1816,7 +1708,8 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists,
log_write_up_to(mtr.commit_lsn(), true); log_write_up_to(mtr.commit_lsn(), true);
if (char* cfg_name = fil_make_filepath( if (char* cfg_name = fil_make_filepath(
path, fil_space_t::name_type{}, CFG, false)) { space->chain.start->name,
fil_space_t::name_type{}, CFG, false)) {
os_file_delete_if_exists(innodb_data_file_key, os_file_delete_if_exists(innodb_data_file_key,
cfg_name, nullptr); cfg_name, nullptr);
ut_free(cfg_name); ut_free(cfg_name);
...@@ -1832,54 +1725,34 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists, ...@@ -1832,54 +1725,34 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists,
/* Double check the sanity of pending ops after reacquiring /* Double check the sanity of pending ops after reacquiring
the fil_system::mutex. */ the fil_system::mutex. */
if (const fil_space_t* s = fil_space_get_by_id(id)) { ut_a(space == fil_space_get_by_id(id));
ut_a(s == space); ut_a(!space->referenced());
ut_a(!space->referenced()); ut_a(UT_LIST_GET_LEN(space->chain) == 1);
ut_a(UT_LIST_GET_LEN(space->chain) == 1); auto handles = fil_system.detach(space, detached_handles != nullptr);
auto handles = fil_system.detach(space, if (detached_handles) {
detached_handles != nullptr); *detached_handles = std::move(handles);
if (detached_handles) { }
*detached_handles = std::move(handles); mysql_mutex_unlock(&fil_system.mutex);
}
mysql_mutex_unlock(&fil_system.mutex);
mysql_mutex_lock(&log_sys.mutex);
if (space->max_lsn != 0) {
ut_d(space->max_lsn = 0);
fil_system.named_spaces.remove(*space);
}
mysql_mutex_unlock(&log_sys.mutex); mysql_mutex_lock(&log_sys.mutex);
fil_space_free_low(space);
if (!os_file_delete(innodb_data_file_key, path) if (space->max_lsn != 0) {
&& !os_file_delete_if_exists( ut_d(space->max_lsn = 0);
innodb_data_file_key, path, NULL)) { fil_system.named_spaces.remove(*space);
}
/* Note: This is because we have removed the mysql_mutex_unlock(&log_sys.mutex);
tablespace instance from the cache. */
err = DB_IO_ERROR; if (!os_file_delete(innodb_data_file_key, space->chain.start->name)
} && !os_file_delete_if_exists(innodb_data_file_key,
} else { space->chain.start->name, NULL)) {
mysql_mutex_unlock(&fil_system.mutex); /* Note: This is because we have removed the
err = DB_TABLESPACE_NOT_FOUND; tablespace instance from the cache. */
err = DB_IO_ERROR;
} }
func_exit: fil_space_free_low(space);
ut_free(path); goto func_exit;
ibuf_delete_for_discarded_space(id);
return(err);
}
/** Prepare to truncate an undo tablespace.
@param[in] space_id undo tablespace id
@return the tablespace
@retval NULL if tablespace not found */
fil_space_t *fil_truncate_prepare(ulint space_id)
{
return fil_check_pending_operations(space_id, true, nullptr);
} }
/*******************************************************************//** /*******************************************************************//**
...@@ -3090,8 +2963,7 @@ fil_io_t fil_space_t::io(const IORequest &type, os_offset_t offset, size_t len, ...@@ -3090,8 +2963,7 @@ fil_io_t fil_space_t::io(const IORequest &type, os_offset_t offset, size_t len,
fil_node_t* node= UT_LIST_GET_FIRST(chain); fil_node_t* node= UT_LIST_GET_FIRST(chain);
ut_ad(node); ut_ad(node);
if (type.type == IORequest::READ_ASYNC && is_stopping() if (type.type == IORequest::READ_ASYNC && is_stopping()) {
&& !is_being_truncated) {
release(); release();
return {DB_TABLESPACE_DELETED, nullptr}; return {DB_TABLESPACE_DELETED, nullptr};
} }
......
...@@ -517,6 +517,12 @@ struct fil_space_t final ...@@ -517,6 +517,12 @@ struct fil_space_t final
/** Note that operations on the tablespace must stop or can resume */ /** Note that operations on the tablespace must stop or can resume */
inline void set_stopping(bool stopping); inline void set_stopping(bool stopping);
/** Look up the tablespace and wait for pending operations to cease
@param id tablespace identifier
@return tablespace
@retval nullptr if no tablespace was found */
static fil_space_t *check_pending_operations(ulint id);
private: private:
MY_ATTRIBUTE((warn_unused_result)) MY_ATTRIBUTE((warn_unused_result))
/** Try to acquire a tablespace reference. /** Try to acquire a tablespace reference.
...@@ -1580,12 +1586,6 @@ dberr_t ...@@ -1580,12 +1586,6 @@ dberr_t
fil_delete_tablespace(ulint id, bool if_exists= false, fil_delete_tablespace(ulint id, bool if_exists= false,
std::vector<pfs_os_file_t> *detached_handles= nullptr); std::vector<pfs_os_file_t> *detached_handles= nullptr);
/** Prepare to truncate an undo tablespace.
@param[in] space_id undo tablespace id
@return the tablespace
@retval NULL if the tablespace does not exist */
fil_space_t* fil_truncate_prepare(ulint space_id);
/** Close a single-table tablespace on failed IMPORT TABLESPACE. /** Close a single-table tablespace on failed IMPORT TABLESPACE.
The tablespace must be cached in the memory cache. The tablespace must be cached in the memory cache.
Free all pages used by the tablespace. */ Free all pages used by the tablespace. */
......
...@@ -605,7 +605,7 @@ static void trx_purge_truncate_history() ...@@ -605,7 +605,7 @@ static void trx_purge_truncate_history()
return; return;
} }
const fil_space_t& space = *purge_sys.truncate.current; fil_space_t& space = *purge_sys.truncate.current;
/* Undo tablespace always are a single file. */ /* Undo tablespace always are a single file. */
ut_a(UT_LIST_GET_LEN(space.chain) == 1); ut_a(UT_LIST_GET_LEN(space.chain) == 1);
fil_node_t* file = UT_LIST_GET_FIRST(space.chain); fil_node_t* file = UT_LIST_GET_FIRST(space.chain);
...@@ -685,26 +685,47 @@ static void trx_purge_truncate_history() ...@@ -685,26 +685,47 @@ static void trx_purge_truncate_history()
log_free_check(); log_free_check();
/* Adjust the tablespace metadata. */
if (!fil_truncate_prepare(space.id)) {
ib::error() << "Failed to find UNDO tablespace "
<< file->name;
return;
}
/* Re-initialize tablespace, in a single mini-transaction. */ /* Re-initialize tablespace, in a single mini-transaction. */
mtr_t mtr; mtr_t mtr;
const ulint size = SRV_UNDO_TABLESPACE_SIZE_IN_PAGES; const ulint size = SRV_UNDO_TABLESPACE_SIZE_IN_PAGES;
mtr.start(); mtr.start();
mtr.x_lock_space(purge_sys.truncate.current); mtr.x_lock_space(&space);
/* Adjust the tablespace metadata. */
mysql_mutex_lock(&fil_system.mutex);
space.set_stopping(true);
space.is_being_truncated = true;
if (space.crypt_data) {
space.reacquire();
mysql_mutex_unlock(&fil_system.mutex);
fil_space_crypt_close_tablespace(&space);
space.release();
} else {
mysql_mutex_unlock(&fil_system.mutex);
}
uint i = 60;
while (space.referenced()) {
if (!--i) {
mtr.commit();
ib::error() << "Failed to freeze"
" UNDO tablespace "
<< file->name;
return;
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
/* Associate the undo tablespace with mtr. /* Associate the undo tablespace with mtr.
During mtr::commit(), InnoDB can use the undo During mtr::commit(), InnoDB can use the undo
tablespace object to clear all freed ranges */ tablespace object to clear all freed ranges */
mtr.set_named_space(purge_sys.truncate.current); mtr.set_named_space(&space);
mtr.trim_pages(page_id_t(space.id, size)); mtr.trim_pages(page_id_t(space.id, size));
fsp_header_init(purge_sys.truncate.current, size, &mtr); fsp_header_init(&space, size, &mtr);
mysql_mutex_lock(&fil_system.mutex); mysql_mutex_lock(&fil_system.mutex);
purge_sys.truncate.current->size = file->size = size; space.size = file->size = size;
mysql_mutex_unlock(&fil_system.mutex); mysql_mutex_unlock(&fil_system.mutex);
buf_block_t* sys_header = trx_sysf_get(&mtr); buf_block_t* sys_header = trx_sysf_get(&mtr);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment