summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2021-04-23 16:58:25 +0300
committerMarko Mäkelä <marko.makela@mariadb.com>2021-04-28 17:24:44 +0300
commita81aec150528523a6da058f130caa69d1b7e378b (patch)
tree9123fe917035d28db8ef876852d734e0b1b3f1e7
parentb3d963fee4cdf9ff4b1ce88ae8140d6746bca901 (diff)
downloadmariadb-git-a81aec150528523a6da058f130caa69d1b7e378b.tar.gz
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 118e258aaac5da75a2ac4556201aaea3688fac67 (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().
-rw-r--r--storage/innobase/fil/fil0fil.cc270
-rw-r--r--storage/innobase/include/fil0fil.h12
-rw-r--r--storage/innobase/trx/trx0purge.cc45
3 files changed, 110 insertions, 217 deletions
diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc
index c96c5f2d595..947076f9b4a 100644
--- a/storage/innobase/fil/fil0fil.cc
+++ b/storage/innobase/fil/fil0fil.cc
@@ -1561,149 +1561,43 @@ fil_name_write(
mtr->log_file_op(FILE_MODIFY, space_id, name);
}
-/** Check for pending operations.
-@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 */
+fil_space_t *fil_space_t::check_pending_operations(ulint id)
{
- mysql_mutex_assert_owner(&fil_system.mutex);
-
- /* The following code must change when InnoDB supports
- 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);
+ ut_a(!is_system_tablespace(id));
+ mysql_mutex_lock(&fil_system.mutex);
+ fil_space_t *space= fil_space_get_by_id(id);
- if (count == 0) {
- break;
- }
+ if (space)
+ {
+ const uint32_t n= space->acquire_low();
+ ut_ad(!(n & STOPPING));
- std::this_thread::sleep_for(std::chrono::milliseconds(20));
- mysql_mutex_lock(&fil_system.mutex);
- sp = fil_space_get_by_id(id);
+ if (space->crypt_data)
+ {
+ 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) {
- mysql_mutex_unlock(&fil_system.mutex);
- break;
- }
- }
+ if (!space)
+ return nullptr;
- 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.
@@ -1712,8 +1606,7 @@ Free all pages used by the tablespace. */
void fil_close_tablespace(ulint id)
{
ut_ad(!is_system_tablespace(id));
- char* path = nullptr;
- fil_space_t* space = fil_check_pending_operations(id, false, &path);
+ fil_space_t* space = fil_space_t::check_pending_operations(id);
if (!space) {
return;
}
@@ -1730,23 +1623,22 @@ void fil_close_tablespace(ulint id)
os_aio_wait_until_no_pending_writes();
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
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)) {
os_file_delete_if_exists(innodb_data_file_key, cfg_name, NULL);
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.
@@ -1757,12 +1649,11 @@ void fil_close_tablespace(ulint id)
dberr_t fil_delete_tablespace(ulint id, bool if_exists,
std::vector<pfs_os_file_t>* detached_handles)
{
- char* path = NULL;
ut_ad(!is_system_tablespace(id));
ut_ad(!detached_handles || detached_handles->empty());
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) {
err = DB_TABLESPACE_NOT_FOUND;
@@ -1771,8 +1662,9 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists,
<< " because it is not found"
" in the tablespace memory cache.";
}
-
- goto func_exit;
+func_exit:
+ ibuf_delete_for_discarded_space(id);
+ return err;
}
/* IMPORTANT: Because we have set space::stop_new_ops there
@@ -1808,7 +1700,7 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists,
mtr_t mtr;
mtr.start();
- mtr.log_file_op(FILE_DELETE, id, path);
+ mtr.log_file_op(FILE_DELETE, id, space->chain.start->name);
mtr.commit();
/* Even if we got killed shortly after deleting the
tablespace file, the record must have already been
@@ -1816,7 +1708,8 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists,
log_write_up_to(mtr.commit_lsn(), true);
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,
cfg_name, nullptr);
ut_free(cfg_name);
@@ -1832,54 +1725,34 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists,
/* Double check the sanity of pending ops after reacquiring
the fil_system::mutex. */
- if (const fil_space_t* s = fil_space_get_by_id(id)) {
- ut_a(s == space);
- ut_a(!space->referenced());
- ut_a(UT_LIST_GET_LEN(space->chain) == 1);
- auto handles = fil_system.detach(space,
- detached_handles != nullptr);
- if (detached_handles) {
- *detached_handles = std::move(handles);
- }
- 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);
- }
+ ut_a(space == fil_space_get_by_id(id));
+ ut_a(!space->referenced());
+ ut_a(UT_LIST_GET_LEN(space->chain) == 1);
+ auto handles = fil_system.detach(space, detached_handles != nullptr);
+ if (detached_handles) {
+ *detached_handles = std::move(handles);
+ }
+ mysql_mutex_unlock(&fil_system.mutex);
- mysql_mutex_unlock(&log_sys.mutex);
- fil_space_free_low(space);
+ mysql_mutex_lock(&log_sys.mutex);
- if (!os_file_delete(innodb_data_file_key, path)
- && !os_file_delete_if_exists(
- innodb_data_file_key, path, NULL)) {
+ if (space->max_lsn != 0) {
+ ut_d(space->max_lsn = 0);
+ fil_system.named_spaces.remove(*space);
+ }
- /* Note: This is because we have removed the
- tablespace instance from the cache. */
+ mysql_mutex_unlock(&log_sys.mutex);
- err = DB_IO_ERROR;
- }
- } else {
- mysql_mutex_unlock(&fil_system.mutex);
- err = DB_TABLESPACE_NOT_FOUND;
+ if (!os_file_delete(innodb_data_file_key, space->chain.start->name)
+ && !os_file_delete_if_exists(innodb_data_file_key,
+ space->chain.start->name, NULL)) {
+ /* Note: This is because we have removed the
+ tablespace instance from the cache. */
+ err = DB_IO_ERROR;
}
-func_exit:
- ut_free(path);
- 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);
+ fil_space_free_low(space);
+ goto func_exit;
}
/*******************************************************************//**
@@ -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);
ut_ad(node);
- if (type.type == IORequest::READ_ASYNC && is_stopping()
- && !is_being_truncated) {
+ if (type.type == IORequest::READ_ASYNC && is_stopping()) {
release();
return {DB_TABLESPACE_DELETED, nullptr};
}
diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h
index 95009bc0bc1..bd953566d23 100644
--- a/storage/innobase/include/fil0fil.h
+++ b/storage/innobase/include/fil0fil.h
@@ -517,6 +517,12 @@ public:
/** Note that operations on the tablespace must stop or can resume */
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:
MY_ATTRIBUTE((warn_unused_result))
/** Try to acquire a tablespace reference.
@@ -1580,12 +1586,6 @@ dberr_t
fil_delete_tablespace(ulint id, bool if_exists= false,
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.
The tablespace must be cached in the memory cache.
Free all pages used by the tablespace. */
diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc
index 9d248061c59..4ebab338996 100644
--- a/storage/innobase/trx/trx0purge.cc
+++ b/storage/innobase/trx/trx0purge.cc
@@ -605,7 +605,7 @@ static void trx_purge_truncate_history()
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. */
ut_a(UT_LIST_GET_LEN(space.chain) == 1);
fil_node_t* file = UT_LIST_GET_FIRST(space.chain);
@@ -685,26 +685,47 @@ not_free:
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. */
mtr_t mtr;
const ulint size = SRV_UNDO_TABLESPACE_SIZE_IN_PAGES;
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.
During mtr::commit(), InnoDB can use the undo
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));
- fsp_header_init(purge_sys.truncate.current, size, &mtr);
+ fsp_header_init(&space, size, &mtr);
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);
buf_block_t* sys_header = trx_sysf_get(&mtr);