diff --git a/db/column_family.cc b/db/column_family.cc index 9dcc64321..7128234b0 100644 --- a/db/column_family.cc +++ b/db/column_family.cc @@ -439,6 +439,10 @@ void ColumnFamilyData::SetCurrent(Version* current_version) { current_ = current_version; } +uint64_t ColumnFamilyData::GetNumLiveVersions() const { + return VersionSet::GetNumLiveVersions(dummy_versions_); +} + MemTable* ColumnFamilyData::ConstructNewMemtable( const MutableCFOptions& mutable_cf_options) { assert(current_ != nullptr); diff --git a/db/column_family.h b/db/column_family.h index 84b01dc71..e32a71975 100644 --- a/db/column_family.h +++ b/db/column_family.h @@ -207,6 +207,8 @@ class ColumnFamilyData { Version* current() { return current_; } Version* dummy_versions() { return dummy_versions_; } void SetCurrent(Version* current); + uint64_t GetNumLiveVersions() const; // REQUIRE: DB mutex held + MemTable* ConstructNewMemtable(const MutableCFOptions& mutable_cf_options); void SetMemtable(MemTable* new_mem) { mem_ = new_mem; } void CreateNewMemtable(const MutableCFOptions& mutable_cf_options); diff --git a/db/db_test.cc b/db/db_test.cc index 3d352e25d..070a133d8 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -3230,6 +3230,43 @@ TEST(DBTest, GetProperty) { ASSERT_TRUE( dbfull()->GetIntProperty("rocksdb.estimate-table-readers-mem", &int_num)); ASSERT_GT(int_num, 0U); + + // Test rocksdb.num-live-versions + { + options.level0_file_num_compaction_trigger = 20; + Reopen(options); + ASSERT_TRUE( + dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num)); + ASSERT_EQ(int_num, 1U); + + // Use an iterator to hold current version + std::unique_ptr iter1(dbfull()->NewIterator(ReadOptions())); + + ASSERT_OK(dbfull()->Put(writeOpt, "k6", big_value)); + Flush(); + ASSERT_TRUE( + dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num)); + ASSERT_EQ(int_num, 2U); + + // Use an iterator to hold current version + std::unique_ptr iter2(dbfull()->NewIterator(ReadOptions())); + + ASSERT_OK(dbfull()->Put(writeOpt, "k7", big_value)); + Flush(); + ASSERT_TRUE( + dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num)); + ASSERT_EQ(int_num, 3U); + + iter2.reset(); + ASSERT_TRUE( + dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num)); + ASSERT_EQ(int_num, 2U); + + iter1.reset(); + ASSERT_TRUE( + dbfull()->GetIntProperty("rocksdb.num-live-versions", &int_num)); + ASSERT_EQ(int_num, 1U); + } } TEST(DBTest, FLUSH) { diff --git a/db/internal_stats.cc b/db/internal_stats.cc index e27e74de7..e557f9e06 100644 --- a/db/internal_stats.cc +++ b/db/internal_stats.cc @@ -140,6 +140,8 @@ DBPropertyType GetPropertyType(const Slice& property, bool* is_int_property, return kNumSnapshots; } else if (in == "oldest-snapshot-time") { return kOldestSnapshotTime; + } else if (in == "num-live-versions") { + return kNumLiveVersions; } return kUnknown; } @@ -224,6 +226,7 @@ bool InternalStats::GetStringProperty(DBPropertyType property_type, bool InternalStats::GetIntProperty(DBPropertyType property_type, uint64_t* value, DBImpl* db) const { + db->mutex_.AssertHeld(); const auto* vstorage = cfd_->current()->storage_info(); switch (property_type) { @@ -273,6 +276,9 @@ bool InternalStats::GetIntProperty(DBPropertyType property_type, case kOldestSnapshotTime: *value = static_cast(db->snapshots().GetOldestSnapshotTime()); return true; + case kNumLiveVersions: + *value = cfd_->GetNumLiveVersions(); + return true; #ifndef ROCKSDB_LITE case kIsFileDeletionEnabled: *value = db->IsFileDeletionsEnabled(); diff --git a/db/internal_stats.h b/db/internal_stats.h index c1d77b6b6..02bdabd09 100644 --- a/db/internal_stats.h +++ b/db/internal_stats.h @@ -50,6 +50,7 @@ enum DBPropertyType : uint32_t { // 0 means file deletions enabled kNumSnapshots, // Number of snapshots in the system kOldestSnapshotTime, // Unix timestamp of the first snapshot + kNumLiveVersions, }; extern DBPropertyType GetPropertyType(const Slice& property, diff --git a/db/version_set.cc b/db/version_set.cc index 09f45b7dc..dd76e8c57 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -2807,4 +2807,12 @@ ColumnFamilyData* VersionSet::CreateColumnFamily( return new_cfd; } +uint64_t VersionSet::GetNumLiveVersions(Version* dummy_versions) { + uint64_t count = 0; + for (Version* v = dummy_versions->next_; v != dummy_versions; v = v->next_) { + count++; + } + return count; +} + } // namespace rocksdb diff --git a/db/version_set.h b/db/version_set.h index b00c9ce2b..e80e61ceb 100644 --- a/db/version_set.h +++ b/db/version_set.h @@ -596,6 +596,8 @@ class VersionSet { ColumnFamilySet* GetColumnFamilySet() { return column_family_set_.get(); } const EnvOptions& env_options() { return env_options_; } + static uint64_t GetNumLiveVersions(Version* dummy_versions); + private: struct ManifestWriter; diff --git a/include/rocksdb/db.h b/include/rocksdb/db.h index 7cba31488..ab165a53f 100644 --- a/include/rocksdb/db.h +++ b/include/rocksdb/db.h @@ -325,6 +325,10 @@ class DB { // "rocksdb.is-file-deletions-enabled" // "rocksdb.num-snapshots" // "rocksdb.oldest-snapshot-time" + // "rocksdb.num-live-versions" - `version` is an internal data structure. + // See version_set.h for details. More live versions often mean more SST + // files are held from being deleted, by iterators or unfinished + // compactions. virtual bool GetProperty(ColumnFamilyHandle* column_family, const Slice& property, std::string* value) = 0; virtual bool GetProperty(const Slice& property, std::string* value) { @@ -347,6 +351,7 @@ class DB { // "rocksdb.is-file-deletions-enabled" // "rocksdb.num-snapshots" // "rocksdb.oldest-snapshot-time" + // "rocksdb.num-live-versions" virtual bool GetIntProperty(ColumnFamilyHandle* column_family, const Slice& property, uint64_t* value) = 0; virtual bool GetIntProperty(const Slice& property, uint64_t* value) {