Skip to content

Commit 2f47be5

Browse files
committed
Check that the destination for a backup is not in use.
1 parent 66f2ea5 commit 2f47be5

14 files changed

+302
-37
lines changed

src/Backups/BackupFactory.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ class BackupFactory : boost::noncopyable
2525
struct CreateParams
2626
{
2727
OpenMode open_mode = OpenMode::WRITE;
28-
std::optional<UUID> backup_uuid;
2928
BackupInfo backup_info;
3029
std::optional<BackupInfo> base_backup_info;
3130
String compression_method;
@@ -34,6 +33,7 @@ class BackupFactory : boost::noncopyable
3433
ContextPtr context;
3534
bool is_internal_backup = false;
3635
std::shared_ptr<IBackupCoordination> backup_coordination;
36+
std::optional<UUID> backup_uuid;
3737
};
3838

3939
static BackupFactory & instance();

src/Backups/BackupIO.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ class IBackupWriter /// BackupWriterFile, BackupWriterDisk, BackupWriterS3
2323
public:
2424
virtual ~IBackupWriter() = default;
2525
virtual bool fileExists(const String & file_name) = 0;
26+
virtual bool fileContentsEqual(const String & file_name, const String & expected_file_contents) = 0;
2627
virtual std::unique_ptr<WriteBuffer> writeFile(const String & file_name) = 0;
27-
virtual void removeFilesAfterFailure(const Strings & file_names) = 0;
28+
virtual void removeFiles(const Strings & file_names) = 0;
2829
};
2930

3031
}

src/Backups/BackupIO_Disk.cpp

+20-1
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,33 @@ bool BackupWriterDisk::fileExists(const String & file_name)
3838
return disk->exists(path / file_name);
3939
}
4040

41+
bool BackupWriterDisk::fileContentsEqual(const String & file_name, const String & expected_file_contents)
42+
{
43+
if (!disk->exists(path / file_name))
44+
return false;
45+
46+
try
47+
{
48+
auto in = disk->readFile(path / file_name);
49+
String actual_file_contents(expected_file_contents.size(), ' ');
50+
return (in->read(actual_file_contents.data(), actual_file_contents.size()) == actual_file_contents.size())
51+
&& (actual_file_contents == expected_file_contents) && in->eof();
52+
}
53+
catch (...)
54+
{
55+
tryLogCurrentException(__PRETTY_FUNCTION__);
56+
return false;
57+
}
58+
}
59+
4160
std::unique_ptr<WriteBuffer> BackupWriterDisk::writeFile(const String & file_name)
4261
{
4362
auto file_path = path / file_name;
4463
disk->createDirectories(file_path.parent_path());
4564
return disk->writeFile(file_path);
4665
}
4766

48-
void BackupWriterDisk::removeFilesAfterFailure(const Strings & file_names)
67+
void BackupWriterDisk::removeFiles(const Strings & file_names)
4968
{
5069
for (const auto & file_name : file_names)
5170
disk->removeFileIfExists(path / file_name);

src/Backups/BackupIO_Disk.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ class BackupWriterDisk : public IBackupWriter
3030
~BackupWriterDisk() override;
3131

3232
bool fileExists(const String & file_name) override;
33+
bool fileContentsEqual(const String & file_name, const String & expected_file_contents) override;
3334
std::unique_ptr<WriteBuffer> writeFile(const String & file_name) override;
34-
void removeFilesAfterFailure(const Strings & file_names) override;
35+
void removeFiles(const Strings & file_names) override;
3536

3637
private:
3738
DiskPtr disk;

src/Backups/BackupIO_File.cpp

+20-1
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,33 @@ bool BackupWriterFile::fileExists(const String & file_name)
3939
return fs::exists(path / file_name);
4040
}
4141

42+
bool BackupWriterFile::fileContentsEqual(const String & file_name, const String & expected_file_contents)
43+
{
44+
if (!fs::exists(path / file_name))
45+
return false;
46+
47+
try
48+
{
49+
auto in = createReadBufferFromFileBase(path / file_name, {});
50+
String actual_file_contents(expected_file_contents.size(), ' ');
51+
return (in->read(actual_file_contents.data(), actual_file_contents.size()) == actual_file_contents.size())
52+
&& (actual_file_contents == expected_file_contents) && in->eof();
53+
}
54+
catch (...)
55+
{
56+
tryLogCurrentException(__PRETTY_FUNCTION__);
57+
return false;
58+
}
59+
}
60+
4261
std::unique_ptr<WriteBuffer> BackupWriterFile::writeFile(const String & file_name)
4362
{
4463
auto file_path = path / file_name;
4564
fs::create_directories(file_path.parent_path());
4665
return std::make_unique<WriteBufferFromFile>(file_path);
4766
}
4867

