Backup Options

Summary: Backup options file to private directory

Test Plan:
backupable_db_test.cc, BackupOptions
	   Modify DB options by calling OpenDB for 3 times. Check the latest options file is in the right place. Also check no redundent files are backuped.

Reviewers: andrewkr

Reviewed By: andrewkr

Subscribers: leveldb, dhruba, andrewkr

Differential Revision: https://reviews.facebook.net/D59373
main
Wanning Jiang 9 years ago
parent a683d4aba9
commit 56887f6cb8
  1. 11
      db/db_filesnapshot.cc
  2. 4
      db/db_impl.cc
  3. 2
      db/db_impl.h
  4. 4
      db/db_test.cc
  5. 3
      db/version_set.h
  6. 12
      utilities/backupable/backupable_db.cc
  7. 60
      utilities/backupable/backupable_db_test.cc
  8. 4
      utilities/checkpoint/checkpoint.cc

@ -14,19 +14,20 @@
#endif #endif
#include <inttypes.h> #include <inttypes.h>
#include <stdint.h>
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include <stdint.h>
#include "db/db_impl.h" #include "db/db_impl.h"
#include "db/filename.h" #include "db/filename.h"
#include "db/job_context.h" #include "db/job_context.h"
#include "db/version_set.h" #include "db/version_set.h"
#include "port/port.h"
#include "rocksdb/db.h" #include "rocksdb/db.h"
#include "rocksdb/env.h" #include "rocksdb/env.h"
#include "port/port.h" #include "util/file_util.h"
#include "util/mutexlock.h" #include "util/mutexlock.h"
#include "util/sync_point.h" #include "util/sync_point.h"
#include "util/file_util.h" #include "util/testharness.h"
namespace rocksdb { namespace rocksdb {
@ -83,7 +84,6 @@ int DBImpl::IsFileDeletionsEnabled() const {
Status DBImpl::GetLiveFiles(std::vector<std::string>& ret, Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
uint64_t* manifest_file_size, uint64_t* manifest_file_size,
bool flush_memtable) { bool flush_memtable) {
*manifest_file_size = 0; *manifest_file_size = 0;
mutex_.Lock(); mutex_.Lock();
@ -126,7 +126,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
} }
ret.clear(); ret.clear();
ret.reserve(live.size() + 2); //*.sst + CURRENT + MANIFEST ret.reserve(live.size() + 3); // *.sst + CURRENT + MANIFEST + OPTIONS
// create names of the live files. The names are not absolute // create names of the live files. The names are not absolute
// paths, instead they are relative to dbname_; // paths, instead they are relative to dbname_;
@ -136,6 +136,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
ret.push_back(CurrentFileName("")); ret.push_back(CurrentFileName(""));
ret.push_back(DescriptorFileName("", versions_->manifest_file_number())); ret.push_back(DescriptorFileName("", versions_->manifest_file_number()));
ret.push_back(OptionsFileName("", versions_->options_file_number()));
// find length of manifest file while holding the mutex lock // find length of manifest file while holding the mutex lock
*manifest_file_size = versions_->manifest_file_size(); *manifest_file_size = versions_->manifest_file_size();

@ -6005,8 +6005,10 @@ Status DBImpl::DeleteObsoleteOptionsFiles() {
Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name) { Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name) {
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
Status s; Status s;
versions_->options_file_number_ = versions_->NewFileNumber();
std::string options_file_name = std::string options_file_name =
OptionsFileName(GetName(), versions_->NewFileNumber()); OptionsFileName(GetName(), versions_->options_file_number_);
// Retry if the file name happen to conflict with an existing one. // Retry if the file name happen to conflict with an existing one.
s = GetEnv()->RenameFile(file_name, options_file_name); s = GetEnv()->RenameFile(file_name, options_file_name);

@ -521,7 +521,7 @@ class DBImpl : public DB {
Compaction *c, const Status &st, Compaction *c, const Status &st,
const CompactionJobStats& job_stats, const CompactionJobStats& job_stats,
int job_id); int job_id);
void NotifyOnMemTableSealed(ColumnFamilyData* cfd, void NotifyOnMemTableSealed(ColumnFamilyData* cfd,
const MemTableInfo& mem_table_info); const MemTableInfo& mem_table_info);
void NewThreadStatusCfInfo(ColumnFamilyData* cfd) const; void NewThreadStatusCfInfo(ColumnFamilyData* cfd) const;

@ -2067,8 +2067,8 @@ TEST_F(DBTest, SnapshotFiles) {
dbfull()->DisableFileDeletions(); dbfull()->DisableFileDeletions();
dbfull()->GetLiveFiles(files, &manifest_size); dbfull()->GetLiveFiles(files, &manifest_size);
// CURRENT, MANIFEST, *.sst files (one for each CF) // CURRENT, MANIFEST, OPTIONS, *.sst files (one for each CF)
ASSERT_EQ(files.size(), 4U); ASSERT_EQ(files.size(), 5U);
uint64_t number = 0; uint64_t number = 0;
FileType type; FileType type;

@ -627,6 +627,8 @@ class VersionSet {
// Return the current manifest file number // Return the current manifest file number
uint64_t manifest_file_number() const { return manifest_file_number_; } uint64_t manifest_file_number() const { return manifest_file_number_; }
uint64_t options_file_number() const { return options_file_number_; }
uint64_t pending_manifest_file_number() const { uint64_t pending_manifest_file_number() const {
return pending_manifest_file_number_; return pending_manifest_file_number_;
} }
@ -743,6 +745,7 @@ class VersionSet {
const DBOptions* const db_options_; const DBOptions* const db_options_;
std::atomic<uint64_t> next_file_number_; std::atomic<uint64_t> next_file_number_;
uint64_t manifest_file_number_; uint64_t manifest_file_number_;
uint64_t options_file_number_;
uint64_t pending_manifest_file_number_; uint64_t pending_manifest_file_number_;
std::atomic<uint64_t> last_sequence_; std::atomic<uint64_t> last_sequence_;
uint64_t prev_log_number_; // 0 or backing store for memtable being compacted uint64_t prev_log_number_; // 0 or backing store for memtable being compacted

@ -29,17 +29,17 @@
#include <inttypes.h> #include <inttypes.h>
#include <stdlib.h> #include <stdlib.h>
#include <algorithm> #include <algorithm>
#include <vector> #include <atomic>
#include <future>
#include <limits>
#include <map> #include <map>
#include <mutex> #include <mutex>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <limits>
#include <atomic>
#include <future>
#include <thread> #include <thread>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector>
namespace rocksdb { namespace rocksdb {
@ -694,6 +694,7 @@ Status BackupEngineImpl::CreateNewBackupWithMetadata(
TEST_SYNC_POINT("BackupEngineImpl::CreateNewBackup:SavedLiveFiles2"); TEST_SYNC_POINT("BackupEngineImpl::CreateNewBackup:SavedLiveFiles2");
BackupID new_backup_id = latest_backup_id_ + 1; BackupID new_backup_id = latest_backup_id_ + 1;
assert(backups_.find(new_backup_id) == backups_.end()); assert(backups_.find(new_backup_id) == backups_.end());
auto ret = backups_.insert( auto ret = backups_.insert(
std::make_pair(new_backup_id, unique_ptr<BackupMeta>(new BackupMeta( std::make_pair(new_backup_id, unique_ptr<BackupMeta>(new BackupMeta(
@ -745,7 +746,8 @@ Status BackupEngineImpl::CreateNewBackupWithMetadata(
} }
// we should only get sst, manifest and current files here // we should only get sst, manifest and current files here
assert(type == kTableFile || type == kDescriptorFile || assert(type == kTableFile || type == kDescriptorFile ||
type == kCurrentFile); type == kCurrentFile || type == kOptionsFile);
if (type == kCurrentFile) { if (type == kCurrentFile) {
// We will craft the current file manually to ensure it's consistent with // We will craft the current file manually to ensure it's consistent with
// the manifest number. This is necessary because current's file contents // the manifest number. This is necessary because current's file contents

@ -20,10 +20,12 @@
#include "rocksdb/transaction_log.h" #include "rocksdb/transaction_log.h"
#include "rocksdb/types.h" #include "rocksdb/types.h"
#include "rocksdb/utilities/backupable_db.h" #include "rocksdb/utilities/backupable_db.h"
#include "rocksdb/utilities/options_util.h"
#include "util/env_chroot.h" #include "util/env_chroot.h"
#include "util/file_reader_writer.h" #include "util/file_reader_writer.h"
#include "util/mutexlock.h" #include "util/mutexlock.h"
#include "util/random.h" #include "util/random.h"
#include "util/stderr_logger.h"
#include "util/string_util.h" #include "util/string_util.h"
#include "util/sync_point.h" #include "util/sync_point.h"
#include "util/testharness.h" #include "util/testharness.h"
@ -200,6 +202,7 @@ class TestEnv : public EnvWrapper {
MutexLock l(&mutex_); MutexLock l(&mutex_);
std::sort(should_have_written.begin(), should_have_written.end()); std::sort(should_have_written.begin(), should_have_written.end());
std::sort(written_files_.begin(), written_files_.end()); std::sort(written_files_.begin(), written_files_.end());
ASSERT_TRUE(written_files_ == should_have_written); ASSERT_TRUE(written_files_ == should_have_written);
} }
@ -756,8 +759,8 @@ TEST_F(BackupableDBTest, NoDoubleCopy) {
test_backup_env_->SetLimitWrittenFiles(7); test_backup_env_->SetLimitWrittenFiles(7);
test_backup_env_->ClearWrittenFiles(); test_backup_env_->ClearWrittenFiles();
test_db_env_->SetLimitWrittenFiles(0); test_db_env_->SetLimitWrittenFiles(0);
dummy_db_->live_files_ = { "/00010.sst", "/00011.sst", dummy_db_->live_files_ = {"/00010.sst", "/00011.sst", "/CURRENT",
"/CURRENT", "/MANIFEST-01" }; "/MANIFEST-01"};
dummy_db_->wal_files_ = {{"/00011.log", true}, {"/00012.log", false}}; dummy_db_->wal_files_ = {{"/00011.log", true}, {"/00012.log", false}};
test_db_env_->SetFilenamesForMockedAttrs(dummy_db_->live_files_); test_db_env_->SetFilenamesForMockedAttrs(dummy_db_->live_files_);
ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), false)); ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), false));
@ -773,20 +776,20 @@ TEST_F(BackupableDBTest, NoDoubleCopy) {
// should not write/copy 00010.sst, since it's already there! // should not write/copy 00010.sst, since it's already there!
test_backup_env_->SetLimitWrittenFiles(6); test_backup_env_->SetLimitWrittenFiles(6);
test_backup_env_->ClearWrittenFiles(); test_backup_env_->ClearWrittenFiles();
dummy_db_->live_files_ = { "/00010.sst", "/00015.sst",
"/CURRENT", "/MANIFEST-01" }; dummy_db_->live_files_ = {"/00010.sst", "/00015.sst", "/CURRENT",
"/MANIFEST-01"};
dummy_db_->wal_files_ = {{"/00011.log", true}, {"/00012.log", false}}; dummy_db_->wal_files_ = {{"/00011.log", true}, {"/00012.log", false}};
test_db_env_->SetFilenamesForMockedAttrs(dummy_db_->live_files_); test_db_env_->SetFilenamesForMockedAttrs(dummy_db_->live_files_);
ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), false)); ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), false));
// should not open 00010.sst - it's already there // should not open 00010.sst - it's already there
should_have_written = {
"/shared/00015.sst.tmp", should_have_written = {"/shared/00015.sst.tmp",
"/private/2.tmp/CURRENT", "/private/2.tmp/CURRENT",
"/private/2.tmp/MANIFEST-01", "/private/2.tmp/MANIFEST-01",
"/private/2.tmp/00011.log", "/private/2.tmp/00011.log",
"/meta/2.tmp", "/meta/2.tmp",
"/LATEST_BACKUP.tmp" "/LATEST_BACKUP.tmp"};
};
AppendPath(backupdir_, should_have_written); AppendPath(backupdir_, should_have_written);
test_backup_env_->AssertWrittenFiles(should_have_written); test_backup_env_->AssertWrittenFiles(should_have_written);
@ -943,6 +946,39 @@ TEST_F(BackupableDBTest, CorruptionsTest) {
AssertBackupConsistency(2, 0, keys_iteration * 2, keys_iteration * 5); AssertBackupConsistency(2, 0, keys_iteration * 2, keys_iteration * 5);
} }
inline std::string OptionsPath(std::string ret, int backupID) {
ret += "/private/";
ret += std::to_string(backupID);
ret += "/";
return ret;
}
// Backup the LATEST options file to
// "<backup_dir>/private/<backup_id>/OPTIONS<number>"
TEST_F(BackupableDBTest, BackupOptions) {
OpenDBAndBackupEngine(true);
for (int i = 1; i < 5; i++) {
std::string name;
std::vector<std::string> filenames;
// Must reset() before reset(OpenDB()) again.
// Calling OpenDB() while *db_ is existing will cause LOCK issue
db_.reset();
db_.reset(OpenDB());
ASSERT_OK(backup_engine_->CreateNewBackup(db_.get(), true));
rocksdb::GetLatestOptionsFileName(db_->GetName(), options_.env, &name);
ASSERT_OK(file_manager_->FileExists(OptionsPath(backupdir_, i) + name));
backup_chroot_env_->GetChildren(OptionsPath(backupdir_, i), &filenames);
for (auto fn : filenames) {
if (fn.compare(0, 7, "OPTIONS") == 0) {
ASSERT_EQ(name, fn);
}
}
}
CloseDBAndBackupEngine();
}
// This test verifies we don't delete the latest backup when read-only option is // This test verifies we don't delete the latest backup when read-only option is
// set // set
TEST_F(BackupableDBTest, NoDeleteWithReadOnly) { TEST_F(BackupableDBTest, NoDeleteWithReadOnly) {

@ -110,9 +110,9 @@ Status CheckpointImpl::CreateCheckpoint(const std::string& checkpoint_dir) {
s = Status::Corruption("Can't parse file name. This is very bad"); s = Status::Corruption("Can't parse file name. This is very bad");
break; break;
} }
// we should only get sst, manifest and current files here // we should only get sst, options, manifest and current files here
assert(type == kTableFile || type == kDescriptorFile || assert(type == kTableFile || type == kDescriptorFile ||
type == kCurrentFile); type == kCurrentFile || type == kOptionsFile);
assert(live_files[i].size() > 0 && live_files[i][0] == '/'); assert(live_files[i].size() > 0 && live_files[i][0] == '/');
if (type == kCurrentFile) { if (type == kCurrentFile) {
// We will craft the current file manually to ensure it's consistent with // We will craft the current file manually to ensure it's consistent with

Loading…
Cancel
Save