Commit 6a4e5bf1 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-25852: Orphan #sql*.ibd files are left behind

The implementation of MDEV-24626 was not entirely correct.
We could occasionally fail to remove some *.ibd files on recovery.

deferred_spaces: Keep track of FILE_DELETE records.

deferred_spaces.add(): Do not allow duplicate file names.

recv_rename_files(): Preserve some of renamed_spaces entries for
deferred_spaces.reinit_all().

Thanks to Thirunarayanan Balathandayuthapani for noticing that
deferred_spaces.add() must filter out duplicate file names,
as well as some debugging help.
parent 65f1a427
...@@ -597,6 +597,8 @@ static struct ...@@ -597,6 +597,8 @@ static struct
lsn_t lsn; lsn_t lsn;
/** File name from the FILE_ record */ /** File name from the FILE_ record */
std::string file_name; std::string file_name;
/** whether a FILE_DELETE record was encountered */
mutable bool deleted;
}; };
using map= std::map<const uint32_t, item, std::less<const uint32_t>, using map= std::map<const uint32_t, item, std::less<const uint32_t>,
...@@ -638,11 +640,39 @@ static struct ...@@ -638,11 +640,39 @@ static struct
char *fil_path= fil_make_filepath(nullptr, {filename, strlen(filename)}, char *fil_path= fil_make_filepath(nullptr, {filename, strlen(filename)},
IBD, false); IBD, false);
const item defer= {lsn, fil_path}; const item defer{lsn, fil_path, false};
auto p= defers.emplace(space, defer);
if (!p.second && p.first->second.lsn <= defer.lsn)
p.first->second= defer;
ut_free(fil_path); ut_free(fil_path);
/* The file name must be unique. Keep the one with the latest LSN. */
auto d= defers.begin();
while (d != defers.end())
{
if (d->second.file_name != defer.file_name)
++d;
else if (d->first == space)
{
/* Neither the file name nor the tablespace ID changed.
Update the LSN if needed. */
if (d->second.lsn < lsn)
d->second.lsn= lsn;
return;
}
else if (d->second.lsn < lsn)
defers.erase(d++);
else
{
ut_ad(d->second.lsn != lsn);
return; /* A later tablespace already has this name. */
}
}
auto p= defers.emplace(space, defer);
if (!p.second && p.first->second.lsn <= lsn)
{
p.first->second.lsn= lsn;
p.first->second.file_name= defer.file_name;
}
} }
void remove(uint32_t space) void remove(uint32_t space)
...@@ -684,20 +714,42 @@ static struct ...@@ -684,20 +714,42 @@ static struct
const uint32_t space_id{d->first}; const uint32_t space_id{d->first};
recv_sys_t::map::iterator p{recv_sys.pages.lower_bound({space_id,0})}; recv_sys_t::map::iterator p{recv_sys.pages.lower_bound({space_id,0})};
if (p == recv_sys.pages.end() || p->first.space() != space_id) if (d->second.deleted ||
p == recv_sys.pages.end() || p->first.space() != space_id)
{ {
/* No pages were recovered. We create a dummy tablespace, /* We found a FILE_DELETE record for the tablespace, or
and let dict_drop_index_tree() delete the file. */ there were no buffered records. Either way, we must create a
dummy tablespace with the latest known name,
for dict_drop_index_tree(). */
while (p != recv_sys.pages.end() && p->first.space() == space_id)
{
recv_sys_t::map::iterator r= p++;
r->second.log.clear();
recv_sys.pages.erase(r);
}
recv_spaces_t::iterator it{recv_spaces.find(space_id)}; recv_spaces_t::iterator it{recv_spaces.find(space_id)};
if (it != recv_spaces.end()) if (it != recv_spaces.end())
create(it, d->second.file_name, static_cast<uint32_t> {
const std::string *name= &d->second.file_name;
if (d->second.deleted)
{
const auto r= renamed_spaces.find(space_id);
if (r != renamed_spaces.end())
name= &r->second;
bool exists;
os_file_type_t ftype;
if (!os_file_status(name->c_str(), &exists, &ftype) || !exists)
goto processed;
}
create(it, *name, static_cast<uint32_t>
(1U << FSP_FLAGS_FCRC32_POS_MARKER | (1U << FSP_FLAGS_FCRC32_POS_MARKER |
FSP_FLAGS_FCRC32_PAGE_SSIZE()), nullptr, 0); FSP_FLAGS_FCRC32_PAGE_SSIZE()), nullptr, 0);
}
} }
else else
fail= recv_sys.recover_deferred(p, d->second.file_name, free_block); fail= recv_sys.recover_deferred(p, d->second.file_name, free_block);
auto e= d++; processed:
defers.erase(e); defers.erase(d++);
if (fail) if (fail)
break; break;
if (free_block) if (free_block)
...@@ -791,11 +843,13 @@ bool recv_sys_t::recover_deferred(recv_sys_t::map::iterator &p, ...@@ -791,11 +843,13 @@ bool recv_sys_t::recover_deferred(recv_sys_t::map::iterator &p,
space->release(); space->release();
return false; return false;
} }
goto fail;
} }
block->unfix(); block->unfix();
} }
fail:
ib::error() << "Cannot apply log to " << first ib::error() << "Cannot apply log to " << first
<< " of corrupted file '" << name << "'"; << " of corrupted file '" << name << "'";
return true; return true;
...@@ -1034,9 +1088,11 @@ fil_name_process(char* name, ulint len, ulint space_id, ...@@ -1034,9 +1088,11 @@ fil_name_process(char* name, ulint len, ulint space_id,
if (deleted) { if (deleted) {
/* Got FILE_DELETE */ /* Got FILE_DELETE */
if (auto d = deferred_spaces.find(static_cast<uint32_t>(
space_id))) {
d->deleted = true;
}
deferred_spaces.remove(
static_cast<uint32_t>(space_id));
if (!p.second && f.status != file_name_t::DELETED) { if (!p.second && f.status != file_name_t::DELETED) {
f.status = file_name_t::DELETED; f.status = file_name_t::DELETED;
if (f.space != NULL) { if (f.space != NULL) {
...@@ -2988,10 +3044,10 @@ void recv_sys_t::apply(bool last_batch) ...@@ -2988,10 +3044,10 @@ void recv_sys_t::apply(bool last_batch)
auto d= deferred_spaces.defers.find(space_id); auto d= deferred_spaces.defers.find(space_id);
if (d != deferred_spaces.defers.end()) if (d != deferred_spaces.defers.end())
{ {
if (recover_deferred(p, d->second.file_name, free_block)) if (d->second.deleted)
{ {
if (!srv_force_recovery) /* For deleted files we must preserve the entry in deferred_spaces */
set_corrupt_fs(); erase_for_space:
while (p != pages.end() && p->first.space() == space_id) while (p != pages.end() && p->first.space() == space_id)
{ {
map::iterator r= p++; map::iterator r= p++;
...@@ -2999,7 +3055,15 @@ void recv_sys_t::apply(bool last_batch) ...@@ -2999,7 +3055,15 @@ void recv_sys_t::apply(bool last_batch)
pages.erase(r); pages.erase(r);
} }
} }
deferred_spaces.defers.erase(d); else if (recover_deferred(p, d->second.file_name, free_block))
{
if (!srv_force_recovery)
set_corrupt_fs();
deferred_spaces.defers.erase(d);
goto erase_for_space;
}
else
deferred_spaces.defers.erase(d);
if (!free_block) if (!free_block)
goto next_free_block; goto next_free_block;
p= pages.lower_bound(page_id); p= pages.lower_bound(page_id);
...@@ -3685,12 +3749,16 @@ static dberr_t recv_rename_files() ...@@ -3685,12 +3749,16 @@ static dberr_t recv_rename_files()
dberr_t err= DB_SUCCESS; dberr_t err= DB_SUCCESS;
for (const auto &r : renamed_spaces) for (auto i= renamed_spaces.begin(); i != renamed_spaces.end(); )
{ {
const auto &r= *i;
const uint32_t id= r.first; const uint32_t id= r.first;
fil_space_t *space= fil_space_t::get(id); fil_space_t *space= fil_space_t::get(id);
if (!space) if (!space)
{
i++;
continue; continue;
}
ut_ad(UT_LIST_GET_LEN(space->chain) == 1); ut_ad(UT_LIST_GET_LEN(space->chain) == 1);
char *old= space->chain.start->name; char *old= space->chain.start->name;
if (r.second != old) if (r.second != old)
...@@ -3743,8 +3811,8 @@ static dberr_t recv_rename_files() ...@@ -3743,8 +3811,8 @@ static dberr_t recv_rename_files()
recv_sys.set_corrupt_fs(); recv_sys.set_corrupt_fs();
break; break;
} }
renamed_spaces.erase(i++);
} }
renamed_spaces.clear();
return err; return err;
} }
......
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