ClickHouse/src/Storages/MergeTree/localBackup.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

148 lines
3.9 KiB
C++
Raw Normal View History

#include "localBackup.h"
#include <Common/Exception.h>
#include <string>
#include <cerrno>
namespace DB
{
namespace ErrorCodes
{
extern const int TOO_DEEP_RECURSION;
extern const int DIRECTORY_ALREADY_EXISTS;
}
namespace
{
void localBackupImpl(
const DiskPtr & disk, const String & source_path,
const String & destination_path, bool make_source_readonly, size_t level,
2022-09-27 13:23:02 +00:00
std::optional<size_t> max_level, const NameSet & files_to_copy_instead_of_hardlinks)
{
2018-06-07 04:18:22 +00:00
if (max_level && level > *max_level)
return;
if (level >= 1000)
throw DB::Exception(DB::ErrorCodes::TOO_DEEP_RECURSION, "Too deep recursion");
disk->createDirectories(destination_path);
for (auto it = disk->iterateDirectory(source_path); it->isValid(); it->next())
{
auto source = it->path();
2021-05-08 10:59:55 +00:00
auto destination = fs::path(destination_path) / it->name();
if (!disk->isDirectory(source))
{
2022-02-17 21:26:37 +00:00
if (make_source_readonly)
disk->setReadOnly(source);
2022-09-27 13:23:02 +00:00
if (files_to_copy_instead_of_hardlinks.contains(it->name()))
disk->copyFile(source, *disk, destination);
else
disk->createHardLink(source, destination);
}
else
{
2022-09-27 13:23:02 +00:00
localBackupImpl(disk, source, destination, make_source_readonly, level + 1, max_level, files_to_copy_instead_of_hardlinks);
}
}
}
2021-10-22 18:20:31 +00:00
class CleanupOnFail
{
public:
2022-02-01 11:07:58 +00:00
explicit CleanupOnFail(std::function<void()> && cleaner_)
: cleaner(cleaner_)
{}
2021-10-22 18:20:31 +00:00
~CleanupOnFail()
{
if (!is_success)
2022-02-01 11:07:58 +00:00
{
/// We are trying to handle race condition here. So if we was not
/// able to backup directory try to remove garbage, but it's ok if
/// it doesn't exist.
try
{
cleaner();
}
catch (...)
{
tryLogCurrentException(__PRETTY_FUNCTION__);
}
}
2021-10-22 18:20:31 +00:00
}
void success()
{
is_success = true;
}
private:
std::function<void()> cleaner;
2022-02-01 11:07:58 +00:00
bool is_success{false};
2021-10-22 18:20:31 +00:00
};
2022-02-01 11:07:58 +00:00
}
2021-10-22 18:20:31 +00:00
void localBackup(
const DiskPtr & disk, const String & source_path,
const String & destination_path, bool make_source_readonly,
2022-09-27 13:50:25 +00:00
std::optional<size_t> max_level, bool copy_instead_of_hardlinks, const NameSet & files_to_copy_intead_of_hardlinks)
{
if (disk->exists(destination_path) && !disk->isDirectoryEmpty(destination_path))
{
throw DB::Exception(ErrorCodes::DIRECTORY_ALREADY_EXISTS, "Directory {} already exists and is not empty.",
DB::fullPath(disk, destination_path));
}
size_t try_no = 0;
const size_t max_tries = 10;
2022-02-01 11:07:58 +00:00
CleanupOnFail cleanup([disk, destination_path]() { disk->removeRecursive(destination_path); });
2021-10-22 18:20:31 +00:00
2017-03-25 20:12:56 +00:00
/** Files in the directory can be permanently added and deleted.
* If some file is deleted during an attempt to make a backup, then try again,
2022-02-01 11:07:58 +00:00
* because it's important to take into account any new files that might appear.
*/
while (true)
{
try
{
if (copy_instead_of_hardlinks)
disk->copyDirectoryContent(source_path, disk, destination_path);
else
2022-09-27 13:23:02 +00:00
localBackupImpl(disk, source_path, destination_path, make_source_readonly, 0, max_level, files_to_copy_intead_of_hardlinks);
}
catch (const DB::ErrnoException & e)
{
if (e.getErrno() != ENOENT)
throw;
++try_no;
if (try_no == max_tries)
throw;
continue;
}
2021-05-07 21:53:44 +00:00
catch (const fs::filesystem_error & e)
{
2021-05-07 21:53:44 +00:00
if (e.code() == std::errc::no_such_file_or_directory)
{
++try_no;
if (try_no == max_tries)
throw;
continue;
}
throw;
}
break;
}
2021-10-22 18:20:31 +00:00
cleanup.success();
}
}