From 9bfd46d0d8ce4536823c0a7e3659eda82d3d2aef Mon Sep 17 00:00:00 2001 From: Yanqin Jin Date: Mon, 15 Jun 2020 14:09:53 -0700 Subject: [PATCH] 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 --- HISTORY.md | 4 ++++ db/db_basic_test.cc | 29 +++++++++++++++++++++++++++++ db/db_impl/db_impl_open.cc | 33 ++++++++++++++++++++++++++++----- 3 files changed, 61 insertions(+), 5 deletions(-) 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.");