49-
void BackupWriterFile::removeFilesAfterFailure(const Strings & file_names)
68+
void BackupWriterFile::removeFiles(const Strings & file_names)
5069
{
5170
for (const auto & file_name : file_names)
5271
fs::remove(path / file_name);

src/Backups/BackupIO_File.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ class BackupWriterFile : public IBackupWriter
2727
~BackupWriterFile() override;
2828

2929
bool fileExists(const String & file_name) override;
30+
bool fileContentsEqual(const String & file_name, const String & expected_file_contents) override;
3031
std::unique_ptr<WriteBuffer> writeFile(const String & file_name) override;
31-
void removeFilesAfterFailure(const Strings & file_names) override;
32+
void removeFiles(const Strings & file_names) override;
3233

3334
private:
3435
std::filesystem::path path;

src/Backups/BackupImpl.cpp

+88-24
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ namespace ErrorCodes
3737
extern const int BACKUP_ENTRY_ALREADY_EXISTS;
3838
extern const int BACKUP_ENTRY_NOT_FOUND;
3939
extern const int BACKUP_IS_EMPTY;
40+
extern const int FAILED_TO_SYNC_BACKUP_OR_RESTORE;
4041
extern const int LOGICAL_ERROR;
4142
}
4243

@@ -146,9 +147,9 @@ BackupImpl::BackupImpl(
146147
const std::optional<BackupInfo> & base_backup_info_,
147148
std::shared_ptr<IBackupWriter> writer_,
148149
const ContextPtr & context_,
149-
const std::optional<UUID> & backup_uuid_,
150150
bool is_internal_backup_,
151-
const std::shared_ptr<IBackupCoordination> & coordination_)
151+
const std::shared_ptr<IBackupCoordination> & coordination_,
152+
const std::optional<UUID> & backup_uuid_)
152153
: backup_name(backup_name_)
153154
, archive_params(archive_params_)
154155
, use_archives(!archive_params.archive_name.empty())
@@ -177,42 +178,28 @@ BackupImpl::~BackupImpl()
177178
}
178179
}
179180

