400 lines
12 KiB
C++
400 lines
12 KiB
C++
/* Copyright (c) 2018, 2021, Alibaba and/or its affiliates. All rights reserved.
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL/PolarDB-X Engine hereby grant you an
|
|
additional permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with
|
|
MySQL/PolarDB-X Engine.
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License, version 2.0, for more details.
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
/** @file fil/fil0purge.cc
|
|
Implementation of data file purge operation.
|
|
|
|
Created 1/11/2019 Galaxy SQL
|
|
*******************************************************/
|
|
|
|
#include "fil0purge.h"
|
|
#include "fil0fil.h"
|
|
#include "os0file.h"
|
|
#include "row0mysql.h"
|
|
#include "srv0file.h"
|
|
#include "ut0mutex.h"
|
|
|
|
/* Global file purge system */
|
|
File_purge *file_purge_sys = nullptr;
|
|
|
|
/** Constructor */
|
|
File_purge::File_purge(ulint thread_id, time_t start_time)
|
|
: m_thread_id(thread_id),
|
|
m_start_time(start_time),
|
|
m_id(0),
|
|
m_dir(nullptr) {
|
|
mutex_create(LATCH_ID_FILE_PURGE_LIST, &m_mutex);
|
|
UT_LIST_INIT(m_list, &file_purge_node_t::LIST);
|
|
}
|
|
|
|
File_purge::~File_purge() {
|
|
/**
|
|
Clear the file nodes, those will be purged again when DDL log recovery after
|
|
reboot
|
|
*/
|
|
for (file_purge_node_t *node = UT_LIST_GET_FIRST(m_list); node != nullptr;
|
|
node = UT_LIST_GET_FIRST(m_list)) {
|
|
if (node->m_file_path) ut_free(node->m_file_path);
|
|
if (node->m_original_path) ut_free(node->m_original_path);
|
|
UT_LIST_REMOVE(m_list, node);
|
|
ut_free(node);
|
|
}
|
|
|
|
mutex_free(&m_mutex);
|
|
}
|
|
|
|
void File_purge::lock() { mutex_enter(&m_mutex); }
|
|
|
|
void File_purge::unlock() { mutex_exit(&m_mutex); }
|
|
|
|
/**
|
|
Iterate the file nodes in list.
|
|
|
|
@param[in] first Whether it is first retrieve from list
|
|
@param[in] last Last iterated file node
|
|
|
|
@retval node The file purge node in list
|
|
*/
|
|
file_purge_node_t *File_purge::iterate_node(bool first,
|
|
file_purge_node_t *last) {
|
|
DBUG_ENTER("File_purge::iterate_node");
|
|
|
|
if (first) {
|
|
ut_ad(last == nullptr);
|
|
DBUG_RETURN(UT_LIST_GET_FIRST(m_list));
|
|
} else {
|
|
ut_ad(last != nullptr);
|
|
DBUG_RETURN(UT_LIST_GET_NEXT(LIST, last));
|
|
}
|
|
}
|
|
|
|
/* Get next unique id number */
|
|
ulint File_purge::next_id() {
|
|
ulint number;
|
|
number = m_id++;
|
|
return number;
|
|
}
|
|
/**
|
|
Add file into purge list
|
|
|
|
@param[in] id Log DDL record id
|
|
@param[int] path The temporary file name
|
|
@param[int] original_path The original file name
|
|
|
|
@retval false Success
|
|
@retval true Failure
|
|
*/
|
|
bool File_purge::add_file(ulint id, const char *path,
|
|
const char *original_path) {
|
|
bool success = false;
|
|
os_offset_t file_size;
|
|
|
|
pfs_os_file_t handle = os_file_create_simple_no_error_handling(
|
|
innodb_data_file_key, path, OS_FILE_OPEN, OS_FILE_READ_WRITE, FALSE,
|
|
&success);
|
|
|
|
if (!success) {
|
|
ib::error(ER_IB_MSG_392) << "Cannot open temp data file for read-write: '"
|
|
<< path << "'"
|
|
<< " when add file into purge list";
|
|
|
|
/** Temp file maybe has been purged */
|
|
ut_free(const_cast<char *>(path));
|
|
log_ddl->remove_by_id(id);
|
|
return true;
|
|
}
|
|
|
|
file_size = os_file_get_size(handle);
|
|
os_file_close(handle);
|
|
|
|
file_purge_node_t *node = static_cast<file_purge_node_t *>(
|
|
ut_malloc_nokey(sizeof(file_purge_node_t)));
|
|
|
|
node->m_log_ddl_id = id;
|
|
node->m_file_path = const_cast<char *>(path);
|
|
node->m_start_time = ut_time();
|
|
|
|
/* Original path will be null when ddl log recovery */
|
|
node->m_original_path = original_path ? mem_strdup(original_path) : nullptr;
|
|
|
|
node->m_original_size = file_size;
|
|
/**
|
|
Current size will change when purge file,
|
|
and is not protected by mutex.
|
|
*/
|
|
node->m_current_size = file_size;
|
|
|
|
if (srv_print_data_file_purge_process)
|
|
ib::info(ER_IB_MSG_FILE_PURGE) << "File purge add file : " << id << ";"
|
|
<< path;
|
|
|
|
mutex_enter(&m_mutex);
|
|
UT_LIST_ADD_LAST(m_list, node);
|
|
mutex_exit(&m_mutex);
|
|
|
|
/* Wake up background thread */
|
|
srv_wakeup_file_purge_thread();
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
Purge the first file node by purge max size.
|
|
|
|
@param[in] size Purge max size (bytes)
|
|
@param[in] force Whether unlink file directly
|
|
|
|
First return in std::pair
|
|
@retval -1 Error
|
|
@retval 0 Success & no file purge
|
|
@retval >0 How many purged operation
|
|
|
|
Second return in std::pair
|
|
@retval size Truncated file size
|
|
*/
|
|
std::pair<int, ulint> File_purge::purge_file(ulint size, bool force) {
|
|
bool success = false;
|
|
uint truncated = 0;
|
|
file_purge_node_t *node = nullptr;
|
|
pfs_os_file_t handle;
|
|
ulint truncated_size = 0;
|
|
|
|
mutex_enter(&m_mutex);
|
|
node = UT_LIST_GET_FIRST(m_list);
|
|
mutex_exit(&m_mutex);
|
|
|
|
if (node) {
|
|
handle = os_file_create_simple_no_error_handling(
|
|
innodb_data_file_key, node->m_file_path, OS_FILE_OPEN,
|
|
OS_FILE_READ_WRITE, FALSE, &success);
|
|
if (!success) {
|
|
ulint err = os_file_get_last_error(true);
|
|
ib::error(ER_IB_MSG_392) << "Cannot open temp data file for read-write: '"
|
|
<< node->m_file_path << "'"
|
|
<< " when purge file from list";
|
|
/* Maybe delete by DBA, so remove file directly */
|
|
if (err == OS_FILE_NOT_FOUND)
|
|
remove_file(node);
|
|
|
|
return std::make_pair(-1, 0);
|
|
}
|
|
|
|
if (srv_print_data_file_purge_process)
|
|
ib::info(ER_IB_MSG_FILE_PURGE) << "File purge purge file : "
|
|
<< node->m_file_path;
|
|
|
|
os_offset_t file_size = os_file_get_size(handle);
|
|
if (!force && file_size > size) {
|
|
ut_ad(file_size == node->m_current_size);
|
|
/* Truncate predefined size once */
|
|
os_file_truncate(node->m_file_path, handle, file_size - size);
|
|
os_file_close(handle);
|
|
truncated++;
|
|
node->m_current_size = (file_size - size);
|
|
truncated_size += size;
|
|
} else {
|
|
/* Direct delete the file when file_size < predefined size */
|
|
os_file_close(handle);
|
|
os_file_delete(innodb_data_file_key, node->m_file_path);
|
|
remove_file(node);
|
|
truncated++;
|
|
truncated_size += file_size;
|
|
}
|
|
} else {
|
|
return std::make_pair(0, 0);
|
|
}
|
|
|
|
return std::make_pair(truncated, truncated_size);
|
|
}
|
|
|
|
/**
|
|
The data file list length
|
|
|
|
@retval >=0
|
|
*/
|
|
ulint File_purge::length() {
|
|
ulint len;
|
|
mutex_enter(&m_mutex);
|
|
len = UT_LIST_GET_LEN(m_list);
|
|
mutex_exit(&m_mutex);
|
|
|
|
return len;
|
|
}
|
|
/**
|
|
Purge all the data file cached in list.
|
|
|
|
@param[in] size Purge max size (bytes)
|
|
@param[in] force Purge little by little
|
|
or unlink directly.
|
|
*/
|
|
void File_purge::purge_all(ulint size, bool force) {
|
|
while (length() > 0) {
|
|
purge_file(size, force);
|
|
}
|
|
}
|
|
/**
|
|
Remove the file node from list
|
|
|
|
@param[in] node File purge node pointer
|
|
*/
|
|
void File_purge::remove_file(file_purge_node_t *node) {
|
|
ulint log_id = node->m_log_ddl_id;
|
|
|
|
mutex_enter(&m_mutex);
|
|
UT_LIST_REMOVE(m_list, node);
|
|
mutex_exit(&m_mutex);
|
|
|
|
#ifdef UNIV_DEBUG
|
|
bool exist;
|
|
os_file_type_t type;
|
|
os_file_status(node->m_file_path, &exist, &type);
|
|
ut_ad(exist == false);
|
|
#endif
|
|
|
|
if (srv_print_data_file_purge_process)
|
|
ib::info(ER_IB_MSG_FILE_PURGE) << "File purge remove file : "
|
|
<< node->m_file_path;
|
|
ut_free(node->m_file_path);
|
|
ut_free(node->m_original_path);
|
|
ut_free(node);
|
|
|
|
log_ddl->remove_by_id(log_id);
|
|
}
|
|
/**
|
|
Generate a unique temporary file name.
|
|
|
|
@param[in] filepath Original file name
|
|
|
|
@retval file name Generated file name
|
|
*/
|
|
char *File_purge::generate_file(const char *filepath) {
|
|
std::string new_path;
|
|
|
|
new_path.assign(get_dir());
|
|
|
|
std::string temp_filename;
|
|
temp_filename.assign(PREFIX);
|
|
temp_filename.append(std::to_string((ulong)m_start_time));
|
|
temp_filename.append("_");
|
|
temp_filename.append(std::to_string(next_id()));
|
|
|
|
char *new_file = Fil_path::make(new_path, temp_filename, NO_EXT, false);
|
|
|
|
if (srv_print_data_file_purge_process)
|
|
ib::info(ER_IB_MSG_FILE_PURGE) << "File purge generate file : " << filepath
|
|
<< ";" << new_file;
|
|
return new_file;
|
|
}
|
|
|
|
/**
|
|
Drop a single-table tablespace and rename the data file as temporary file.
|
|
This deletes the fil_space_t if found and rename the file on disk.
|
|
|
|
@param[in] space_id Tablespace id
|
|
@param[in] filepath File path of tablespace to delete
|
|
|
|
@retval error code */
|
|
dberr_t row_purge_single_table_tablespace(space_id_t space_id,
|
|
const char *filepath) {
|
|
dberr_t err = DB_SUCCESS;
|
|
uint64_t log_id;
|
|
|
|
char *new_filepath = file_purge_sys->generate_file(filepath);
|
|
|
|
log_ddl->write_purge_file_log(&log_id, file_purge_sys->get_thread_id(),
|
|
new_filepath);
|
|
|
|
if (srv_print_data_file_purge_process)
|
|
ib::info(ER_IB_MSG_FILE_PURGE) << "File purge write log : " << log_id << ";"
|
|
<< new_filepath;
|
|
|
|
if (!fil_space_exists_in_mem(space_id, nullptr, true, false, NULL, 0)) {
|
|
if (fil_purge_file(filepath, new_filepath)) {
|
|
ib::info(ER_IB_MSG_989) << "Purge data file " << filepath;
|
|
}
|
|
} else {
|
|
// purge file TODO BUF_REMOVE_FLUSH_NO_WRITE -> BUF_REMOVE_NONE
|
|
err = fil_delete_tablespace(space_id, BUF_REMOVE_FLUSH_NO_WRITE,
|
|
new_filepath);
|
|
}
|
|
|
|
file_purge_sys->add_file(log_id, new_filepath, filepath);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
Rename the ibd data file and delete the releted files
|
|
|
|
@param[in] old_filepath The original data file
|
|
@param[in] new_filepath The new data file
|
|
|
|
@retval TRUE Success
|
|
@retval FALSE Failure
|
|
*/
|
|
bool fil_purge_file(const char *old_filepath, const char *new_filepath) {
|
|
bool success = true;
|
|
bool exist;
|
|
os_file_type_t type;
|
|
|
|
/* If old file has been renamed or dropped, just skip it */
|
|
os_file_status(old_filepath, &exist, &type);
|
|
|
|
if (exist && (!(success = os_file_rename(innodb_data_file_key, old_filepath,
|
|
new_filepath)))) {
|
|
success =
|
|
os_file_delete_if_exists(innodb_data_file_key, old_filepath, nullptr);
|
|
}
|
|
|
|
char *cfg_filepath = Fil_path::make_cfg(old_filepath);
|
|
|
|
if (cfg_filepath != nullptr) {
|
|
os_file_delete_if_exists(innodb_data_file_key, cfg_filepath, nullptr);
|
|
|
|
ut_free(cfg_filepath);
|
|
}
|
|
|
|
char *cfp_filepath = Fil_path::make_cfp(old_filepath);
|
|
|
|
if (cfp_filepath != nullptr) {
|
|
os_file_delete_if_exists(innodb_data_file_key, cfp_filepath, nullptr);
|
|
|
|
ut_free(cfp_filepath);
|
|
}
|
|
|
|
return (success);
|
|
}
|
|
|
|
/**
|
|
Drop or purge single table tablespace
|
|
|
|
@param[in] space_id tablespace id
|
|
@param[in] filepath tablespace data file path
|
|
|
|
|
|
@retval DB_SUCCESS or error
|
|
*/
|
|
dberr_t row_drop_or_purge_single_table_tablespace(space_id_t space_id,
|
|
const char *filepath) {
|
|
if (srv_data_file_purge)
|
|
return row_purge_single_table_tablespace(space_id, filepath);
|
|
else
|
|
return row_drop_tablespace(space_id, filepath);
|
|
}
|