Skip to content

Commit

Permalink
Reunite checkpoint and backup core logic
Browse files Browse the repository at this point in the history
Summary:
These code paths forked when checkpoint was introduced by copy/pasting the core backup logic. Over time they diverged and bug fixes were sometimes applied to one but not the other (like fix to include all relevant WALs for 2PC), or it required extra effort to fix both (like fix to forge CURRENT file). This diff reunites the code paths by extracting the core logic into a function, CreateCustomCheckpoint(), that is customizable via callbacks to implement both checkpoint and backup.

Related changes:

- flush_before_backup is now forcibly enabled when 2PC is enabled
- Extracted CheckpointImpl class definition into a header file. This is so the function, CreateCustomCheckpoint(), can be called by internal rocksdb code but not exposed to users.
- Implemented more functions in DummyDB/DummyLogFile (in backupable_db_test.cc) that are used by CreateCustomCheckpoint().
Closes facebook#1932

Differential Revision: D4622986

Pulled By: ajkr

fbshipit-source-id: 157723884236ee3999a682673b64f7457a7a0d87
  • Loading branch information
ajkr authored and facebook-github-bot committed Apr 24, 2017
1 parent 72c21fb commit e5e545a
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 231 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ set(SOURCES
util/xxhash.cc
utilities/backupable/backupable_db.cc
utilities/blob_db/blob_db.cc
utilities/checkpoint/checkpoint.cc
utilities/checkpoint/checkpoint_impl.cc
utilities/col_buf_decoder.cc
utilities/col_buf_encoder.cc
utilities/column_aware_encoding_util.cc
Expand Down
2 changes: 1 addition & 1 deletion TARGETS
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ cpp_library(
"util/xxhash.cc",
"utilities/backupable/backupable_db.cc",
"utilities/blob_db/blob_db.cc",
"utilities/checkpoint/checkpoint.cc",
"utilities/checkpoint/checkpoint_impl.cc",
"utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc",
"utilities/convenience/info_log_finder.cc",
"utilities/date_tiered/date_tiered_db_impl.cc",
Expand Down
2 changes: 2 additions & 0 deletions include/rocksdb/utilities/backupable_db.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,12 +256,14 @@ class BackupEngine {
BackupEngine** backup_engine_ptr);

// same as CreateNewBackup, but stores extra application metadata
// Flush will always trigger if 2PC is enabled.
virtual Status CreateNewBackupWithMetadata(
DB* db, const std::string& app_metadata, bool flush_before_backup = false,
std::function<void()> progress_callback = []() {}) = 0;

// Captures the state of the database in the latest backup
// NOT a thread safe call
// Flush will always trigger if 2PC is enabled.
virtual Status CreateNewBackup(DB* db, bool flush_before_backup = false,
std::function<void()> progress_callback =
[]() {}) {
Expand Down
2 changes: 1 addition & 1 deletion src.mk
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ LIB_SOURCES = \
util/xxhash.cc \
utilities/backupable/backupable_db.cc \
utilities/blob_db/blob_db.cc \
utilities/checkpoint/checkpoint.cc \
utilities/checkpoint/checkpoint_impl.cc \
utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc \
utilities/convenience/info_log_finder.cc \
utilities/date_tiered/date_tiered_db_impl.cc \
Expand Down
142 changes: 46 additions & 96 deletions utilities/backupable/backupable_db.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "util/logging.h"
#include "util/string_util.h"
#include "util/sync_point.h"
#include "utilities/checkpoint/checkpoint_impl.h"

#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
Expand Down Expand Up @@ -702,28 +703,6 @@ Status BackupEngineImpl::CreateNewBackupWithMetadata(
if (app_metadata.size() > kMaxAppMetaSize) {
return Status::InvalidArgument("App metadata too large");
}
Status s;
std::vector<std::string> live_files;
VectorLogPtr live_wal_files;
uint64_t manifest_file_size = 0;
uint64_t sequence_number = db->GetLatestSequenceNumber();

s = db->DisableFileDeletions();
if (s.ok()) {
// this will return live_files prefixed with "/"
s = db->GetLiveFiles(live_files, &manifest_file_size, flush_before_backup);
}
// if we didn't flush before backup, we need to also get WAL files
if (s.ok() && !flush_before_backup && options_.backup_log_files) {
// returns file names prefixed with "/"
s = db->GetSortedWalFiles(live_wal_files);
}
if (!s.ok()) {
db->EnableFileDeletions(false);
return s;
}
TEST_SYNC_POINT("BackupEngineImpl::CreateNewBackup:SavedLiveFiles1");
TEST_SYNC_POINT("BackupEngineImpl::CreateNewBackup:SavedLiveFiles2");

BackupID new_backup_id = latest_backup_id_ + 1;

Expand All @@ -735,7 +714,6 @@ Status BackupEngineImpl::CreateNewBackupWithMetadata(
assert(ret.second == true);
auto& new_backup = ret.first->second;
new_backup->RecordTimestamp();
new_backup->SetSequenceNumber(sequence_number);
new_backup->SetAppMetadata(app_metadata);

auto start_backup = backup_env_-> NowMicros();
Expand All @@ -745,7 +723,7 @@ Status BackupEngineImpl::CreateNewBackupWithMetadata(
new_backup_id);

auto private_tmp_dir = GetAbsolutePath(GetPrivateFileRel(new_backup_id, true));
s = backup_env_->FileExists(private_tmp_dir);
Status s = backup_env_->FileExists(private_tmp_dir);
if (s.ok()) {
// maybe last backup failed and left partial state behind, clean it up
s = GarbageCollect();
Expand All @@ -767,81 +745,53 @@ Status BackupEngineImpl::CreateNewBackupWithMetadata(
// This is used to check whether a live files shares a dst_path with another
// live file.
std::unordered_set<std::string> live_dst_paths;
live_dst_paths.reserve(live_files.size() + live_wal_files.size());

// Pre-fetch sizes for data files
std::unordered_map<std::string, uint64_t> data_path_to_size;
if (s.ok()) {
s = InsertPathnameToSizeBytes(db->GetName(), db_env_, &data_path_to_size);
}

std::vector<BackupAfterCopyOrCreateWorkItem> backup_items_to_finish;
// Add a CopyOrCreateWorkItem to the channel for each live file
std::string manifest_fname, current_fname;
for (size_t i = 0; s.ok() && i < live_files.size(); ++i) {
uint64_t number;
FileType type;
bool ok = ParseFileName(live_files[i], &number, &type);
if (!ok) {
assert(false);
return Status::Corruption("Can't parse file name. This is very bad");
}
// we should only get sst, manifest and current files here
assert(type == kTableFile || type == kDescriptorFile ||
type == kCurrentFile || type == kOptionsFile);

if (type == kCurrentFile) {
// We will craft the current file manually to ensure it's consistent with
// the manifest number. This is necessary because current's file contents
// can change during backup.
current_fname = live_files[i];
continue;
} else if (type == kDescriptorFile) {
manifest_fname = live_files[i];
}

auto data_path_to_size_iter =
data_path_to_size.find(db->GetName() + live_files[i]);
uint64_t size_bytes = data_path_to_size_iter == data_path_to_size.end()
? port::kMaxUint64
: data_path_to_size_iter->second;

// rules:
// * if it's kTableFile, then it's shared
// * if it's kDescriptorFile, limit the size to manifest_file_size
s = AddBackupFileWorkItem(
live_dst_paths, backup_items_to_finish, new_backup_id,
options_.share_table_files && type == kTableFile, db->GetName(),
live_files[i], rate_limiter, size_bytes,
(type == kDescriptorFile) ? manifest_file_size : 0,
options_.share_files_with_checksum && type == kTableFile,
progress_callback);
}
if (s.ok() && !current_fname.empty() && !manifest_fname.empty()) {
// Write the current file with the manifest filename as its contents.
s = AddBackupFileWorkItem(
live_dst_paths, backup_items_to_finish, new_backup_id,
false /* shared */, "" /* src_dir */, CurrentFileName(""), rate_limiter,
manifest_fname.size(), 0 /* size_limit */, false /* shared_checksum */,
progress_callback, manifest_fname.substr(1) + "\n");
}
ROCKS_LOG_INFO(options_.info_log,
"begin add wal files for backup -- %" ROCKSDB_PRIszt,
live_wal_files.size());
// Add a CopyOrCreateWorkItem to the channel for each WAL file
for (size_t i = 0; s.ok() && i < live_wal_files.size(); ++i) {
uint64_t size_bytes = live_wal_files[i]->SizeFileBytes();
if (live_wal_files[i]->Type() == kAliveLogFile) {
ROCKS_LOG_INFO(options_.info_log,
"add wal file for backup %s -- %" PRIu64,
live_wal_files[i]->PathName().c_str(), size_bytes);
// we only care about live log files
// copy the file into backup_dir/files/<new backup>/
s = AddBackupFileWorkItem(live_dst_paths, backup_items_to_finish,
new_backup_id, false, /* not shared */
db->GetOptions().wal_dir,
live_wal_files[i]->PathName(), rate_limiter,
size_bytes, size_bytes);
db->DisableFileDeletions();
if (s.ok()) {
CheckpointImpl checkpoint(db);
uint64_t sequence_number = 0;
s = checkpoint.CreateCustomCheckpoint(
db->GetDBOptions(),
[&](const std::string& src_dirname, const std::string& fname,
FileType) {
// custom checkpoint will switch to calling copy_file_cb after it sees
// NotSupported returned from link_file_cb.
return Status::NotSupported();
} /* link_file_cb */,
[&](const std::string& src_dirname, const std::string& fname,
uint64_t size_limit_bytes, FileType type) {
if (type == kLogFile && !options_.backup_log_files) {
return Status::OK();
}
Log(options_.info_log, "add file for backup %s", fname.c_str());
uint64_t size_bytes = 0;
Status st;
if (type == kTableFile) {
st = db_env_->GetFileSize(src_dirname + fname, &size_bytes);
}
if (st.ok()) {
st = AddBackupFileWorkItem(
live_dst_paths, backup_items_to_finish, new_backup_id,
options_.share_table_files && type == kTableFile, src_dirname,
fname, rate_limiter, size_bytes, size_limit_bytes,
options_.share_files_with_checksum && type == kTableFile,
progress_callback);
}
return st;
} /* copy_file_cb */,
[&](const std::string& fname, const std::string& contents, FileType) {
Log(options_.info_log, "add file for backup %s", fname.c_str());
return AddBackupFileWorkItem(
live_dst_paths, backup_items_to_finish, new_backup_id,
false /* shared */, "" /* src_dir */, fname, rate_limiter,
contents.size(), 0 /* size_limit */, false /* shared_checksum */,
progress_callback, contents);
} /* create_file_cb */,
&sequence_number, flush_before_backup ? 0 : port::kMaxUint64);
if (s.ok()) {
new_backup->SetSequenceNumber(sequence_number);
}
}
ROCKS_LOG_INFO(options_.info_log, "add files for backup done, wait finish.");
Expand Down
35 changes: 30 additions & 5 deletions utilities/backupable/backupable_db_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ class DummyDB : public StackableDB {
return options_;
}

virtual DBOptions GetDBOptions() const override {
return DBOptions(options_);
}

virtual Status EnableFileDeletions(bool force) override {
EXPECT_TRUE(!deletions_enabled_);
deletions_enabled_ = true;
Expand Down Expand Up @@ -106,9 +110,9 @@ class DummyDB : public StackableDB {
}

virtual SequenceNumber StartSequence() const override {
// backupabledb should not need this method
EXPECT_TRUE(false);
return 0;
// this seqnum guarantees the dummy file will be included in the backup
// as long as it is alive.
return kMaxSequenceNumber;
}

virtual uint64_t SizeFileBytes() const override {
Expand Down Expand Up @@ -204,6 +208,14 @@ class TestEnv : public EnvWrapper {
return EnvWrapper::DeleteFile(fname);
}

virtual Status DeleteDir(const std::string& dirname) override {
MutexLock l(&mutex_);
if (fail_delete_files_) {
return Status::IOError();
}
return EnvWrapper::DeleteDir(dirname);
}

void AssertWrittenFiles(std::vector<std::string>& should_have_written) {
MutexLock l(&mutex_);
std::sort(should_have_written.begin(), should_have_written.end());
Expand Down Expand Up @@ -266,6 +278,19 @@ class TestEnv : public EnvWrapper {
}
return EnvWrapper::GetChildrenFileAttributes(dir, r);
}
Status GetFileSize(const std::string& path, uint64_t* size_bytes) override {
if (filenames_for_mocked_attrs_.size() > 0) {
auto fname = path.substr(path.find_last_of('/'));
auto filename_iter = std::find(filenames_for_mocked_attrs_.begin(),
filenames_for_mocked_attrs_.end(), fname);
if (filename_iter != filenames_for_mocked_attrs_.end()) {
*size_bytes = 10;
return Status::OK();
}
return Status::NotFound(fname);
}
return EnvWrapper::GetFileSize(path, size_bytes);
}

void SetCreateDirIfMissingFailure(bool fail) {
create_dir_if_missing_failure_ = fail;
Expand Down Expand Up @@ -1374,10 +1399,10 @@ TEST_F(BackupableDBTest, ChangeManifestDuringBackupCreation) {
FillDB(db_.get(), 0, 100);

rocksdb::SyncPoint::GetInstance()->LoadDependency({
{"BackupEngineImpl::CreateNewBackup:SavedLiveFiles1",
{"CheckpointImpl::CreateCheckpoint:SavedLiveFiles1",
"VersionSet::LogAndApply:WriteManifest"},
{"VersionSet::LogAndApply:WriteManifestDone",
"BackupEngineImpl::CreateNewBackup:SavedLiveFiles2"},
"CheckpointImpl::CreateCheckpoint:SavedLiveFiles2"},
});
rocksdb::SyncPoint::GetInstance()->EnableProcessing();

Expand Down
Loading

0 comments on commit e5e545a

Please sign in to comment.