summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2021-01-27 15:10:37 +0200
committerMarko Mäkelä <marko.makela@mariadb.com>2021-01-27 15:10:37 +0200
commitfb219c7cb05e0e486658de05a11cb775e39ecc3d (patch)
treec0b5d5755b333fbe428c77d055a0532ae0d3c8e3
parent5d188796dbaf3600112fa733208e140a6e38ff6a (diff)
downloadmariadb-git-bb-10.6-monty-MDEV-24184.tar.gz
WIP MDEV-24184 InnoDB RENAME TABLE recovery failure if names are reusedbb-10.6-monty-MDEV-24184
fil_op_replay_rename(): Remove. fil_rename_tablespace_check(): Remove a parameter is_discarded=false. recv_sys_t::parse(): Instead of applying FILE_RENAME operations, buffer the operations in renamed_spaces. recv_sys_t::apply(): In the last_batch, apply renamed_spaces. FIXME: Remove or adjust rename_table_in_prepare() in Mariabackup so that the fil_space_t will match the metadata. With this correct RENAME logging, we should not need either *.ren files or any log_file_op calls.
-rw-r--r--storage/innobase/fil/fil0fil.cc108
-rw-r--r--storage/innobase/include/fil0fil.h14
-rw-r--r--storage/innobase/log/log0recv.cc72
3 files changed, 70 insertions, 124 deletions
diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc
index deb4c2bd7dd..1443aabb665 100644
--- a/storage/innobase/fil/fil0fil.cc
+++ b/storage/innobase/fil/fil0fil.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2014, 2020, MariaDB Corporation.
+Copyright (c) 2014, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -111,19 +111,6 @@ bool fil_space_t::try_to_close(bool print_info)
return false;
}
-/** Test if a tablespace file can be renamed to a new filepath by checking
-if that the old filepath exists and the new filepath does not exist.
-@param[in] old_path old filepath
-@param[in] new_path new filepath
-@param[in] is_discarded whether the tablespace is discarded
-@param[in] replace_new whether to ignore the existence of new_path
-@return innodb error code */
-static dberr_t
-fil_rename_tablespace_check(
- const char* old_path,
- const char* new_path,
- bool is_discarded,
- bool replace_new = false);
/** Rename a single-table tablespace.
The tablespace must exist in the memory cache.
@param[in] id tablespace identifier
@@ -1539,8 +1526,6 @@ fil_name_write_rename(
fil_name_write_rename_low(space_id, old_name, new_name, &mtr);
mtr.commit();
log_write_up_to(mtr.commit_lsn(), true);
- /* MDEV-24184 FIXME: Remove the call to log_make_checkpoint() */
- log_make_checkpoint();
}
/** Write FILE_MODIFY for a file.
@@ -1558,89 +1543,6 @@ fil_name_write(
mtr->log_file_op(FILE_MODIFY, space_id, name);
}
-/** Replay a file rename operation if possible.
-@param[in] space_id tablespace identifier
-@param[in] name old file name
-@param[in] new_name new file name
-@return whether the operation was successfully applied
-(the name did not exist, or new_name did not exist and
-name was successfully renamed to new_name) */
-bool
-fil_op_replay_rename(
- ulint space_id,
- const char* name,
- const char* new_name)
-{
- /* In order to replay the rename, the following must hold:
- * The new name is not already used.
- * A tablespace exists with the old name.
- * The space ID for that tablepace matches this log entry.
- This will prevent unintended renames during recovery. */
- fil_space_t* space = fil_space_get(space_id);
-
- if (space == NULL) {
- return(true);
- }
-
- const bool name_match
- = strcmp(name, UT_LIST_GET_FIRST(space->chain)->name) == 0;
-
- if (!name_match) {
- return(true);
- }
-
- /* Create the database directory for the new name, if
- it does not exist yet */
-
- const char* namend = strrchr(new_name, OS_PATH_SEPARATOR);
- ut_a(namend != NULL);
-
- char* dir = static_cast<char*>(
- ut_malloc_nokey(ulint(namend - new_name) + 1));
-
- memcpy(dir, new_name, ulint(namend - new_name));
- dir[namend - new_name] = '\0';
-
- bool success = os_file_create_directory(dir, false);
- ut_a(success);
-
- ulint dirlen = 0;
-
- if (const char* dirend = strrchr(dir, OS_PATH_SEPARATOR)) {
- dirlen = ulint(dirend - dir) + 1;
- }
-
- ut_free(dir);
-
- /* New path must not exist. */
- dberr_t err = fil_rename_tablespace_check(
- name, new_name, false);
- if (err != DB_SUCCESS) {
- ib::error() << " Cannot replay file rename."
- " Remove either file and try again.";
- return(false);
- }
-
- char* new_table = mem_strdupl(
- new_name + dirlen,
- strlen(new_name + dirlen)
- - 4 /* remove ".ibd" */);
-
- ut_ad(new_table[ulint(namend - new_name) - dirlen]
- == OS_PATH_SEPARATOR);
-#if OS_PATH_SEPARATOR != '/'
- new_table[namend - new_name - dirlen] = '/';
-#endif
-
- if (!fil_rename_tablespace(
- space_id, name, new_table, new_name)) {
- ut_error;
- }
-
- ut_free(new_table);
- return(true);
-}
-
/** Check for pending operations.
@param[in] space tablespace
@param[in] count number of attempts so far
@@ -2067,22 +1969,18 @@ fil_make_filepath(
if that the old filepath exists and the new filepath does not exist.
@param[in] old_path old filepath
@param[in] new_path new filepath
-@param[in] is_discarded whether the tablespace is discarded
@param[in] replace_new whether to ignore the existence of new_path
@return innodb error code */
static dberr_t
fil_rename_tablespace_check(
const char* old_path,
const char* new_path,
- bool is_discarded,
bool replace_new)
{
bool exists = false;
os_file_type_t ftype;
- if (!is_discarded
- && os_file_status(old_path, &exists, &ftype)
- && !exists) {
+ if (os_file_status(old_path, &exists, &ftype) && !exists) {
ib::error() << "Cannot rename '" << old_path
<< "' to '" << new_path
<< "' because the source file"
@@ -2142,7 +2040,7 @@ dberr_t fil_space_t::rename(const char* name, const char* path, bool log,
if (log) {
dberr_t err = fil_rename_tablespace_check(
- chain.start->name, path, false, replace);
+ chain.start->name, path, replace);
if (err != DB_SUCCESS) {
return(err);
}
diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h
index 94740c68410..c99e3571217 100644
--- a/storage/innobase/include/fil0fil.h
+++ b/storage/innobase/include/fil0fil.h
@@ -1564,20 +1564,6 @@ fil_write_flushed_lsn(
lsn_t lsn)
MY_ATTRIBUTE((warn_unused_result));
-/** Replay a file rename operation if possible.
-@param[in] space_id tablespace identifier
-@param[in] name old file name
-@param[in] new_name new file name
-@return whether the operation was successfully applied
-(the name did not exist, or new_name did not exist and
-name was successfully renamed to new_name) */
-bool
-fil_op_replay_rename(
- ulint space_id,
- const char* name,
- const char* new_name)
- MY_ATTRIBUTE((warn_unused_result));
-
/** Delete a tablespace and associated .ibd file.
@param[in] id tablespace identifier
@param[in] if_exists whether to ignore missing tablespace
diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc
index f80392a60b0..5383b0dfa53 100644
--- a/storage/innobase/log/log0recv.cc
+++ b/storage/innobase/log/log0recv.cc
@@ -2,7 +2,7 @@
Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
-Copyright (c) 2013, 2020, MariaDB Corporation.
+Copyright (c) 2013, 2021, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -584,6 +584,9 @@ typedef std::map<
static recv_spaces_t recv_spaces;
+/** The last parsed FILE_RENAME records */
+static std::map<uint32_t,std::string> renamed_spaces;
+
/** Report an operation to create, delete, or rename a file during backup.
@param[in] space_id tablespace identifier
@param[in] create whether the file is being created
@@ -943,6 +946,7 @@ void recv_sys_t::close()
}
recv_spaces.clear();
+ renamed_spaces.clear();
mlog_init.clear();
close_files();
@@ -2205,11 +2209,15 @@ same_page:
l, static_cast<ulint>(fnend - fn),
reinterpret_cast<const byte*>(fn2),
fn2 ? static_cast<ulint>(fn2end - fn2) : 0);
-
- if (!fn2 || !apply);
- else if (UNIV_UNLIKELY(!fil_op_replay_rename(space_id, fn, fn2)))
- found_corrupt_fs= true;
const_cast<char&>(fn[rlen])= saved_end;
+
+ if (fn2 && apply)
+ {
+ const size_t len= fn2end - fn2;
+ auto r= renamed_spaces.emplace(space_id, std::string{fn2, len});
+ if (!r.second)
+ r.first->second= std::string{fn2, len};
+ }
if (is_corrupt_fs())
return true;
}
@@ -2456,6 +2464,7 @@ inline void recv_sys_t::maybe_finish_batch()
{
mysql_mutex_assert_owner(&mutex);
ut_ad(recovery_on);
+
if (!apply_batch_on || pages.empty() || is_corrupt_log() || is_corrupt_fs())
mysql_cond_broadcast(&cond);
}
@@ -2798,6 +2807,59 @@ next_page:
buf_pool_invalidate();
mysql_mutex_lock(&log_sys.mutex);
}
+#if 1 /* Mariabackup FIXME: Remove or adjust rename_table_in_prepare() */
+ else if (srv_operation != SRV_OPERATION_NORMAL);
+#endif
+ else
+ {
+ /* In the last batch, we will apply any rename operations. */
+ for (auto r : renamed_spaces)
+ {
+ const uint32_t id= r.first;
+ fil_space_t *space= fil_space_t::get(id);
+ if (!space)
+ continue;
+ ut_ad(UT_LIST_GET_LEN(space->chain) == 1);
+ const char *old= space->chain.start->name;
+ if (r.second != old)
+ {
+ bool exists;
+ os_file_type_t ftype;
+ const char *new_name= r.second.c_str();
+ if (!os_file_status(new_name, &exists, &ftype) || exists)
+ {
+ ib::error() << "Cannot replay rename of tablespace " << id
+ << " from '" << old << "' to '" << r.second << "'";
+ found_corrupt_fs= true;
+ }
+ else
+ {
+ size_t base= r.second.rfind(OS_PATH_SEPARATOR);
+ ut_ad(base != std::string::npos);
+ size_t start= r.second.rfind(OS_PATH_SEPARATOR, base - 1);
+ if (start == std::string::npos)
+ start= 0;
+ /* Keep only databasename/tablename without .ibd suffix */
+ std::string space_name(r.second, start, r.second.size() - start - 4);
+ ut_ad(space_name[base - start] == OS_PATH_SEPARATOR);
+#if OS_PATH_SEPARATOR != '/'
+ space_name[base - start]= '/';
+#endif
+ mysql_mutex_lock(&log_sys.mutex);
+ if (dberr_t err= space->rename(space_name.c_str(), r.second.c_str(),
+ false))
+ {
+ ib::error() << "Cannot replay rename of tablespace " << id
+ << " to '" << r.second << "': " << err;
+ found_corrupt_fs= true;
+ }
+ mysql_mutex_unlock(&log_sys.mutex);
+ }
+ }
+ space->release();
+ }
+ renamed_spaces.clear();
+ }
mysql_mutex_lock(&mutex);