diff --git a/HISTORY.md b/HISTORY.md index acab10db7..d53198066 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,8 @@ # 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) ### Bug Fixes * Fix consistency checking error swallowing in some cases when options.force_consistency_checks = true. diff --git a/db/db_basic_test.cc b/db/db_basic_test.cc index b2b4ff92a..5d304fabf 100644 --- a/db/db_basic_test.cc +++ b/db/db_basic_test.cc @@ -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 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) { Options options = CurrentOptions(); DestroyAndReopen(options); diff --git a/db/db_impl/db_impl_open.cc b/db/db_impl/db_impl_open.cc index 2587d8cd5..dc83a0478 100644 --- a/db/db_impl/db_impl_open.cc +++ b/db/db_impl/db_impl_open.cc @@ -370,7 +370,30 @@ Status DBImpl::Recover( } 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 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 (immutable_db_options_.create_if_missing) { s = NewDB(); @@ -398,14 +421,14 @@ Status DBImpl::Recover( FileOptions customized_fs(file_options_); customized_fs.use_direct_reads |= immutable_db_options_.use_direct_io_for_flush_and_compaction; - s = fs_->NewRandomAccessFile(current_fname, customized_fs, &idfile, - nullptr); + const std::string& fname = + manifest_path.empty() ? current_fname : manifest_path; + s = fs_->NewRandomAccessFile(fname, customized_fs, &idfile, nullptr); if (!s.ok()) { std::string error_str = s.ToString(); // Check if unsupported Direct I/O is the root cause customized_fs.use_direct_reads = false; - s = fs_->NewRandomAccessFile(current_fname, customized_fs, &idfile, - nullptr); + s = fs_->NewRandomAccessFile(fname, customized_fs, &idfile, nullptr); if (s.ok()) { return Status::InvalidArgument( "Direct I/O is not supported by the specified DB.");