diff --git a/HISTORY.md b/HISTORY.md index 429c891db5..0ead41a22e 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,12 +1,4 @@ # Rocksdb Change Log -## Unreleased -### New Features - -### Public API Change - -### Bug Fixes -* Fix a deadlock caused by compaction and file ingestion waiting for each other in the event of write stalls. - ## 5.18.0 (11/30/2018) ### New Features * Introduced `JemallocNodumpAllocator` memory allocator. When being use, block cache will be excluded from core dump. @@ -18,6 +10,7 @@ * Add xxhash64 checksum support * Introduced `MemoryAllocator`, which lets the user specify custom memory allocator for block based table. * Improved `DeleteRange` to prevent read performance degradation. The feature is no longer marked as experimental. +* Enabled checkpoint on readonly db (DBImplReadOnly). ### Public API Change * `DBOptions::use_direct_reads` now affects reads issued by `BackupEngine` on the database's SSTs. @@ -34,6 +27,7 @@ * Fixed Get correctness bug in the presence of range tombstones where merge operands covered by a range tombstone always result in NotFound. * Start populating `NO_FILE_CLOSES` ticker statistic, which was always zero previously. * The default value of NewBloomFilterPolicy()'s argument use_block_based_builder is changed to false. Note that this new default may cause large temp memory usage when building very large SST files. +* Fix a deadlock caused by compaction and file ingestion waiting for each other in the event of write stalls. ## 5.17.0 (10/05/2018) ### Public API Change diff --git a/db/compacted_db_impl.h b/db/compacted_db_impl.h index 736002e1e5..5c574b4b9a 100644 --- a/db/compacted_db_impl.h +++ b/db/compacted_db_impl.h @@ -67,10 +67,11 @@ class CompactedDBImpl : public DBImpl { virtual Status EnableFileDeletions(bool /*force*/) override { return Status::NotSupported("Not supported in compacted db mode."); } - virtual Status GetLiveFiles(std::vector&, - uint64_t* /*manifest_file_size*/, - bool /*flush_memtable*/ = true) override { - return Status::NotSupported("Not supported in compacted db mode."); + virtual Status GetLiveFiles(std::vector& ret, + uint64_t* manifest_file_size, + bool /*flush_memtable*/) override { + return DBImpl::GetLiveFiles(ret, manifest_file_size, + false /* flush_memtable */); } using DBImpl::Flush; virtual Status Flush(const FlushOptions& /*options*/, diff --git a/db/db_impl_open.cc b/db/db_impl_open.cc index 5ea8c61b50..5196be7ba4 100644 --- a/db/db_impl_open.cc +++ b/db/db_impl_open.cc @@ -474,6 +474,28 @@ Status DBImpl::Recover( } } + if (read_only) { + // If we are opening as read-only, we need to update options_file_number_ + // to reflect the most recent OPTIONS file. It does not matter for regular + // read-write db instance because options_file_number_ will later be + // updated to versions_->NewFileNumber() in RenameTempFileToOptionsFile. + std::vector file_names; + if (s.ok()) { + s = env_->GetChildren(GetName(), &file_names); + } + if (s.ok()) { + uint64_t number = 0; + uint64_t options_file_number = 0; + FileType type; + for (const auto& fname : file_names) { + if (ParseFileName(fname, &number, &type) && type == kOptionsFile) { + options_file_number = std::max(number, options_file_number); + } + } + versions_->options_file_number_ = options_file_number; + } + } + return s; } diff --git a/db/db_impl_readonly.h b/db/db_impl_readonly.h index 6ebe1bce76..2d77dbac0e 100644 --- a/db/db_impl_readonly.h +++ b/db/db_impl_readonly.h @@ -89,10 +89,11 @@ class DBImplReadOnly : public DBImpl { virtual Status EnableFileDeletions(bool /*force*/) override { return Status::NotSupported("Not supported operation in read only mode."); } - virtual Status GetLiveFiles(std::vector&, - uint64_t* /*manifest_file_size*/, - bool /*flush_memtable*/ = true) override { - return Status::NotSupported("Not supported operation in read only mode."); + virtual Status GetLiveFiles(std::vector& ret, + uint64_t* manifest_file_size, + bool /*flush_memtable*/) override { + return DBImpl::GetLiveFiles(ret, manifest_file_size, + false /* flush_memtable */); } using DBImpl::Flush; diff --git a/utilities/checkpoint/checkpoint_test.cc b/utilities/checkpoint/checkpoint_test.cc index 62c78faa8b..b8436ccf59 100644 --- a/utilities/checkpoint/checkpoint_test.cc +++ b/utilities/checkpoint/checkpoint_test.cc @@ -164,6 +164,16 @@ class CheckpointTest : public testing::Test { return DB::OpenForReadOnly(options, dbname_, &db_); } + Status ReadOnlyReopenWithColumnFamilies(const std::vector& cfs, + const Options& options) { + std::vector column_families; + for (const auto& cf : cfs) { + column_families.emplace_back(cf, options); + } + return DB::OpenForReadOnly(options, dbname_, column_families, &handles_, + &db_); + } + Status TryReopen(const Options& options) { Close(); last_options_ = options; @@ -612,6 +622,69 @@ TEST_F(CheckpointTest, CheckpointWithUnsyncedDataDropped) { db_ = nullptr; } +TEST_F(CheckpointTest, CheckpointReadOnlyDB) { + ASSERT_OK(Put("foo", "foo_value")); + ASSERT_OK(Flush()); + Close(); + Options options = CurrentOptions(); + ASSERT_OK(ReadOnlyReopen(options)); + Checkpoint* checkpoint = nullptr; + ASSERT_OK(Checkpoint::Create(db_, &checkpoint)); + ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name_)); + delete checkpoint; + checkpoint = nullptr; + Close(); + DB* snapshot_db = nullptr; + ASSERT_OK(DB::Open(options, snapshot_name_, &snapshot_db)); + ReadOptions read_opts; + std::string get_result; + ASSERT_OK(snapshot_db->Get(read_opts, "foo", &get_result)); + ASSERT_EQ("foo_value", get_result); + delete snapshot_db; +} + +TEST_F(CheckpointTest, CheckpointReadOnlyDBWithMultipleColumnFamilies) { + Options options = CurrentOptions(); + CreateAndReopenWithCF({"pikachu", "eevee"}, options); + for (int i = 0; i != 3; ++i) { + ASSERT_OK(Put(i, "foo", "foo_value")); + ASSERT_OK(Flush(i)); + } + Close(); + Status s = ReadOnlyReopenWithColumnFamilies( + {kDefaultColumnFamilyName, "pikachu", "eevee"}, options); + ASSERT_OK(s); + Checkpoint* checkpoint = nullptr; + ASSERT_OK(Checkpoint::Create(db_, &checkpoint)); + ASSERT_OK(checkpoint->CreateCheckpoint(snapshot_name_)); + delete checkpoint; + checkpoint = nullptr; + Close(); + + std::vector column_families{ + {kDefaultColumnFamilyName, options}, + {"pikachu", options}, + {"eevee", options}}; + DB* snapshot_db = nullptr; + std::vector snapshot_handles; + s = DB::Open(options, snapshot_name_, column_families, &snapshot_handles, + &snapshot_db); + ASSERT_OK(s); + ReadOptions read_opts; + for (int i = 0; i != 3; ++i) { + std::string get_result; + s = snapshot_db->Get(read_opts, snapshot_handles[i], "foo", &get_result); + ASSERT_OK(s); + ASSERT_EQ("foo_value", get_result); + } + + for (auto snapshot_h : snapshot_handles) { + delete snapshot_h; + } + snapshot_handles.clear(); + delete snapshot_db; +} + } // namespace rocksdb int main(int argc, char** argv) {