From d4b0ac27604577075fc5f6e272a1340c6eaf6860 Mon Sep 17 00:00:00 2001 From: Islam AbdelRahman Date: Wed, 25 Jan 2017 14:03:49 -0800 Subject: [PATCH] Fix CompactFiles() bug when used with CompactionFilter using SuperVersion Summary: GetAndRefSuperVersion() should not be called again in the same thread before ReturnAndCleanupSuperVersion() is called. If we have a compaction filter that is using DB::Get, This will happen ``` CompactFiles() { GetAndRefSuperVersion() // -- first call .. CompactionFilter() { GetAndRefSuperVersion() // -- second call ReturnAndCleanupSuperVersion() } .. ReturnAndCleanupSuperVersion() } ``` We solve this issue in the same way Iterator is solving it, but using GetReferencedSuperVersion() This was discovered in https://github.com/facebook/mysql-5.6/issues/427 by alxyang Closes https://github.com/facebook/rocksdb/pull/1803 Differential Revision: D4460155 Pulled By: IslamAbdelRahman fbshipit-source-id: 5e54322 --- db/compact_files_test.cc | 55 ++++++++++++++++++++++++++++++++++++++++ db/db_impl.cc | 9 +++++-- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/db/compact_files_test.cc b/db/compact_files_test.cc index 051cca8a4..76042b6e3 100644 --- a/db/compact_files_test.cc +++ b/db/compact_files_test.cc @@ -253,6 +253,61 @@ TEST_F(CompactFilesTest, CapturingPendingFiles) { delete db; } +TEST_F(CompactFilesTest, CompactionFilterWithGetSv) { + class FilterWithGet : public CompactionFilter { + public: + virtual bool Filter(int level, const Slice& key, const Slice& value, + std::string* new_value, + bool* value_changed) const override { + if (db_ == nullptr) { + return true; + } + std::string res; + db_->Get(ReadOptions(), "", &res); + return true; + } + + void SetDB(DB* db) { + db_ = db; + } + + virtual const char* Name() const override { return "FilterWithGet"; } + + private: + DB* db_; + }; + + + std::shared_ptr cf(new FilterWithGet()); + + Options options; + options.create_if_missing = true; + options.compaction_filter = cf.get(); + + DB* db = nullptr; + DestroyDB(db_name_, options); + Status s = DB::Open(options, db_name_, &db); + ASSERT_OK(s); + + cf->SetDB(db); + + // Write one L0 file + db->Put(WriteOptions(), "K1", "V1"); + db->Flush(FlushOptions()); + + // Compact all L0 files using CompactFiles + rocksdb::ColumnFamilyMetaData meta; + db->GetColumnFamilyMetaData(&meta); + for (auto& file : meta.levels[0].files) { + std::string fname = file.db_path + "/" + file.name; + ASSERT_OK( + db->CompactFiles(rocksdb::CompactionOptions(), {fname}, 0)); + } + + + delete db; +} + } // namespace rocksdb int main(int argc, char** argv) { diff --git a/db/db_impl.cc b/db/db_impl.cc index e8e83626b..578fe7cbc 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -2149,7 +2149,7 @@ Status DBImpl::CompactFiles( immutable_db_options_.info_log.get()); // Perform CompactFiles - SuperVersion* sv = GetAndRefSuperVersion(cfd); + SuperVersion* sv = cfd->GetReferencedSuperVersion(&mutex_); { InstrumentedMutexLock l(&mutex_); @@ -2161,7 +2161,12 @@ Status DBImpl::CompactFiles( input_file_names, output_level, output_path_id, &job_context, &log_buffer); } - ReturnAndCleanupSuperVersion(cfd, sv); + if (sv->Unref()) { + mutex_.Lock(); + sv->Cleanup(); + mutex_.Unlock(); + delete sv; + } // Find and delete obsolete files {