180-
181181
void BackupImpl::open(const ContextPtr & context)
182182
{
183183
std::lock_guard lock{mutex};
184184

185-
String file_name_to_check_existence;
186-
if (use_archives)
187-
file_name_to_check_existence = archive_params.archive_name;
188-
else
189-
file_name_to_check_existence = ".backup";
190-
bool backup_exists = (open_mode == OpenMode::WRITE) ? writer->fileExists(file_name_to_check_existence) : reader->fileExists(file_name_to_check_existence);
191-
192-
if (open_mode == OpenMode::WRITE)
193-
{
194-
if (backup_exists)
195-
throw Exception(ErrorCodes::BACKUP_ALREADY_EXISTS, "Backup {} already exists", backup_name);
196-
}
197-
else
198-
{
199-
if (!backup_exists)
200-
throw Exception(ErrorCodes::BACKUP_NOT_FOUND, "Backup {} not found", backup_name);
201-
}
202-
203185
if (open_mode == OpenMode::WRITE)
204186
{
205187
timestamp = std::time(nullptr);
206188
if (!uuid)
207189
uuid = UUIDHelpers::generateV4();
190+
lock_file_name = use_archives ? (archive_params.archive_name + ".lock") : ".lock";
208191
writing_finalized = false;
192+
193+
/// Check that we can write a backup there and create the lock file to own this destination.
194+
checkBackupDoesntExist();
195+
if (!is_internal_backup)
196+
createLockFile();
197+
checkLockFile(true);
209198
}
210199

211200
if (open_mode == OpenMode::READ)
212201
readBackupMetadata();
213202

214-
assert(uuid); /// Backup's UUID must be loaded or generated at this point.
215-
216203
if (base_backup_info)
217204
{
218205
BackupFactory::CreateParams params;
@@ -253,6 +240,8 @@ time_t BackupImpl::getTimestamp() const
253240

254241
void BackupImpl::writeBackupMetadata()
255242
{
243+
assert(!is_internal_backup);
244+
256245
Poco::AutoPtr<Poco::Util::XMLConfiguration> config{new Poco::Util::XMLConfiguration()};
257246
config->setUInt("version", CURRENT_BACKUP_VERSION);
258247
config->setString("timestamp", toString(LocalDateTime{timestamp}));
@@ -308,6 +297,8 @@ void BackupImpl::writeBackupMetadata()
308297
config->save(stream);
309298
String str = stream.str();
310299

300+
checkLockFile(true);
301+
311302
std::unique_ptr<WriteBuffer> out;
312303
if (use_archives)
313304
out = getArchiveWriter("")->writeFile(".backup");
@@ -321,9 +312,17 @@ void BackupImpl::readBackupMetadata()
321312
{
322313
std::unique_ptr<ReadBuffer> in;
323314
if (use_archives)
315+
{
316+
if (!reader->fileExists(archive_params.archive_name))
317+
throw Exception(ErrorCodes::BACKUP_NOT_FOUND, "Backup {} not found", backup_name);
324318
in = getArchiveReader("")->readFile(".backup");
319+
}
325320
else
321+
{
322+
if (!reader->fileExists(".backup"))
323+
throw Exception(ErrorCodes::BACKUP_NOT_FOUND, "Backup {} not found", backup_name);
326324
in = reader->readFile(".backup");
325+
}
327326

328327
String str;
329328
readStringUntilEOF(str, *in);
@@ -387,6 +386,59 @@ void BackupImpl::readBackupMetadata()
387386
}
388387
}
389388

389+
void BackupImpl::checkBackupDoesntExist() const
390+
{
391+
String file_name_to_check_existence;
392+
if (use_archives)
393+
file_name_to_check_existence = archive_params.archive_name;
394+
else
395+
file_name_to_check_existence = ".backup";
396+
397+
if (writer->fileExists(file_name_to_check_existence))
398+
throw Exception(ErrorCodes::BACKUP_ALREADY_EXISTS, "Backup {} already exists", backup_name);
399+
400+
/// Check that no other backup (excluding internal backups) is writing to the same destination.
401+
if (!is_internal_backup)
402+
{
403+
assert(!lock_file_name.empty());
404+
if (writer->fileExists(lock_file_name))
405+
throw Exception(ErrorCodes::BACKUP_ALREADY_EXISTS, "Backup {} is being written already", backup_name);
406+
}
407+
}
408+
409+
void BackupImpl::createLockFile()
410+
{
411+
/// Internal backup must not create the lock file (it should be created by the initiator).
412+
assert(!is_internal_backup);
413+
414+
assert(uuid);
415+
auto out = writer->writeFile(lock_file_name);
416+
writeUUIDText(*uuid, *out);
417+
}
418+
419+
bool BackupImpl::checkLockFile(bool throw_if_failed) const
420+
{
421+
if (!lock_file_name.empty() && uuid && writer->fileContentsEqual(lock_file_name, toString(*uuid)))
422+
return true;
423+
424+
if (throw_if_failed)
425+
{
426+
if (!writer->fileExists(lock_file_name))
427+
throw Exception(ErrorCodes::FAILED_TO_SYNC_BACKUP_OR_RESTORE, "Lock file {} suddenly disappeared while writing backup {}", lock_file_name, backup_name);
428+
throw Exception(ErrorCodes::BACKUP_ALREADY_EXISTS, "A concurrent backup writing to the same destination {} detected", backup_name);
429+
}
430+
return false;
431+
}
432+
433+
void BackupImpl::removeLockFile()
434+
{
435+
if (is_internal_backup)
436+
return; /// Internal backup must not remove the lock file (it's still used by the initiator).
437+
438+
if (checkLockFile(false))
439+
writer->removeFiles({lock_file_name});
440+
}
441+
390442
Strings BackupImpl::listFiles(const String & directory, bool recursive) const
391443
{
392444
std::lock_guard lock{mutex};
@@ -648,6 +700,9 @@ void BackupImpl::writeFile(const String & file_name, BackupEntryPtr entry)
648700
read_buffer = entry->getReadBuffer();
649701
read_buffer->seek(copy_pos, SEEK_SET);
650702

703+
if (!num_files_written)
704+
checkLockFile(true);
705+
651706
/// Copy the entry's data after `copy_pos`.
652707
std::unique_ptr<WriteBuffer> out;
653708
if (use_archives)
@@ -675,6 +730,7 @@ void BackupImpl::writeFile(const String & file_name, BackupEntryPtr entry)
675730

676731
copyData(*read_buffer, *out);
677732
out->finalize();
733+
++num_files_written;
678734
}
679735

680736

@@ -694,6 +750,7 @@ void BackupImpl::finalizeWriting()
694750
{
695751
LOG_TRACE(log, "Finalizing backup {}", backup_name);
696752
writeBackupMetadata();
753+
removeLockFile();
697754
LOG_TRACE(log, "Finalized backup {}", backup_name);
698755
}
699756

@@ -741,6 +798,9 @@ std::shared_ptr<IArchiveWriter> BackupImpl::getArchiveWriter(const String & suff
741798

742799
void BackupImpl::removeAllFilesAfterFailure()
743800
{
801+
if (is_internal_backup)
802+
return; /// Let the initiator remove unnecessary files.
803+
744804
try
745805
{
746806
LOG_INFO(log, "Removing all files of backup {} after failure", backup_name);
@@ -762,7 +822,11 @@ void BackupImpl::removeAllFilesAfterFailure()
762822
files_to_remove.push_back(file_info.data_file_name);
763823
}
764824

765-
writer->removeFilesAfterFailure(files_to_remove);
825+
if (!checkLockFile(false))
826+
return;
827+
828+
writer->removeFiles(files_to_remove);
829+
removeLockFile();
766830
}
767831
catch (...)
768832
{

src/Backups/BackupImpl.h

+18-3
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ class BackupImpl : public IBackup
4747
const std::optional<BackupInfo> & base_backup_info_,
4848
std::shared_ptr<IBackupWriter> writer_,
4949
const ContextPtr & context_,
50-
const std::optional<UUID> & backup_uuid_ = {},
5150
bool is_internal_backup_ = false,
52-
const std::shared_ptr<IBackupCoordination> & coordination_ = {});
51+
const std::shared_ptr<IBackupCoordination> & coordination_ = {},
52+
const std::optional<UUID> & backup_uuid_ = {});
5353

5454
~BackupImpl() override;
5555

@@ -76,12 +76,25 @@ class BackupImpl : public IBackup
7676

7777
void open(const ContextPtr & context);
7878
void close();
79+
80+
/// Writes the file ".backup" containing backup's metadata.
7981
void writeBackupMetadata();
8082
void readBackupMetadata();
83+
84+
/// Checks that a new backup doesn't exist yet.
85+
void checkBackupDoesntExist() const;
86+
87+
/// Lock file named ".lock" and containing the UUID of a backup is used to own the place where we're writing the backup.
88+
/// Thus it will not be allowed to put any other backup to the same place (even if the BACKUP command is executed on a different node).
89+
void createLockFile();
90+
bool checkLockFile(bool throw_if_failed) const;
91+
void removeLockFile();
92+
93+
void removeAllFilesAfterFailure();
94+
8195
String getArchiveNameWithSuffix(const String & suffix) const;
8296
std::shared_ptr<IArchiveReader> getArchiveReader(const String & suffix) const;
8397
std::shared_ptr<IArchiveWriter> getArchiveWriter(const String & suffix);
84-
void removeAllFilesAfterFailure();
8598

8699
const String backup_name;
87100
const ArchiveParams archive_params;
@@ -102,6 +115,8 @@ class BackupImpl : public IBackup
102115
mutable std::unordered_map<String /* archive_suffix */, std::shared_ptr<IArchiveReader>> archive_readers;
103116
std::pair<String, std::shared_ptr<IArchiveWriter>> archive_writers[2];
104117
String current_archive_suffix;
118+
String lock_file_name;
119+
size_t num_files_written = 0;
105120
bool writing_finalized = false;
106121
const Poco::Logger * log;
107122
};

0 commit comments

Comments
 (0)