mirror of https://github.com/facebook/rocksdb.git
Fail BackupEngine::Open upon meta-file read error
Summary: We used to treat any failure to read a backup's meta-file as if the backup were corrupted; however, we should distinguish corruption errors from errors in the backup Env. This fixes an issue where callers would get inconsistent results from GetBackupInfo() if they called it on an engine that encountered Env error during initialization. Now we fail Initialize() in this case so callers cannot invoke GetBackupInfo() on such engines. Closes https://github.com/facebook/rocksdb/pull/1654 Differential Revision: D4318573 Pulled By: ajkr fbshipit-source-id: f7a7c54
This commit is contained in:
parent
a79eae4b01
commit
83f9a6fd21
|
@ -613,11 +613,16 @@ Status BackupEngineImpl::Initialize() {
|
||||||
&abs_path_to_size);
|
&abs_path_to_size);
|
||||||
Status s =
|
Status s =
|
||||||
backup.second->LoadFromFile(options_.backup_dir, abs_path_to_size);
|
backup.second->LoadFromFile(options_.backup_dir, abs_path_to_size);
|
||||||
if (!s.ok()) {
|
if (s.IsCorruption()) {
|
||||||
Log(options_.info_log, "Backup %u corrupted -- %s", backup.first,
|
Log(options_.info_log, "Backup %u corrupted -- %s", backup.first,
|
||||||
s.ToString().c_str());
|
s.ToString().c_str());
|
||||||
corrupt_backups_.insert(std::make_pair(
|
corrupt_backups_.insert(std::make_pair(
|
||||||
backup.first, std::make_pair(s, std::move(backup.second))));
|
backup.first, std::make_pair(s, std::move(backup.second))));
|
||||||
|
} else if (!s.ok()) {
|
||||||
|
// Distinguish corruption errors from errors in the backup Env.
|
||||||
|
// Errors in the backup Env (i.e., this code path) will cause Open() to
|
||||||
|
// fail, whereas corruption errors would not cause Open() failures.
|
||||||
|
return s;
|
||||||
} else {
|
} else {
|
||||||
Log(options_.info_log, "Loading backup %" PRIu32 " OK:\n%s",
|
Log(options_.info_log, "Loading backup %" PRIu32 " OK:\n%s",
|
||||||
backup.first, backup.second->GetInfoString().c_str());
|
backup.first, backup.second->GetInfoString().c_str());
|
||||||
|
@ -1615,7 +1620,7 @@ Status BackupEngineImpl::BackupMeta::LoadFromFile(
|
||||||
try {
|
try {
|
||||||
size = abs_path_to_size.at(abs_path);
|
size = abs_path_to_size.at(abs_path);
|
||||||
} catch (std::out_of_range&) {
|
} catch (std::out_of_range&) {
|
||||||
return Status::NotFound("Size missing for pathname: " + abs_path);
|
return Status::Corruption("Size missing for pathname: " + abs_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,8 +148,12 @@ class TestEnv : public EnvWrapper {
|
||||||
|
|
||||||
class DummySequentialFile : public SequentialFile {
|
class DummySequentialFile : public SequentialFile {
|
||||||
public:
|
public:
|
||||||
DummySequentialFile() : SequentialFile(), rnd_(5) {}
|
explicit DummySequentialFile(bool fail_reads)
|
||||||
|
: SequentialFile(), rnd_(5), fail_reads_(fail_reads) {}
|
||||||
virtual Status Read(size_t n, Slice* result, char* scratch) override {
|
virtual Status Read(size_t n, Slice* result, char* scratch) override {
|
||||||
|
if (fail_reads_) {
|
||||||
|
return Status::IOError();
|
||||||
|
}
|
||||||
size_t read_size = (n > size_left) ? size_left : n;
|
size_t read_size = (n > size_left) ? size_left : n;
|
||||||
for (size_t i = 0; i < read_size; ++i) {
|
for (size_t i = 0; i < read_size; ++i) {
|
||||||
scratch[i] = rnd_.Next() & 255;
|
scratch[i] = rnd_.Next() & 255;
|
||||||
|
@ -166,13 +170,15 @@ class TestEnv : public EnvWrapper {
|
||||||
private:
|
private:
|
||||||
size_t size_left = 200;
|
size_t size_left = 200;
|
||||||
Random rnd_;
|
Random rnd_;
|
||||||
|
bool fail_reads_;
|
||||||
};
|
};
|
||||||
|
|
||||||
Status NewSequentialFile(const std::string& f, unique_ptr<SequentialFile>* r,
|
Status NewSequentialFile(const std::string& f, unique_ptr<SequentialFile>* r,
|
||||||
const EnvOptions& options) override {
|
const EnvOptions& options) override {
|
||||||
MutexLock l(&mutex_);
|
MutexLock l(&mutex_);
|
||||||
if (dummy_sequential_file_) {
|
if (dummy_sequential_file_) {
|
||||||
r->reset(new TestEnv::DummySequentialFile());
|
r->reset(
|
||||||
|
new TestEnv::DummySequentialFile(dummy_sequential_file_fail_reads_));
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
} else {
|
} else {
|
||||||
return EnvWrapper::NewSequentialFile(f, r, options);
|
return EnvWrapper::NewSequentialFile(f, r, options);
|
||||||
|
@ -224,6 +230,10 @@ class TestEnv : public EnvWrapper {
|
||||||
MutexLock l(&mutex_);
|
MutexLock l(&mutex_);
|
||||||
dummy_sequential_file_ = dummy_sequential_file;
|
dummy_sequential_file_ = dummy_sequential_file;
|
||||||
}
|
}
|
||||||
|
void SetDummySequentialFileFailReads(bool dummy_sequential_file_fail_reads) {
|
||||||
|
MutexLock l(&mutex_);
|
||||||
|
dummy_sequential_file_fail_reads_ = dummy_sequential_file_fail_reads;
|
||||||
|
}
|
||||||
|
|
||||||
void SetGetChildrenFailure(bool fail) { get_children_failure_ = fail; }
|
void SetGetChildrenFailure(bool fail) { get_children_failure_ = fail; }
|
||||||
Status GetChildren(const std::string& dir,
|
Status GetChildren(const std::string& dir,
|
||||||
|
@ -273,6 +283,7 @@ class TestEnv : public EnvWrapper {
|
||||||
private:
|
private:
|
||||||
port::Mutex mutex_;
|
port::Mutex mutex_;
|
||||||
bool dummy_sequential_file_ = false;
|
bool dummy_sequential_file_ = false;
|
||||||
|
bool dummy_sequential_file_fail_reads_ = false;
|
||||||
std::vector<std::string> written_files_;
|
std::vector<std::string> written_files_;
|
||||||
std::vector<std::string> filenames_for_mocked_attrs_;
|
std::vector<std::string> filenames_for_mocked_attrs_;
|
||||||
uint64_t limit_written_files_ = 1000000;
|
uint64_t limit_written_files_ = 1000000;
|
||||||
|
@ -1277,6 +1288,22 @@ TEST_F(BackupableDBTest, EnvFailures) {
|
||||||
test_backup_env_->SetNewDirectoryFailure(false);
|
test_backup_env_->SetNewDirectoryFailure(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read from meta-file failure
|
||||||
|
{
|
||||||
|
DestroyDB(dbname_, options_);
|
||||||
|
OpenDBAndBackupEngine(true);
|
||||||
|
FillDB(db_.get(), 0, 100);
|
||||||
|
ASSERT_TRUE(backup_engine_->CreateNewBackup(db_.get(), true).ok());
|
||||||
|
CloseDBAndBackupEngine();
|
||||||
|
test_backup_env_->SetDummySequentialFile(true);
|
||||||
|
test_backup_env_->SetDummySequentialFileFailReads(true);
|
||||||
|
backupable_options_->destroy_old_data = false;
|
||||||
|
ASSERT_NOK(BackupEngine::Open(test_db_env_.get(), *backupable_options_,
|
||||||
|
&backup_engine));
|
||||||
|
test_backup_env_->SetDummySequentialFile(false);
|
||||||
|
test_backup_env_->SetDummySequentialFileFailReads(false);
|
||||||
|
}
|
||||||
|
|
||||||
// no failure
|
// no failure
|
||||||
{
|
{
|
||||||
ASSERT_OK(BackupEngine::Open(test_db_env_.get(), *backupable_options_,
|
ASSERT_OK(BackupEngine::Open(test_db_env_.get(), *backupable_options_,
|
||||||
|
|
Loading…
Reference in New Issue