diff --git a/db/db_test2.cc b/db/db_test2.cc index 1e0352e982f519e1955c6accf59f0f26b5dcce9e..e4f22ce60db773e13b79241808c8809ee962b0f6 100644 --- a/db/db_test2.cc +++ b/db/db_test2.cc @@ -5588,6 +5588,7 @@ TEST_F(DBTest2, MultiDBParallelOpenTest) { namespace { class DummyOldStats : public Statistics { public: + const char* Name() const override { return "DummyOldStats"; } uint64_t getTickerCount(uint32_t /*ticker_type*/) const override { return 0; } void recordTick(uint32_t /* ticker_type */, uint64_t /* count */) override { num_rt++; diff --git a/include/rocksdb/statistics.h b/include/rocksdb/statistics.h index 36f87431c445f6bab3e3957c1ccd35dc13b3ef77..57d173cd6541c6de1800b4888b79412b2d258944 100644 --- a/include/rocksdb/statistics.h +++ b/include/rocksdb/statistics.h @@ -13,6 +13,7 @@ #include #include +#include "rocksdb/customizable.h" #include "rocksdb/status.h" namespace ROCKSDB_NAMESPACE { @@ -568,10 +569,13 @@ enum StatsLevel : uint8_t { // options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED); // HistogramData hist; // options.statistics->histogramData(FLUSH_TIME, &hist); -class Statistics { +class Statistics : public Customizable { public: virtual ~Statistics() {} static const char* Type() { return "Statistics"; } + static Status CreateFromString(const ConfigOptions& opts, + const std::string& value, + std::shared_ptr* result); virtual uint64_t getTickerCount(uint32_t tickerType) const = 0; virtual void histogramData(uint32_t type, HistogramData* const data) const = 0; diff --git a/include/rocksdb/utilities/customizable_util.h b/include/rocksdb/utilities/customizable_util.h index 05b8460e51ed00867386b833c1ae1e4fcb69a77b..86239b00ba26041fa17b32fef08d609b2a82c4b3 100644 --- a/include/rocksdb/utilities/customizable_util.h +++ b/include/rocksdb/utilities/customizable_util.h @@ -120,6 +120,8 @@ static Status NewManagedObject( return object->ConfigureFromMap(config_options, opt_map); }); #else + (void)result; + (void)opt_map; status = Status::NotSupported("Cannot load object in LITE mode ", id); #endif // ROCKSDB_LITE if (config_options.ignore_unsupported_options && status.IsNotSupported()) { diff --git a/monitoring/statistics.cc b/monitoring/statistics.cc index acc148255e2fa50ebca231dc989bfa62d8ddc73d..00533006e2c711817eeed0af908ff1c19015b667 100644 --- a/monitoring/statistics.cc +++ b/monitoring/statistics.cc @@ -8,7 +8,12 @@ #include #include #include + +#include "rocksdb/convenience.h" #include "rocksdb/statistics.h" +#include "rocksdb/utilities/customizable_util.h" +#include "rocksdb/utilities/options_type.h" +#include "util/string_util.h" namespace ROCKSDB_NAMESPACE { @@ -272,8 +277,52 @@ std::shared_ptr CreateDBStatistics() { return std::make_shared(nullptr); } +#ifndef ROCKSDB_LITE +static int RegisterBuiltinStatistics(ObjectLibrary& library, + const std::string& /*arg*/) { + library.Register( + StatisticsImpl::kClassName(), + [](const std::string& /*uri*/, std::unique_ptr* guard, + std::string* /* errmsg */) { + guard->reset(new StatisticsImpl(nullptr)); + return guard->get(); + }); + return 1; +} +#endif // ROCKSDB_LITE + +Status Statistics::CreateFromString(const ConfigOptions& config_options, + const std::string& id, + std::shared_ptr* result) { +#ifndef ROCKSDB_LITE + static std::once_flag once; + std::call_once(once, [&]() { + RegisterBuiltinStatistics(*(ObjectLibrary::Default().get()), ""); + }); +#endif // ROCKSDB_LITE + Status s; + if (id == "" || id == StatisticsImpl::kClassName()) { + result->reset(new StatisticsImpl(nullptr)); + } else if (id == kNullptrString) { + result->reset(); + } else { + s = LoadSharedObject(config_options, id, nullptr, result); + } + return s; +} + +static std::unordered_map stats_type_info = { +#ifndef ROCKSDB_LITE + {"inner", OptionTypeInfo::AsCustomSharedPtr( + 0, OptionVerificationType::kByNameAllowFromNull, + OptionTypeFlags::kCompareNever)}, +#endif // !ROCKSDB_LITE +}; + StatisticsImpl::StatisticsImpl(std::shared_ptr stats) - : stats_(std::move(stats)) {} + : stats_(std::move(stats)) { + RegisterOptions("StatisticsOptions", &stats_, &stats_type_info); +} StatisticsImpl::~StatisticsImpl() {} diff --git a/monitoring/statistics.h b/monitoring/statistics.h index f633aa4efbe3a91323fc889abf44ed9f50916cc6..9ad1087634f8b744425183d2e1f997b4c9fa543e 100644 --- a/monitoring/statistics.h +++ b/monitoring/statistics.h @@ -44,6 +44,8 @@ class StatisticsImpl : public Statistics { public: StatisticsImpl(std::shared_ptr stats); virtual ~StatisticsImpl(); + const char* Name() const override { return kClassName(); } + static const char* kClassName() { return "BasicStatistics"; } virtual uint64_t getTickerCount(uint32_t ticker_type) const override; virtual void histogramData(uint32_t histogram_type, @@ -68,6 +70,8 @@ class StatisticsImpl : public Statistics { virtual bool getTickerMap(std::map*) const override; virtual bool HistEnabledForType(uint32_t type) const override; + const Customizable* Inner() const override { return stats_.get(); } + private: // If non-nullptr, forwards updates to the object pointed to by `stats_`. std::shared_ptr stats_; diff --git a/options/customizable_test.cc b/options/customizable_test.cc index e4afca42c3717acd51690cfa2d52fc0d6db97890..442edffcf5ecd8fd94a58310ee6822179985bd71 100644 --- a/options/customizable_test.cc +++ b/options/customizable_test.cc @@ -22,6 +22,7 @@ #include "rocksdb/env_encryption.h" #include "rocksdb/flush_block_policy.h" #include "rocksdb/secondary_cache.h" +#include "rocksdb/statistics.h" #include "rocksdb/utilities/customizable_util.h" #include "rocksdb/utilities/object_registry.h" #include "rocksdb/utilities/options_type.h" @@ -1218,6 +1219,27 @@ class TestSecondaryCache : public SecondaryCache { std::string GetPrintableOptions() const override { return ""; } }; +class TestStatistics : public StatisticsImpl { + public: + TestStatistics() : StatisticsImpl(nullptr) {} + const char* Name() const override { return kClassName(); } + static const char* kClassName() { return "Test"; } +}; + +class TestFlushBlockPolicyFactory : public FlushBlockPolicyFactory { + public: + TestFlushBlockPolicyFactory() {} + + static const char* kClassName() { return "TestFlushBlockPolicyFactory"; } + const char* Name() const override { return kClassName(); } + + FlushBlockPolicy* NewFlushBlockPolicy( + const BlockBasedTableOptions& /*table_options*/, + const BlockBuilder& /*data_block_builder*/) const override { + return nullptr; + } +}; + #ifndef ROCKSDB_LITE class MockEncryptionProvider : public EncryptionProvider { public: @@ -1260,23 +1282,7 @@ class MockCipher : public BlockCipher { Status Encrypt(char* /*data*/) override { return Status::NotSupported(); } Status Decrypt(char* data) override { return Encrypt(data); } }; -#endif // ROCKSDB_LITE - -class TestFlushBlockPolicyFactory : public FlushBlockPolicyFactory { - public: - TestFlushBlockPolicyFactory() {} - - static const char* kClassName() { return "TestFlushBlockPolicyFactory"; } - const char* Name() const override { return kClassName(); } - - FlushBlockPolicy* NewFlushBlockPolicy( - const BlockBasedTableOptions& /*table_options*/, - const BlockBuilder& /*data_block_builder*/) const override { - return nullptr; - } -}; -#ifndef ROCKSDB_LITE static int RegisterLocalObjects(ObjectLibrary& library, const std::string& /*arg*/) { size_t num_types; @@ -1302,6 +1308,14 @@ static int RegisterLocalObjects(ObjectLibrary& library, return guard->get(); }); // Load any locally defined objects here + library.Register( + TestStatistics::kClassName(), + [](const std::string& /*uri*/, std::unique_ptr* guard, + std::string* /* errmsg */) { + guard->reset(new TestStatistics()); + return guard->get(); + }); + library.Register( "Mock(://test)?", [](const std::string& uri, std::unique_ptr* guard, @@ -1432,6 +1446,56 @@ TEST_F(LoadCustomizableTest, LoadComparatorTest) { } } +TEST_F(LoadCustomizableTest, LoadStatisticsTest) { + std::shared_ptr stats; + ASSERT_NOK(Statistics::CreateFromString( + config_options_, TestStatistics::kClassName(), &stats)); + ASSERT_OK( + Statistics::CreateFromString(config_options_, "BasicStatistics", &stats)); + ASSERT_NE(stats, nullptr); + ASSERT_EQ(stats->Name(), std::string("BasicStatistics")); +#ifndef ROCKSDB_LITE + ASSERT_NOK(GetDBOptionsFromString(config_options_, db_opts_, + "statistics=Test", &db_opts_)); + ASSERT_OK(GetDBOptionsFromString(config_options_, db_opts_, + "statistics=BasicStatistics", &db_opts_)); + ASSERT_NE(db_opts_.statistics, nullptr); + ASSERT_STREQ(db_opts_.statistics->Name(), "BasicStatistics"); + + if (RegisterTests("test")) { + ASSERT_OK(Statistics::CreateFromString( + config_options_, TestStatistics::kClassName(), &stats)); + ASSERT_NE(stats, nullptr); + ASSERT_STREQ(stats->Name(), TestStatistics::kClassName()); + + ASSERT_OK(GetDBOptionsFromString(config_options_, db_opts_, + "statistics=Test", &db_opts_)); + ASSERT_NE(db_opts_.statistics, nullptr); + ASSERT_STREQ(db_opts_.statistics->Name(), TestStatistics::kClassName()); + + ASSERT_OK(GetDBOptionsFromString( + config_options_, db_opts_, "statistics={id=Test;inner=BasicStatistics}", + &db_opts_)); + ASSERT_NE(db_opts_.statistics, nullptr); + ASSERT_STREQ(db_opts_.statistics->Name(), TestStatistics::kClassName()); + auto* inner = db_opts_.statistics->GetOptions>( + "StatisticsOptions"); + ASSERT_NE(inner, nullptr); + ASSERT_NE(inner->get(), nullptr); + ASSERT_STREQ(inner->get()->Name(), "BasicStatistics"); + + ASSERT_OK(Statistics::CreateFromString( + config_options_, "id=BasicStatistics;inner=Test", &stats)); + ASSERT_NE(stats, nullptr); + ASSERT_STREQ(stats->Name(), "BasicStatistics"); + inner = stats->GetOptions>("StatisticsOptions"); + ASSERT_NE(inner, nullptr); + ASSERT_NE(inner->get(), nullptr); + ASSERT_STREQ(inner->get()->Name(), TestStatistics::kClassName()); + } +#endif +} + TEST_F(LoadCustomizableTest, LoadMemTableRepFactoryTest) { std::unique_ptr result; ASSERT_NOK(MemTableRepFactory::CreateFromString( @@ -1567,7 +1631,7 @@ TEST_F(LoadCustomizableTest, LoadFlushBlockPolicyFactoryTest) { std::shared_ptr table; std::shared_ptr result; ASSERT_NOK(FlushBlockPolicyFactory::CreateFromString( - config_options_, "TestFlushBlockPolicyFactory", &result)); + config_options_, TestFlushBlockPolicyFactory::kClassName(), &result)); ASSERT_OK( FlushBlockPolicyFactory::CreateFromString(config_options_, "", &result)); @@ -1595,16 +1659,17 @@ TEST_F(LoadCustomizableTest, LoadFlushBlockPolicyFactoryTest) { FlushBlockEveryKeyPolicyFactory::kClassName()); if (RegisterTests("Test")) { ASSERT_OK(FlushBlockPolicyFactory::CreateFromString( - config_options_, "TestFlushBlockPolicyFactory", &result)); + config_options_, TestFlushBlockPolicyFactory::kClassName(), &result)); ASSERT_NE(result, nullptr); - ASSERT_STREQ(result->Name(), "TestFlushBlockPolicyFactory"); + ASSERT_STREQ(result->Name(), TestFlushBlockPolicyFactory::kClassName()); ASSERT_OK(TableFactory::CreateFromString( - config_options_, table_opts + "TestFlushBlockPolicyFactory", &table)); + config_options_, table_opts + TestFlushBlockPolicyFactory::kClassName(), + &table)); bbto = table->GetOptions(); ASSERT_NE(bbto, nullptr); ASSERT_NE(bbto->flush_block_policy_factory.get(), nullptr); ASSERT_STREQ(bbto->flush_block_policy_factory->Name(), - "TestFlushBlockPolicyFactory"); + TestFlushBlockPolicyFactory::kClassName()); } #endif // ROCKSDB_LITE } diff --git a/options/db_options.cc b/options/db_options.cc index ee156942db062c3a3d8566561fa57f2de2b987c5..e6e2c25ee270c20528e2e9759f2aeb09c52c04e2 100644 --- a/options/db_options.cc +++ b/options/db_options.cc @@ -18,6 +18,7 @@ #include "rocksdb/listener.h" #include "rocksdb/rate_limiter.h" #include "rocksdb/sst_file_manager.h" +#include "rocksdb/statistics.h" #include "rocksdb/system_clock.h" #include "rocksdb/utilities/options_type.h" #include "rocksdb/wal_filter.h" @@ -443,6 +444,15 @@ static std::unordered_map {offsetof(struct ImmutableDBOptions, allow_data_in_errors), OptionType::kBoolean, OptionVerificationType::kNormal, OptionTypeFlags::kNone}}, + {"statistics", + OptionTypeInfo::AsCustomSharedPtr( + // Statistics should not be compared and can be null + // Statistics are maked "don't serialize" until they can be shared + // between DBs + offsetof(struct ImmutableDBOptions, statistics), + OptionVerificationType::kNormal, + OptionTypeFlags::kCompareNever | OptionTypeFlags::kDontSerialize | + OptionTypeFlags::kAllowNull)}, // Allow EventListeners that have a non-empty Name() to be read/written // as options Each listener will either be // - A simple name (e.g. "MyEventListener") diff --git a/tools/db_bench_tool.cc b/tools/db_bench_tool.cc index 5ff8c4e250cea1f9a373f8b2fd8c3880e9238413..dc32c10753d21ef82425df5503fb1d9d3bbc19e1 100644 --- a/tools/db_bench_tool.cc +++ b/tools/db_bench_tool.cc @@ -8017,6 +8017,7 @@ class Benchmark { int db_bench_tool(int argc, char** argv) { ROCKSDB_NAMESPACE::port::InstallStackTraceHandler(); + ConfigOptions config_options; static bool initialized = false; if (!initialized) { SetUsageMessage(std::string("\nUSAGE:\n") + std::string(argv[0]) + @@ -8033,8 +8034,8 @@ int db_bench_tool(int argc, char** argv) { exit(1); } if (!FLAGS_statistics_string.empty()) { - Status s = ObjectRegistry::NewInstance()->NewSharedObject( - FLAGS_statistics_string, &dbstats); + Status s = Statistics::CreateFromString(config_options, + FLAGS_statistics_string, &dbstats); if (dbstats == nullptr) { fprintf(stderr, "No Statistics registered matching string: %s status=%s\n", @@ -8080,7 +8081,7 @@ int db_bench_tool(int argc, char** argv) { } if (env_opts == 1) { - Status s = Env::CreateFromUri(ConfigOptions(), FLAGS_env_uri, FLAGS_fs_uri, + Status s = Env::CreateFromUri(config_options, FLAGS_env_uri, FLAGS_fs_uri, &FLAGS_env, &env_guard); if (!s.ok()) { fprintf(stderr, "Failed creating env: %s\n", s.ToString().c_str());