mirror of https://github.com/facebook/rocksdb.git
Let best-efforts recovery ignore CURRENT file (#6970)
Summary: Best-efforts recovery does not check the content of CURRENT file to determine which MANIFEST to recover from. However, it still checks the presence of CURRENT file to determine whether to create a new DB during `open()`. Therefore, we can tweak the logic in `open()` a little bit so that best-efforts recovery does not rely on CURRENT file at all. Test plan (dev server): make check ./db_basic_test --gtest_filter=DBBasicTest.RecoverWithNoCurrentFile Pull Request resolved: https://github.com/facebook/rocksdb/pull/6970 Reviewed By: anand1976 Differential Revision: D22013990 Pulled By: riversand963 fbshipit-source-id: db552a1868c60ed70e1f7cd252a3a076eb8ea58f
This commit is contained in:
parent
aa8f1331af
commit
9bfd46d0d8
|
@ -1,4 +1,8 @@
|
||||||
# Rocksdb Change Log
|
# Rocksdb Change Log
|
||||||
|
## Unreleased
|
||||||
|
### Behavior Changes
|
||||||
|
* Best-efforts recovery ignores CURRENT file completely. If CURRENT file is missing during recovery, best-efforts recovery still proceeds with MANIFEST file(s).
|
||||||
|
|
||||||
## 6.11 (6/12/2020)
|
## 6.11 (6/12/2020)
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
* Fix consistency checking error swallowing in some cases when options.force_consistency_checks = true.
|
* Fix consistency checking error swallowing in some cases when options.force_consistency_checks = true.
|
||||||
|
|
|
@ -2249,6 +2249,35 @@ TEST_F(DBBasicTest, RecoverWithNoCurrentFile) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DBBasicTest, RecoverWithNoManifest) {
|
||||||
|
Options options = CurrentOptions();
|
||||||
|
options.env = env_;
|
||||||
|
DestroyAndReopen(options);
|
||||||
|
ASSERT_OK(Put("foo", "value"));
|
||||||
|
ASSERT_OK(Flush());
|
||||||
|
Close();
|
||||||
|
{
|
||||||
|
// Delete all MANIFEST.
|
||||||
|
std::vector<std::string> files;
|
||||||
|
ASSERT_OK(env_->GetChildren(dbname_, &files));
|
||||||
|
for (const auto& file : files) {
|
||||||
|
uint64_t number = 0;
|
||||||
|
FileType type = kLogFile;
|
||||||
|
if (ParseFileName(file, &number, &type) && type == kDescriptorFile) {
|
||||||
|
ASSERT_OK(env_->DeleteFile(dbname_ + "/" + file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
options.best_efforts_recovery = true;
|
||||||
|
options.create_if_missing = false;
|
||||||
|
Status s = TryReopen(options);
|
||||||
|
ASSERT_TRUE(s.IsInvalidArgument());
|
||||||
|
options.create_if_missing = true;
|
||||||
|
Reopen(options);
|
||||||
|
// Since no MANIFEST exists, best-efforts recovery creates a new, empty db.
|
||||||
|
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(DBBasicTest, SkipWALIfMissingTableFiles) {
|
TEST_F(DBBasicTest, SkipWALIfMissingTableFiles) {
|
||||||
Options options = CurrentOptions();
|
Options options = CurrentOptions();
|
||||||
DestroyAndReopen(options);
|
DestroyAndReopen(options);
|
||||||
|
|
|
@ -370,7 +370,30 @@ Status DBImpl::Recover(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string current_fname = CurrentFileName(dbname_);
|
std::string current_fname = CurrentFileName(dbname_);
|
||||||
s = env_->FileExists(current_fname);
|
// Path to any MANIFEST file in the db dir. It does not matter which one.
|
||||||
|
// Since best-efforts recovery ignores CURRENT file, existence of a
|
||||||
|
// MANIFEST indicates the recovery to recover existing db. If no MANIFEST
|
||||||
|
// can be found, a new db will be created.
|
||||||
|
std::string manifest_path;
|
||||||
|
if (!immutable_db_options_.best_efforts_recovery) {
|
||||||
|
s = env_->FileExists(current_fname);
|
||||||
|
} else {
|
||||||
|
s = Status::NotFound();
|
||||||
|
std::vector<std::string> files;
|
||||||
|
// No need to check return value
|
||||||
|
env_->GetChildren(dbname_, &files);
|
||||||
|
for (const std::string& file : files) {
|
||||||
|
uint64_t number = 0;
|
||||||
|
FileType type = kLogFile; // initialize
|
||||||
|
if (ParseFileName(file, &number, &type) && type == kDescriptorFile) {
|
||||||
|
// Found MANIFEST (descriptor log), thus best-efforts recovery does
|
||||||
|
// not have to treat the db as empty.
|
||||||
|
s = Status::OK();
|
||||||
|
manifest_path = dbname_ + "/" + file;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (s.IsNotFound()) {
|
if (s.IsNotFound()) {
|
||||||
if (immutable_db_options_.create_if_missing) {
|
if (immutable_db_options_.create_if_missing) {
|
||||||
s = NewDB();
|
s = NewDB();
|
||||||
|
@ -398,14 +421,14 @@ Status DBImpl::Recover(
|
||||||
FileOptions customized_fs(file_options_);
|
FileOptions customized_fs(file_options_);
|
||||||
customized_fs.use_direct_reads |=
|
customized_fs.use_direct_reads |=
|
||||||
immutable_db_options_.use_direct_io_for_flush_and_compaction;
|
immutable_db_options_.use_direct_io_for_flush_and_compaction;
|
||||||
s = fs_->NewRandomAccessFile(current_fname, customized_fs, &idfile,
|
const std::string& fname =
|
||||||
nullptr);
|
manifest_path.empty() ? current_fname : manifest_path;
|
||||||
|
s = fs_->NewRandomAccessFile(fname, customized_fs, &idfile, nullptr);
|
||||||
if (!s.ok()) {
|
if (!s.ok()) {
|
||||||
std::string error_str = s.ToString();
|
std::string error_str = s.ToString();
|
||||||
// Check if unsupported Direct I/O is the root cause
|
// Check if unsupported Direct I/O is the root cause
|
||||||
customized_fs.use_direct_reads = false;
|
customized_fs.use_direct_reads = false;
|
||||||
s = fs_->NewRandomAccessFile(current_fname, customized_fs, &idfile,
|
s = fs_->NewRandomAccessFile(fname, customized_fs, &idfile, nullptr);
|
||||||
nullptr);
|
|
||||||
if (s.ok()) {
|
if (s.ok()) {
|
||||||
return Status::InvalidArgument(
|
return Status::InvalidArgument(
|
||||||
"Direct I/O is not supported by the specified DB.");
|
"Direct I/O is not supported by the specified DB.");
|
||||||
|
|
Loading…
Reference in New Issue