ASSERT_EQ(expected_manifest, manifest_cnt);
}
+TEST_F(DBSecondaryTest, NonExistingDb) {
+ Destroy(last_options_);
+
+ Options options = GetDefaultOptions();
+ options.env = env_;
+ options.max_open_files = -1;
+ const std::string dbname = "/doesnt/exist";
+ Status s =
+ DB::OpenAsSecondary(options, dbname, secondary_path_, &db_secondary_);
+ ASSERT_TRUE(s.IsIOError());
+}
+
TEST_F(DBSecondaryTest, ReopenAsSecondary) {
Options options;
options.env = env_;
SyncPoint::GetInstance()->LoadDependency(
{{"ReactiveVersionSet::MaybeSwitchManifest:AfterGetCurrentManifestPath:0",
"VersionSet::ProcessManifestWrites:BeforeNewManifest"},
- {"VersionSet::ProcessManifestWrites:AfterNewManifest",
+ {"DBImpl::Open:AfterDeleteFilesAndSyncDir",
"ReactiveVersionSet::MaybeSwitchManifest:AfterGetCurrentManifestPath:"
"1"}});
SyncPoint::GetInstance()->EnableProcessing();
- // Make sure db calls RecoverLogFiles so as to trigger a manifest write,
- // which causes the db to switch to a new MANIFEST upon start.
port::Thread ro_db_thread([&]() {
Options options1;
options1.env = env_;
options1.max_open_files = -1;
+ Status s = TryOpenSecondary(options1);
+ ASSERT_TRUE(s.IsTryAgain());
+
+ // Try again
OpenSecondary(options1);
CloseSecondary();
});
if (!io_s.ok()) {
s = io_s;
}
- TEST_SYNC_POINT("VersionSet::ProcessManifestWrites:AfterNewManifest");
}
if (s.ok()) {
static_cast_with_check<LogReporter>(manifest_reporter->get())->status =
manifest_reader_status->get();
Status s = MaybeSwitchManifest(manifest_reporter->get(), manifest_reader);
+ if (!s.ok()) {
+ return s;
+ }
log::Reader* reader = manifest_reader->get();
assert(reader);
std::unique_ptr<log::FragmentBufferedReader>* manifest_reader) {
assert(manifest_reader != nullptr);
Status s;
- do {
- std::string manifest_path;
- s = GetCurrentManifestPath(dbname_, fs_.get(), &manifest_path,
- &manifest_file_number_);
- std::unique_ptr<FSSequentialFile> manifest_file;
- if (s.ok()) {
- if (nullptr == manifest_reader->get() ||
- manifest_reader->get()->file()->file_name() != manifest_path) {
- TEST_SYNC_POINT(
- "ReactiveVersionSet::MaybeSwitchManifest:"
- "AfterGetCurrentManifestPath:0");
- TEST_SYNC_POINT(
- "ReactiveVersionSet::MaybeSwitchManifest:"
- "AfterGetCurrentManifestPath:1");
- s = fs_->NewSequentialFile(manifest_path,
- fs_->OptimizeForManifestRead(file_options_),
- &manifest_file, nullptr);
- } else {
- // No need to switch manifest.
- break;
- }
- }
- std::unique_ptr<SequentialFileReader> manifest_file_reader;
- if (s.ok()) {
- manifest_file_reader.reset(new SequentialFileReader(
- std::move(manifest_file), manifest_path,
- db_options_->log_readahead_size, io_tracer_));
- manifest_reader->reset(new log::FragmentBufferedReader(
- nullptr, std::move(manifest_file_reader), reporter,
- true /* checksum */, 0 /* log_number */));
- ROCKS_LOG_INFO(db_options_->info_log, "Switched to new manifest: %s\n",
- manifest_path.c_str());
- if (manifest_tailer_) {
- manifest_tailer_->PrepareToReadNewManifest();
- }
- }
- } while (s.IsPathNotFound());
+ std::string manifest_path;
+ s = GetCurrentManifestPath(dbname_, fs_.get(), &manifest_path,
+ &manifest_file_number_);
+ if (!s.ok()) {
+ return s;
+ }
+ std::unique_ptr<FSSequentialFile> manifest_file;
+ if (manifest_reader->get() != nullptr &&
+ manifest_reader->get()->file()->file_name() == manifest_path) {
+ // CURRENT points to the same MANIFEST as before, no need to switch
+ // MANIFEST.
+ return s;
+ }
+ assert(nullptr == manifest_reader->get() ||
+ manifest_reader->get()->file()->file_name() != manifest_path);
+ s = fs_->FileExists(manifest_path, IOOptions(), nullptr);
+ if (s.IsNotFound()) {
+ return Status::TryAgain(
+ "The primary may have switched to a new MANIFEST and deleted the old "
+ "one.");
+ } else if (!s.ok()) {
+ return s;
+ }
+ TEST_SYNC_POINT(
+ "ReactiveVersionSet::MaybeSwitchManifest:"
+ "AfterGetCurrentManifestPath:0");
+ TEST_SYNC_POINT(
+ "ReactiveVersionSet::MaybeSwitchManifest:"
+ "AfterGetCurrentManifestPath:1");
+ // The primary can also delete the MANIFEST while the secondary is reading
+ // it. This is OK on POSIX. For other file systems, maybe create a hard link
+ // to MANIFEST. The hard link should be cleaned up later by the secondary.
+ s = fs_->NewSequentialFile(manifest_path,
+ fs_->OptimizeForManifestRead(file_options_),
+ &manifest_file, nullptr);
+ std::unique_ptr<SequentialFileReader> manifest_file_reader;
+ if (s.ok()) {
+ manifest_file_reader.reset(
+ new SequentialFileReader(std::move(manifest_file), manifest_path,
+ db_options_->log_readahead_size, io_tracer_));
+ manifest_reader->reset(new log::FragmentBufferedReader(
+ nullptr, std::move(manifest_file_reader), reporter, true /* checksum */,
+ 0 /* log_number */));
+ ROCKS_LOG_INFO(db_options_->info_log, "Switched to new manifest: %s\n",
+ manifest_path.c_str());
+ if (manifest_tailer_) {
+ manifest_tailer_->PrepareToReadNewManifest();
+ }
+ } else if (s.IsPathNotFound()) {
+ // This can happen if the primary switches to a new MANIFEST after the
+ // secondary reads the CURRENT file but before the secondary actually tries
+ // to open the MANIFEST.
+ s = Status::TryAgain(
+ "The primary may have switched to a new MANIFEST and deleted the old "
+ "one.");
+ }
return s;
}