Skip to content

Commit a618691

Browse files
committed
Read-only BackupEngine
Summary: Read-only BackupEngine can connect to the same backup directory that is already running BackupEngine. That enables some interesting use-cases (i.e. restoring replica from primary's backup directory) Test Plan: added a unit test Reviewers: dhruba, haobo, ljin Reviewed By: ljin CC: leveldb Differential Revision: https://reviews.facebook.net/D18297
1 parent ccaca59 commit a618691

File tree

3 files changed

+155
-29
lines changed

3 files changed

+155
-29
lines changed

include/utilities/backupable_db.h

+23
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,29 @@ struct BackupInfo {
117117
: backup_id(_backup_id), timestamp(_timestamp), size(_size) {}
118118
};
119119

120+
class BackupEngineReadOnly {
121+
public:
122+
virtual ~BackupEngineReadOnly() {}
123+
124+
static BackupEngineReadOnly* NewReadOnlyBackupEngine(
125+
Env* db_env, const BackupableDBOptions& options);
126+
127+
// You can GetBackupInfo safely, even with other BackupEngine performing
128+
// backups on the same directory
129+
virtual void GetBackupInfo(std::vector<BackupInfo>* backup_info) = 0;
130+
131+
// Restoring DB from backup is NOT safe when there is another BackupEngine
132+
// running that might call DeleteBackup() or PurgeOldBackups(). It is caller's
133+
// responsibility to synchronize the operation, i.e. don't delete the backup
134+
// when you're restoring from it
135+
virtual Status RestoreDBFromBackup(
136+
BackupID backup_id, const std::string& db_dir, const std::string& wal_dir,
137+
const RestoreOptions& restore_options = RestoreOptions()) = 0;
138+
virtual Status RestoreDBFromLatestBackup(
139+
const std::string& db_dir, const std::string& wal_dir,
140+
const RestoreOptions& restore_options = RestoreOptions()) = 0;
141+
};
142+
120143
// Please see the documentation in BackupableDB and RestoreBackupableDB
121144
class BackupEngine {
122145
public:

utilities/backupable/backupable_db.cc

+90-27
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ void BackupableDBOptions::Dump(Logger* logger) const {
8787
// -------- BackupEngineImpl class ---------
8888
class BackupEngineImpl : public BackupEngine {
8989
public:
90-
BackupEngineImpl(Env* db_env, const BackupableDBOptions& options);
90+
BackupEngineImpl(Env* db_env, const BackupableDBOptions& options,
91+
bool read_only = false);
9192
~BackupEngineImpl();
9293
Status CreateNewBackup(DB* db, bool flush_before_backup = false);
9394
Status PurgeOldBackups(uint32_t num_backups_to_keep);
@@ -149,7 +150,7 @@ class BackupEngineImpl : public BackupEngine {
149150

150151
Status AddFile(const FileInfo& file_info);
151152

152-
void Delete();
153+
void Delete(bool delete_meta = true);
153154

154155
bool Empty() {
155156
return files_.empty();
@@ -258,6 +259,7 @@ class BackupEngineImpl : public BackupEngine {
258259

259260
static const size_t kDefaultCopyFileBufferSize = 5 * 1024 * 1024LL; // 5MB
260261
size_t copy_file_buffer_size_;
262+
bool read_only_;
261263
};
262264

263265
BackupEngine* BackupEngine::NewBackupEngine(
@@ -266,27 +268,34 @@ BackupEngine* BackupEngine::NewBackupEngine(
266268
}
267269

268270
BackupEngineImpl::BackupEngineImpl(Env* db_env,
269-
const BackupableDBOptions& options)
271+
const BackupableDBOptions& options,
272+
bool read_only)
270273
: stop_backup_(false),
271274
options_(options),
272275
db_env_(db_env),
273276
backup_env_(options.backup_env != nullptr ? options.backup_env : db_env_),
274-
copy_file_buffer_size_(kDefaultCopyFileBufferSize) {
277+
copy_file_buffer_size_(kDefaultCopyFileBufferSize),
278+
read_only_(read_only) {
279+
if (read_only_) {
280+
Log(options_.info_log, "Starting read_only backup engine");
281+
}
275282
options_.Dump(options_.info_log);
276283

277-
// create all the dirs we need
278-
backup_env_->CreateDirIfMissing(GetAbsolutePath());
279-
backup_env_->NewDirectory(GetAbsolutePath(), &backup_directory_);
280-
if (options_.share_table_files) {
281-
backup_env_->CreateDirIfMissing(GetAbsolutePath(GetSharedFileRel()));
282-
backup_env_->NewDirectory(GetAbsolutePath(GetSharedFileRel()),
283-
&shared_directory_);
284+
if (!read_only_) {
285+
// create all the dirs we need
286+
backup_env_->CreateDirIfMissing(GetAbsolutePath());
287+
backup_env_->NewDirectory(GetAbsolutePath(), &backup_directory_);
288+
if (options_.share_table_files) {
289+
backup_env_->CreateDirIfMissing(GetAbsolutePath(GetSharedFileRel()));
290+
backup_env_->NewDirectory(GetAbsolutePath(GetSharedFileRel()),
291+
&shared_directory_);
292+
}
293+
backup_env_->CreateDirIfMissing(GetAbsolutePath(GetPrivateDirRel()));
294+
backup_env_->NewDirectory(GetAbsolutePath(GetPrivateDirRel()),
295+
&private_directory_);
296+
backup_env_->CreateDirIfMissing(GetBackupMetaDir());
297+
backup_env_->NewDirectory(GetBackupMetaDir(), &meta_directory_);
284298
}
285-
backup_env_->CreateDirIfMissing(GetAbsolutePath(GetPrivateDirRel()));
286-
backup_env_->NewDirectory(GetAbsolutePath(GetPrivateDirRel()),
287-
&private_directory_);
288-
backup_env_->CreateDirIfMissing(GetBackupMetaDir());
289-
backup_env_->NewDirectory(GetBackupMetaDir(), &meta_directory_);
290299

291300
std::vector<std::string> backup_meta_files;
292301
backup_env_->GetChildren(GetBackupMetaDir(), &backup_meta_files);
@@ -295,8 +304,10 @@ BackupEngineImpl::BackupEngineImpl(Env* db_env,
295304
BackupID backup_id = 0;
296305
sscanf(file.c_str(), "%u", &backup_id);
297306
if (backup_id == 0 || file != std::to_string(backup_id)) {
298-
// invalid file name, delete that
299-
backup_env_->DeleteFile(GetBackupMetaDir() + "/" + file);
307+
if (!read_only_) {
308+
// invalid file name, delete that
309+
backup_env_->DeleteFile(GetBackupMetaDir() + "/" + file);
310+
}
300311
continue;
301312
}
302313
assert(backups_.find(backup_id) == backups_.end());
@@ -306,6 +317,7 @@ BackupEngineImpl::BackupEngineImpl(Env* db_env,
306317
}
307318

308319
if (options_.destroy_old_data) { // Destory old data
320+
assert(!read_only_);
309321
for (auto& backup : backups_) {
310322
backup.second.Delete();
311323
obsolete_backups_.push_back(backup.first);
@@ -319,9 +331,12 @@ BackupEngineImpl::BackupEngineImpl(Env* db_env,
319331
for (auto& backup : backups_) {
320332
Status s = backup.second.LoadFromFile(options_.backup_dir);
321333
if (!s.ok()) {
322-
Log(options_.info_log, "Backup %u corrupted - deleting -- %s",
323-
backup.first, s.ToString().c_str());
324-
backup.second.Delete();
334+
Log(options_.info_log, "Backup %u corrupted -- %s", backup.first,
335+
s.ToString().c_str());
336+
if (!read_only_) {
337+
Log(options_.info_log, "-> Deleting backup %u", backup.first);
338+
}
339+
backup.second.Delete(!read_only_);
325340
obsolete_backups_.push_back(backup.first);
326341
}
327342
}
@@ -331,6 +346,7 @@ BackupEngineImpl::BackupEngineImpl(Env* db_env,
331346
}
332347

333348
Status s = GetLatestBackupFileContents(&latest_backup_id_);
349+
334350
// If latest backup file is corrupted or non-existent
335351
// set latest backup as the biggest backup we have
336352
// or 0 if we have no backups
@@ -349,16 +365,18 @@ BackupEngineImpl::BackupEngineImpl(Env* db_env,
349365
itr = backups_.erase(itr);
350366
}
351367

352-
PutLatestBackupFileContents(latest_backup_id_); // Ignore errors
353-
GarbageCollection(true);
354-
Log(options_.info_log,
355-
"Initialized BackupEngine, the latest backup is %u.",
368+
if (!read_only_) {
369+
PutLatestBackupFileContents(latest_backup_id_); // Ignore errors
370+
GarbageCollection(true);
371+
}
372+
Log(options_.info_log, "Initialized BackupEngine, the latest backup is %u.",
356373
latest_backup_id_);
357374
}
358375

359376
BackupEngineImpl::~BackupEngineImpl() { LogFlush(options_.info_log); }
360377

361378
Status BackupEngineImpl::CreateNewBackup(DB* db, bool flush_before_backup) {
379+
assert(!read_only_);
362380
Status s;
363381
std::vector<std::string> live_files;
364382
VectorLogPtr live_wal_files;
@@ -499,6 +517,7 @@ Status BackupEngineImpl::CreateNewBackup(DB* db, bool flush_before_backup) {
499517
}
500518

501519
Status BackupEngineImpl::PurgeOldBackups(uint32_t num_backups_to_keep) {
520+
assert(!read_only_);
502521
Log(options_.info_log, "Purging old backups, keeping %u",
503522
num_backups_to_keep);
504523
while (num_backups_to_keep < backups_.size()) {
@@ -512,6 +531,7 @@ Status BackupEngineImpl::PurgeOldBackups(uint32_t num_backups_to_keep) {
512531
}
513532

514533
Status BackupEngineImpl::DeleteBackup(BackupID backup_id) {
534+
assert(!read_only_);
515535
Log(options_.info_log, "Deleting backup %u", backup_id);
516536
auto backup = backups_.find(backup_id);
517537
if (backup == backups_.end()) {
@@ -662,6 +682,7 @@ Status BackupEngineImpl::GetLatestBackupFileContents(uint32_t* latest_backup) {
662682
// do something like 1. delete file, 2. write new file
663683
// We write to a tmp file and then atomically rename
664684
Status BackupEngineImpl::PutLatestBackupFileContents(uint32_t latest_backup) {
685+
assert(!read_only_);
665686
Status s;
666687
unique_ptr<WritableFile> file;
667688
EnvOptions env_options;
@@ -871,6 +892,7 @@ void BackupEngineImpl::DeleteChildren(const std::string& dir,
871892
}
872893

873894
void BackupEngineImpl::GarbageCollection(bool full_scan) {
895+
assert(!read_only_);
874896
Log(options_.info_log, "Starting garbage collection");
875897
std::vector<std::string> to_delete;
876898
for (auto& itr : backuped_file_infos_) {
@@ -973,15 +995,17 @@ Status BackupEngineImpl::BackupMeta::AddFile(const FileInfo& file_info) {
973995
return Status::OK();
974996
}
975997

976-
void BackupEngineImpl::BackupMeta::Delete() {
998+
void BackupEngineImpl::BackupMeta::Delete(bool delete_meta) {
977999
for (const auto& file : files_) {
9781000
auto itr = file_infos_->find(file);
9791001
assert(itr != file_infos_->end());
9801002
--(itr->second.refs); // decrease refcount
9811003
}
9821004
files_.clear();
9831005
// delete meta file
984-
env_->DeleteFile(meta_filename_);
1006+
if (delete_meta) {
1007+
env_->DeleteFile(meta_filename_);
1008+
}
9851009
timestamp_ = 0;
9861010
}
9871011

@@ -1107,6 +1131,45 @@ Status BackupEngineImpl::BackupMeta::StoreToFile(bool sync) {
11071131
return s;
11081132
}
11091133

1134+
// -------- BackupEngineReadOnlyImpl ---------
1135+
class BackupEngineReadOnlyImpl : public BackupEngineReadOnly {
1136+
public:
1137+
BackupEngineReadOnlyImpl(Env* db_env, const BackupableDBOptions& options) {
1138+
backup_engine_ = new BackupEngineImpl(db_env, options, true);
1139+
}
1140+
virtual ~BackupEngineReadOnlyImpl() {}
1141+
1142+
virtual void GetBackupInfo(std::vector<BackupInfo>* backup_info) {
1143+
backup_engine_->GetBackupInfo(backup_info);
1144+
}
1145+
1146+
virtual Status RestoreDBFromBackup(
1147+
BackupID backup_id, const std::string& db_dir, const std::string& wal_dir,
1148+
const RestoreOptions& restore_options = RestoreOptions()) {
1149+
return backup_engine_->RestoreDBFromBackup(backup_id, db_dir, wal_dir,
1150+
restore_options);
1151+
}
1152+
1153+
virtual Status RestoreDBFromLatestBackup(
1154+
const std::string& db_dir, const std::string& wal_dir,
1155+
const RestoreOptions& restore_options = RestoreOptions()) {
1156+
return backup_engine_->RestoreDBFromLatestBackup(db_dir, wal_dir,
1157+
restore_options);
1158+
}
1159+
1160+
private:
1161+
BackupEngineImpl* backup_engine_;
1162+
};
1163+
1164+
BackupEngineReadOnly* BackupEngineReadOnly::NewReadOnlyBackupEngine(
1165+
Env* db_env, const BackupableDBOptions& options) {
1166+
if (options.destroy_old_data) {
1167+
assert(false);
1168+
return nullptr;
1169+
}
1170+
return new BackupEngineReadOnlyImpl(db_env, options);
1171+
}
1172+
11101173
// --- BackupableDB methods --------
11111174

11121175
BackupableDB::BackupableDB(DB* db, const BackupableDBOptions& options)

utilities/backupable/backupable_db_test.cc

+42-2
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,12 @@ class TestEnv : public EnvWrapper {
178178
return EnvWrapper::NewWritableFile(f, r, options);
179179
}
180180

181+
virtual Status DeleteFile(const std::string& fname) override {
182+
ASSERT_GT(limit_delete_files_, 0);
183+
limit_delete_files_--;
184+
return EnvWrapper::DeleteFile(fname);
185+
}
186+
181187
void AssertWrittenFiles(std::vector<std::string>& should_have_written) {
182188
sort(should_have_written.begin(), should_have_written.end());
183189
sort(written_files_.begin(), written_files_.end());
@@ -192,6 +198,8 @@ class TestEnv : public EnvWrapper {
192198
limit_written_files_ = limit;
193199
}
194200

201+
void SetLimitDeleteFiles(uint64_t limit) { limit_delete_files_ = limit; }
202+
195203
void SetDummySequentialFile(bool dummy_sequential_file) {
196204
dummy_sequential_file_ = dummy_sequential_file;
197205
}
@@ -200,7 +208,8 @@ class TestEnv : public EnvWrapper {
200208
bool dummy_sequential_file_ = false;
201209
std::vector<std::string> written_files_;
202210
uint64_t limit_written_files_ = 1000000;
203-
}; // TestEnv
211+
uint64_t limit_delete_files_ = 1000000;
212+
}; // TestEnv
204213

205214
class FileManager : public EnvWrapper {
206215
public:
@@ -864,7 +873,38 @@ TEST(BackupableDBTest, RateLimiting) {
864873
}
865874
}
866875

867-
} // anon namespace
876+
TEST(BackupableDBTest, ReadOnlyBackupEngine) {
877+
DestroyDB(dbname_, Options());
878+
OpenBackupableDB(true);
879+
FillDB(db_.get(), 0, 100);
880+
ASSERT_OK(db_->CreateNewBackup(true));
881+
FillDB(db_.get(), 100, 200);
882+
ASSERT_OK(db_->CreateNewBackup(true));
883+
CloseBackupableDB();
884+
DestroyDB(dbname_, Options());
885+
886+
backupable_options_->destroy_old_data = false;
887+
test_backup_env_->ClearWrittenFiles();
888+
test_backup_env_->SetLimitDeleteFiles(0);
889+
auto read_only_backup_engine =
890+
BackupEngineReadOnly::NewReadOnlyBackupEngine(env_, *backupable_options_);
891+
std::vector<BackupInfo> backup_info;
892+
read_only_backup_engine->GetBackupInfo(&backup_info);
893+
ASSERT_EQ(backup_info.size(), 2U);
894+
895+
RestoreOptions restore_options(false);
896+
ASSERT_OK(read_only_backup_engine->RestoreDBFromLatestBackup(
897+
dbname_, dbname_, restore_options));
898+
delete read_only_backup_engine;
899+
std::vector<std::string> should_have_written;
900+
test_backup_env_->AssertWrittenFiles(should_have_written);
901+
902+
DB* db = OpenDB();
903+
AssertExists(db, 0, 200);
904+
delete db;
905+
}
906+
907+
} // anon namespace
868908

869909
} // namespace rocksdb
870910

0 commit comments

Comments
 (0)