MDEV-8069 DROP or rebuild of a large table may lock up InnoDB
Problematic mutex is dict_sys.mutex. Idea of the patch: unlink() fd under that mutex while it's still open. This way unlink() will be fast and actual file removal will happen on close(). And close() will be called outside of dict_sys.mutex. This should be safe against crash which may happen between unlink() and close(): file will be removed by OS anyway. The same applies to both *nix and Windows. I created and removed a 4G file on some NVMe SSD on ext4: write(3, "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1"..., 1048576) = 1048576 <0.000519> fdatasync(3) = 0 <3.533763> close(3) = 0 <0.000011> unlink("file") = 0 <0.411563> write(3, "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1"..., 1048576) = 1048576 <0.000520> fdatasync(3) = 0 <3.544938> unlink("file") = 0 <0.000029> close(3) = 0 <0.407057> Such systems can benefit of this patch. fil_node_t::detach(): closes fil_node_t but not file handle, returns that file handle fil_node_t::prepare_to_close_or_deatch(): 'closes' fil_node_t fil_node_t:close_to_free(): new argument detach_handle fil_system_t::detach(): now can detach file handles fil_delete_tablespace(): now can detach file handles row_drop_table_for_mysql(): performs actual file removal
This commit is contained in:
parent
af194ab5bd
commit
264a98eaa0
@ -511,29 +511,38 @@ fail:
|
|||||||
/** Close the file handle. */
|
/** Close the file handle. */
|
||||||
void fil_node_t::close()
|
void fil_node_t::close()
|
||||||
{
|
{
|
||||||
bool ret;
|
prepare_to_close_or_detach();
|
||||||
|
|
||||||
|
/* printf("Closing file %s\n", name); */
|
||||||
|
int ret= os_file_close(handle);
|
||||||
|
ut_a(ret);
|
||||||
|
handle= OS_FILE_CLOSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
pfs_os_file_t fil_node_t::detach()
|
||||||
|
{
|
||||||
|
prepare_to_close_or_detach();
|
||||||
|
|
||||||
|
pfs_os_file_t result= handle;
|
||||||
|
handle= OS_FILE_CLOSED;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fil_node_t::prepare_to_close_or_detach()
|
||||||
|
{
|
||||||
ut_ad(mutex_own(&fil_system.mutex));
|
ut_ad(mutex_own(&fil_system.mutex));
|
||||||
ut_a(is_open());
|
ut_a(is_open());
|
||||||
ut_a(n_pending == 0);
|
ut_a(n_pending == 0);
|
||||||
ut_a(n_pending_flushes == 0);
|
ut_a(n_pending_flushes == 0);
|
||||||
ut_a(!being_extended);
|
ut_a(!being_extended);
|
||||||
ut_a(!needs_flush
|
ut_a(!needs_flush || space->purpose == FIL_TYPE_TEMPORARY ||
|
||||||
|| space->purpose == FIL_TYPE_TEMPORARY
|
srv_fast_shutdown == 2 || !srv_was_started);
|
||||||
|| srv_fast_shutdown == 2
|
|
||||||
|| !srv_was_started);
|
|
||||||
|
|
||||||
ret = os_file_close(handle);
|
|
||||||
ut_a(ret);
|
|
||||||
|
|
||||||
/* printf("Closing file %s\n", name); */
|
|
||||||
|
|
||||||
handle = OS_FILE_CLOSED;
|
|
||||||
ut_ad(!is_open());
|
|
||||||
ut_a(fil_system.n_open > 0);
|
ut_a(fil_system.n_open > 0);
|
||||||
fil_system.n_open--;
|
fil_system.n_open--;
|
||||||
|
|
||||||
if (fil_space_belongs_in_lru(space)) {
|
if (fil_space_belongs_in_lru(space))
|
||||||
|
{
|
||||||
ut_a(UT_LIST_GET_LEN(fil_system.LRU) > 0);
|
ut_a(UT_LIST_GET_LEN(fil_system.LRU) > 0);
|
||||||
UT_LIST_REMOVE(fil_system.LRU, this);
|
UT_LIST_REMOVE(fil_system.LRU, this);
|
||||||
}
|
}
|
||||||
@ -917,7 +926,7 @@ fil_space_extend(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Prepare to free a file from fil_system. */
|
/** Prepare to free a file from fil_system. */
|
||||||
inline void fil_node_t::close_to_free()
|
pfs_os_file_t fil_node_t::close_to_free(bool detach_handle)
|
||||||
{
|
{
|
||||||
ut_ad(mutex_own(&fil_system.mutex));
|
ut_ad(mutex_own(&fil_system.mutex));
|
||||||
ut_a(magic_n == FIL_NODE_MAGIC_N);
|
ut_a(magic_n == FIL_NODE_MAGIC_N);
|
||||||
@ -958,15 +967,24 @@ inline void fil_node_t::close_to_free()
|
|||||||
}
|
}
|
||||||
ut_a(!n_pending_flushes);
|
ut_a(!n_pending_flushes);
|
||||||
ut_a(!being_extended);
|
ut_a(!being_extended);
|
||||||
|
if (detach_handle)
|
||||||
|
{
|
||||||
|
auto result= handle;
|
||||||
|
handle= OS_FILE_CLOSED;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
bool ret= os_file_close(handle);
|
bool ret= os_file_close(handle);
|
||||||
ut_a(ret);
|
ut_a(ret);
|
||||||
handle= OS_FILE_CLOSED;
|
handle= OS_FILE_CLOSED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return OS_FILE_CLOSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Detach a tablespace from the cache and close the files. */
|
/** Detach a tablespace from the cache and close the files. */
|
||||||
inline void fil_system_t::detach(fil_space_t *space)
|
std::vector<pfs_os_file_t> fil_system_t::detach(fil_space_t *space,
|
||||||
|
bool detach_handle)
|
||||||
{
|
{
|
||||||
ut_ad(mutex_own(&fil_system.mutex));
|
ut_ad(mutex_own(&fil_system.mutex));
|
||||||
HASH_DELETE(fil_space_t, hash, spaces, space->id, space);
|
HASH_DELETE(fil_space_t, hash, spaces, space->id, space);
|
||||||
@ -1000,9 +1018,18 @@ inline void fil_system_t::detach(fil_space_t *space)
|
|||||||
n_open--;
|
n_open--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<pfs_os_file_t> handles;
|
||||||
|
handles.reserve(UT_LIST_GET_LEN(space->chain));
|
||||||
|
|
||||||
for (fil_node_t* node= UT_LIST_GET_FIRST(space->chain); node;
|
for (fil_node_t* node= UT_LIST_GET_FIRST(space->chain); node;
|
||||||
node= UT_LIST_GET_NEXT(chain, node))
|
node= UT_LIST_GET_NEXT(chain, node))
|
||||||
node->close_to_free();
|
{
|
||||||
|
auto handle= node->close_to_free(detach_handle);
|
||||||
|
if (handle != OS_FILE_CLOSED)
|
||||||
|
handles.push_back(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handles;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Free a tablespace object on which fil_system_t::detach() was invoked.
|
/** Free a tablespace object on which fil_system_t::detach() was invoked.
|
||||||
@ -2180,11 +2207,14 @@ bool fil_table_accessible(const dict_table_t* table)
|
|||||||
/** Delete a tablespace and associated .ibd file.
|
/** Delete a tablespace and associated .ibd file.
|
||||||
@param[in] id tablespace identifier
|
@param[in] id tablespace identifier
|
||||||
@param[in] if_exists whether to ignore missing tablespace
|
@param[in] if_exists whether to ignore missing tablespace
|
||||||
|
@param[in,out] detached_handles return detached handles if not nullptr
|
||||||
@return DB_SUCCESS or error */
|
@return DB_SUCCESS or error */
|
||||||
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)
|
||||||
{
|
{
|
||||||
char* path = NULL;
|
char* path = NULL;
|
||||||
ut_ad(!is_system_tablespace(id));
|
ut_ad(!is_system_tablespace(id));
|
||||||
|
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_check_pending_operations(id, false, &path);
|
||||||
@ -2260,7 +2290,11 @@ dberr_t fil_delete_tablespace(ulint id, bool if_exists)
|
|||||||
ut_a(s == space);
|
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);
|
||||||
fil_system.detach(space);
|
auto handles = fil_system.detach(space,
|
||||||
|
detached_handles != nullptr);
|
||||||
|
if (detached_handles) {
|
||||||
|
*detached_handles = std::move(handles);
|
||||||
|
}
|
||||||
mutex_exit(&fil_system.mutex);
|
mutex_exit(&fil_system.mutex);
|
||||||
|
|
||||||
log_mutex_enter();
|
log_mutex_enter();
|
||||||
|
@ -653,11 +653,19 @@ struct fil_node_t {
|
|||||||
|
|
||||||
/** Close the file handle. */
|
/** Close the file handle. */
|
||||||
void close();
|
void close();
|
||||||
/** Prepare to free a file from fil_system. */
|
/** Same as close() but returns file handle instead of closing it. */
|
||||||
inline void close_to_free();
|
pfs_os_file_t detach() MY_ATTRIBUTE((warn_unused_result));
|
||||||
|
/** Prepare to free a file from fil_system.
|
||||||
|
@param detach_handle whether to detach instead of closing a handle
|
||||||
|
@return detached handle or OS_FILE_CLOSED */
|
||||||
|
pfs_os_file_t close_to_free(bool detach_handle= false);
|
||||||
|
|
||||||
/** Update the data structures on I/O completion */
|
/** Update the data structures on I/O completion */
|
||||||
inline void complete_io(bool write= false);
|
inline void complete_io(bool write= false);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Does stuff common for close() and detach() */
|
||||||
|
void prepare_to_close_or_detach();
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Value of fil_node_t::magic_n */
|
/** Value of fil_node_t::magic_n */
|
||||||
@ -973,8 +981,12 @@ public:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
/** Detach a tablespace from the cache and close the files. */
|
/** Detach a tablespace from the cache and close the files.
|
||||||
inline void detach(fil_space_t *space);
|
@param space tablespace
|
||||||
|
@param detach_handle whether to detach or close handles
|
||||||
|
@return detached handles or empty vector */
|
||||||
|
std::vector<pfs_os_file_t> detach(fil_space_t *space,
|
||||||
|
bool detach_handle= false);
|
||||||
|
|
||||||
ib_mutex_t mutex; /*!< The mutex protecting the cache */
|
ib_mutex_t mutex; /*!< The mutex protecting the cache */
|
||||||
fil_space_t* sys_space; /*!< The innodb_system tablespace */
|
fil_space_t* sys_space; /*!< The innodb_system tablespace */
|
||||||
@ -1285,8 +1297,11 @@ bool fil_table_accessible(const dict_table_t* table)
|
|||||||
/** Delete a tablespace and associated .ibd file.
|
/** Delete a tablespace and associated .ibd file.
|
||||||
@param[in] id tablespace identifier
|
@param[in] id tablespace identifier
|
||||||
@param[in] if_exists whether to ignore missing tablespace
|
@param[in] if_exists whether to ignore missing tablespace
|
||||||
|
@param[out] leaked_handles return detached handles here
|
||||||
@return DB_SUCCESS or error */
|
@return DB_SUCCESS or error */
|
||||||
dberr_t fil_delete_tablespace(ulint id, bool if_exists= false);
|
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.
|
/** Prepare to truncate an undo tablespace.
|
||||||
@param[in] space_id undo tablespace id
|
@param[in] space_id undo tablespace id
|
||||||
|
@ -3374,6 +3374,8 @@ row_drop_table_for_mysql(
|
|||||||
DBUG_RETURN(DB_TABLE_NOT_FOUND);
|
DBUG_RETURN(DB_TABLE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<pfs_os_file_t> detached_handles;
|
||||||
|
|
||||||
const bool is_temp_name = strstr(table->name.m_name,
|
const bool is_temp_name = strstr(table->name.m_name,
|
||||||
"/" TEMP_FILE_PREFIX);
|
"/" TEMP_FILE_PREFIX);
|
||||||
|
|
||||||
@ -3757,7 +3759,8 @@ do_drop:
|
|||||||
ut_ad(!filepath);
|
ut_ad(!filepath);
|
||||||
|
|
||||||
if (space->id != TRX_SYS_SPACE) {
|
if (space->id != TRX_SYS_SPACE) {
|
||||||
err = fil_delete_tablespace(space->id);
|
err = fil_delete_tablespace(space->id, false,
|
||||||
|
&detached_handles);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -3837,6 +3840,11 @@ funct_exit_all_freed:
|
|||||||
row_mysql_unlock_data_dictionary(trx);
|
row_mysql_unlock_data_dictionary(trx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& handle : detached_handles) {
|
||||||
|
ut_ad(handle != OS_FILE_CLOSED);
|
||||||
|
os_file_close(handle);
|
||||||
|
}
|
||||||
|
|
||||||
trx->op_info = "";
|
trx->op_info = "";
|
||||||
|
|
||||||
srv_wake_master_thread();
|
srv_wake_master_thread();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user