diff --git a/HISTORY.md b/HISTORY.md index 6b287d581..64458d95f 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -12,6 +12,7 @@ * Fix false negative from the VerifyChecksum() API when there is a checksum mismatch in an index partition block in a BlockBasedTable format table file (index_type is kTwoLevelIndexSearch). * Fix sst_dump to return non-zero exit code if the specified file is not a recognized SST file or fails requested checks. * Fix incorrect results from batched MultiGet for duplicate keys, when the duplicate key matches the largest key of an SST file and the value type for the key in the file is a merge value. +* Best-efforts recovery ignores CURRENT file completely. If CURRENT file is missing during recovery, best-efforts recovery still proceeds with MANIFEST file(s). ### Public API Change * Flush(..., column_family) may return Status::ColumnFamilyDropped() instead of Status::InvalidArgument() if column_family is dropped while processing the flush request. diff --git a/db/db_basic_test.cc b/db/db_basic_test.cc index 3544400ba..9d47b8b4e 100644 --- a/db/db_basic_test.cc +++ b/db/db_basic_test.cc @@ -2199,6 +2199,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.");