起因
异地容灾项目遇到一个奇怪的问题, 清理文件目录的时候, 总会清理不干净,导致恢复失败
分析
- 日志确认,在调用boost::remove_all 的地方打日志, 发现文件和目录都删了一遍
- 可能是文件重复创建了,因为此时文件的ctime 很新, 排查了所有文件处理函数,都没有可能出现创建的效果
- 那就是remove_all 的问题
问题定位:
模拟remove_all删除文件,单线程一切正常,后来发现我们的项目是多线程的,于是多线程下,remove_all 同时删除,问题复现
原因分析
remove_all 删除目录时,会先迭代删除子目录和文件, 最后删除本身目录, 而多线程下,有子目录被其它线程删的时候, remove_all 异常退出, 不再继续删除, 这导致了目录遗留的情况。 至于文件目录ctime问题,是由于设置权限chmod 时, 会更新ctime。
解决办法
检查异常码, 如果异常码为 no_such_file_or_directory 则进行再次删除
如果不是子目录被删的情况,即使指定目录不存在, ec 也不会为 no_such_file_or_directory
fs::remove_all(fs::path(m_abs_path), ec)) {
if (ec && ec == boost::system::errc::no_such_file_or_directory) {
// 重复执行 remove_all
}
boost remove_all 源码
- while 循环递归删除
const fs::directory_iterator end_dit;
while(itr != end_dit)
{
fs::file_type tmp_type = query_file_type(itr->path(), ec);
if (ec != 0 && *ec)
return count;
count += remove_all_aux(itr->path(), tmp_type, ec);
if (ec != 0 && *ec)
return count;
fs::detail::directory_iterator_increment(itr, ec);
if (ec != 0 && *ec)
return count;
}
remove_file_or_directory(p, type, ec);
if (ec != 0 && *ec)
return count;
return ++count;
- 执行删除, 由error 来判断是否抛异常
if (type == fs::directory_file
# ifdef BOOST_WINDOWS_API
|| type == fs::_detail_directory_symlink
# endif
)
{
if (error(!remove_directory(p) ? BOOST_ERRNO : 0, p, ec,
"boost::filesystem::remove"))
return false;
}
else
{
if (error(!remove_file(p) ? BOOST_ERRNO : 0, p, ec,
"boost::filesystem::remove"))
return false;
}
- 抛异常的地方, 可见通过ec本身是否为0 来决定是抛异常,还是直接赋值 ec。 这是boost库常见的两种api调用
void emit_error(err_t error_num, const path& p, system::error_code* ec, const char* message)
{
if (!ec)
BOOST_FILESYSTEM_THROW(filesystem_error(message, p, system::error_code(error_num, system::system_category())));
else
ec->assign(error_num, system::system_category());
}