Add ObjectRegistry to ConfigOptions (#8166)

Summary:
This change enables a couple of things:
- Different ConfigOptions can have different registry/factory associated with it, thereby allowing things like a "Test" ConfigOptions versus a "Production"
- The ObjectRegistry is created fewer times and can be re-used

The ConfigOptions can also be initialized/constructed from a DBOptions, in which case it will grab some of its settings (Env, Logger) from the DBOptions.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/8166

Reviewed By: zhichao-cao

Differential Revision: D27657952

Pulled By: mrambacher

fbshipit-source-id: ae1d6200bb7ab127405cdeefaba43c7fe694dfdd
This commit is contained in:
mrambacher 2021-05-11 06:45:49 -07:00 committed by Facebook GitHub Bot
parent ff463742b5
commit 9f2d255aed
12 changed files with 308 additions and 40 deletions

View File

@ -23,6 +23,8 @@
* Removed unused structure `CompactionFilterContext`. * Removed unused structure `CompactionFilterContext`.
* The `skip_filters` parameter to SstFileWriter is now considered deprecated. Use `BlockBasedTableOptions::filter_policy` to control generation of filters. * The `skip_filters` parameter to SstFileWriter is now considered deprecated. Use `BlockBasedTableOptions::filter_policy` to control generation of filters.
* ClockCache is known to have bugs that could lead to crash or corruption, so should not be used until fixed. Use NewLRUCache instead. * ClockCache is known to have bugs that could lead to crash or corruption, so should not be used until fixed. Use NewLRUCache instead.
* Added the ObjectRegistry to the ConfigOptions class. This registry instance will be used to find any customizable loadable objects during initialization.
* Expanded the ObjectRegistry functionality to allow nested ObjectRegistry instances. Added methods to register a set of functions with the registry/library as a group.
* Deprecated backupable_db.h and BackupableDBOptions in favor of new versions with appropriate names: backup_engine.h and BackupEngineOptions. Old API compatibility is preserved. * Deprecated backupable_db.h and BackupableDBOptions in favor of new versions with appropriate names: backup_engine.h and BackupEngineOptions. Old API compatibility is preserved.
### Default Option Change ### Default Option Change

View File

@ -34,7 +34,7 @@ class DBOptionsTest : public DBTestBase {
const DBOptions& options) { const DBOptions& options) {
std::string options_str; std::string options_str;
std::unordered_map<std::string, std::string> mutable_map; std::unordered_map<std::string, std::string> mutable_map;
ConfigOptions config_options; ConfigOptions config_options(options);
config_options.delimiter = "; "; config_options.delimiter = "; ";
EXPECT_OK(GetStringFromMutableDBOptions( EXPECT_OK(GetStringFromMutableDBOptions(

View File

@ -16,6 +16,9 @@
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
class Env; class Env;
class Logger;
class ObjectRegistry;
struct ColumnFamilyOptions; struct ColumnFamilyOptions;
struct DBOptions; struct DBOptions;
struct Options; struct Options;
@ -27,6 +30,15 @@ struct Options;
// of the serialization (e.g. delimiter), and how to compare // of the serialization (e.g. delimiter), and how to compare
// options (sanity_level). // options (sanity_level).
struct ConfigOptions { struct ConfigOptions {
// Constructs a new ConfigOptions with a new object registry.
// This method should only be used when a DBOptions is not available,
// else registry settings may be lost
ConfigOptions();
// Constructs a new ConfigOptions using the settings from
// the input DBOptions. Currently constructs a new object registry.
explicit ConfigOptions(const DBOptions&);
// This enum defines the RocksDB options sanity level. // This enum defines the RocksDB options sanity level.
enum SanityLevel : unsigned char { enum SanityLevel : unsigned char {
kSanityLevelNone = 0x01, // Performs no sanity check at all. kSanityLevelNone = 0x01, // Performs no sanity check at all.
@ -78,6 +90,11 @@ struct ConfigOptions {
// The environment to use for this option // The environment to use for this option
Env* env = Env::Default(); Env* env = Env::Default();
#ifndef ROCKSDB_LITE
// The object registry to use for this options
std::shared_ptr<ObjectRegistry> registry;
#endif
bool IsShallow() const { return depth == Depth::kDepthShallow; } bool IsShallow() const { return depth == Depth::kDepthShallow; }
bool IsDetailed() const { return depth == Depth::kDepthDetailed; } bool IsDetailed() const { return depth == Depth::kDepthDetailed; }
@ -500,7 +517,6 @@ Status VerifySstFileChecksum(const Options& options,
const EnvOptions& env_options, const EnvOptions& env_options,
const ReadOptions& read_options, const ReadOptions& read_options,
const std::string& file_path); const std::string& file_path);
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE

View File

@ -17,12 +17,23 @@
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
class Logger; class Logger;
class ObjectLibrary;
// Returns a new T when called with a string. Populates the std::unique_ptr // Returns a new T when called with a string. Populates the std::unique_ptr
// argument if granting ownership to caller. // argument if granting ownership to caller.
template <typename T> template <typename T>
using FactoryFunc = using FactoryFunc =
std::function<T*(const std::string&, std::unique_ptr<T>*, std::string*)>; std::function<T*(const std::string&, std::unique_ptr<T>*, std::string*)>;
// The signature of the function for loading factories
// into an object library. This method is expected to register
// factory functions in the supplied ObjectLibrary.
// The ObjectLibrary is the library in which the factories will be loaded.
// The std::string is the argument passed to the loader function.
// The RegistrarFunc should return the number of objects loaded into this
// library
using RegistrarFunc = std::function<int(ObjectLibrary&, const std::string&)>;
class ObjectLibrary { class ObjectLibrary {
public: public:
// Base class for an Entry in the Registry. // Base class for an Entry in the Registry.
@ -62,9 +73,18 @@ class ObjectLibrary {
FactoryFunc<T> factory_; FactoryFunc<T> factory_;
}; // End class FactoryEntry }; // End class FactoryEntry
public: public:
explicit ObjectLibrary(const std::string& id) { id_ = id; }
const std::string& GetID() const { return id_; }
// Finds the entry matching the input name and type // Finds the entry matching the input name and type
const Entry* FindEntry(const std::string& type, const Entry* FindEntry(const std::string& type,
const std::string& name) const; const std::string& name) const;
// Returns the total number of factories registered for this library.
// This method returns the sum of all factories registered for all types.
// @param num_types returns how many unique types are registered.
size_t GetFactoryCount(size_t* num_types) const;
void Dump(Logger* logger) const; void Dump(Logger* logger) const;
// Registers the factory with the library for the pattern. // Registers the factory with the library for the pattern.
@ -76,6 +96,12 @@ class ObjectLibrary {
AddEntry(T::Type(), entry); AddEntry(T::Type(), entry);
return factory; return factory;
} }
// Invokes the registrar function with the supplied arg for this library.
int Register(const RegistrarFunc& registrar, const std::string& arg) {
return registrar(*this, arg);
}
// Returns the default ObjectLibrary // Returns the default ObjectLibrary
static std::shared_ptr<ObjectLibrary>& Default(); static std::shared_ptr<ObjectLibrary>& Default();
@ -85,6 +111,9 @@ class ObjectLibrary {
// ** FactoryFunctions for this loader, organized by type // ** FactoryFunctions for this loader, organized by type
std::unordered_map<std::string, std::vector<std::unique_ptr<Entry>>> entries_; std::unordered_map<std::string, std::vector<std::unique_ptr<Entry>>> entries_;
// The name for this library
std::string id_;
}; };
// The ObjectRegistry is used to register objects that can be created by a // The ObjectRegistry is used to register objects that can be created by a
@ -93,11 +122,26 @@ class ObjectLibrary {
class ObjectRegistry { class ObjectRegistry {
public: public:
static std::shared_ptr<ObjectRegistry> NewInstance(); static std::shared_ptr<ObjectRegistry> NewInstance();
static std::shared_ptr<ObjectRegistry> NewInstance(
const std::shared_ptr<ObjectRegistry>& parent);
static std::shared_ptr<ObjectRegistry> Default();
explicit ObjectRegistry(const std::shared_ptr<ObjectRegistry>& parent)
: parent_(parent) {}
ObjectRegistry(); std::shared_ptr<ObjectLibrary> AddLibrary(const std::string& id) {
auto library = std::make_shared<ObjectLibrary>(id);
libraries_.push_back(library);
return library;
}
void AddLibrary(const std::shared_ptr<ObjectLibrary>& library) { void AddLibrary(const std::shared_ptr<ObjectLibrary>& library) {
libraries_.emplace_back(library); libraries_.push_back(library);
}
void AddLibrary(const std::string& id, const RegistrarFunc& registrar,
const std::string& arg) {
auto library = AddLibrary(id);
library->Register(registrar, arg);
} }
// Creates a new T using the factory function that was registered with a // Creates a new T using the factory function that was registered with a
@ -193,6 +237,10 @@ class ObjectRegistry {
void Dump(Logger* logger) const; void Dump(Logger* logger) const;
private: private:
explicit ObjectRegistry(const std::shared_ptr<ObjectLibrary>& library) {
libraries_.push_back(library);
}
const ObjectLibrary::Entry* FindEntry(const std::string& type, const ObjectLibrary::Entry* FindEntry(const std::string& type,
const std::string& name) const; const std::string& name) const;
@ -200,6 +248,7 @@ class ObjectRegistry {
// The libraries are searched in reverse order (back to front) when // The libraries are searched in reverse order (back to front) when
// searching for entries. // searching for entries.
std::vector<std::shared_ptr<ObjectLibrary>> libraries_; std::vector<std::shared_ptr<ObjectLibrary>> libraries_;
std::shared_ptr<ObjectRegistry> parent_;
}; };
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE

View File

@ -541,12 +541,12 @@ static std::unordered_map<std::string, OptionTypeInfo>
OptionType::kComparator, OptionVerificationType::kByName, OptionType::kComparator, OptionVerificationType::kByName,
OptionTypeFlags::kCompareLoose, OptionTypeFlags::kCompareLoose,
// Parses the string and sets the corresponding comparator // Parses the string and sets the corresponding comparator
[](const ConfigOptions& /*opts*/, const std::string& /*name*/, [](const ConfigOptions& opts, const std::string& /*name*/,
const std::string& value, char* addr) { const std::string& value, char* addr) {
auto old_comparator = reinterpret_cast<const Comparator**>(addr); auto old_comparator = reinterpret_cast<const Comparator**>(addr);
const Comparator* new_comparator = *old_comparator; const Comparator* new_comparator = *old_comparator;
Status status = ObjectRegistry::NewInstance()->NewStaticObject( Status status =
value, &new_comparator); opts.registry->NewStaticObject(value, &new_comparator);
if (status.ok()) { if (status.ok()) {
*old_comparator = new_comparator; *old_comparator = new_comparator;
return status; return status;
@ -661,12 +661,11 @@ static std::unordered_map<std::string, OptionTypeInfo>
OptionVerificationType::kByNameAllowFromNull, OptionVerificationType::kByNameAllowFromNull,
OptionTypeFlags::kCompareLoose, OptionTypeFlags::kCompareLoose,
// Parses the input value as a MergeOperator, updating the value // Parses the input value as a MergeOperator, updating the value
[](const ConfigOptions& /*opts*/, const std::string& /*name*/, [](const ConfigOptions& opts, const std::string& /*name*/,
const std::string& value, char* addr) { const std::string& value, char* addr) {
auto mop = reinterpret_cast<std::shared_ptr<MergeOperator>*>(addr); auto mop = reinterpret_cast<std::shared_ptr<MergeOperator>*>(addr);
Status status = Status status =
ObjectRegistry::NewInstance()->NewSharedObject<MergeOperator>( opts.registry->NewSharedObject<MergeOperator>(value, mop);
value, mop);
// Only support static comparator for now. // Only support static comparator for now.
if (status.ok()) { if (status.ok()) {
return status; return status;

View File

@ -83,12 +83,13 @@ static Status LoadSharedObject(const ConfigOptions& config_options,
return Status::NotSupported("Cannot reset object ", id); return Status::NotSupported("Cannot reset object ", id);
} else { } else {
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
status = ObjectRegistry::NewInstance()->NewSharedObject(id, result); status = config_options.registry->NewSharedObject(id, result);
#else #else
status = Status::NotSupported("Cannot load object in LITE mode ", id); status = Status::NotSupported("Cannot load object in LITE mode ", id);
#endif #endif
if (!status.ok()) { if (!status.ok()) {
if (config_options.ignore_unsupported_options) { if (config_options.ignore_unsupported_options &&
status.IsNotSupported()) {
return Status::OK(); return Status::OK();
} else { } else {
return status; return status;
@ -142,12 +143,13 @@ static Status LoadUniqueObject(const ConfigOptions& config_options,
return Status::NotSupported("Cannot reset object ", id); return Status::NotSupported("Cannot reset object ", id);
} else { } else {
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
status = ObjectRegistry::NewInstance()->NewUniqueObject(id, result); status = config_options.registry->NewUniqueObject(id, result);
#else #else
status = Status::NotSupported("Cannot load object in LITE mode ", id); status = Status::NotSupported("Cannot load object in LITE mode ", id);
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE
if (!status.ok()) { if (!status.ok()) {
if (config_options.ignore_unsupported_options) { if (config_options.ignore_unsupported_options &&
status.IsNotSupported()) {
return Status::OK(); return Status::OK();
} else { } else {
return status; return status;
@ -199,12 +201,13 @@ static Status LoadStaticObject(const ConfigOptions& config_options,
return Status::NotSupported("Cannot reset object ", id); return Status::NotSupported("Cannot reset object ", id);
} else { } else {
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
status = ObjectRegistry::NewInstance()->NewStaticObject(id, result); status = config_options.registry->NewStaticObject(id, result);
#else #else
status = Status::NotSupported("Cannot load object in LITE mode ", id); status = Status::NotSupported("Cannot load object in LITE mode ", id);
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE
if (!status.ok()) { if (!status.ok()) {
if (config_options.ignore_unsupported_options) { if (config_options.ignore_unsupported_options &&
status.IsNotSupported()) {
return Status::OK(); return Status::OK();
} else { } else {
return status; return status;

View File

@ -695,6 +695,68 @@ TEST_F(CustomizableTest, MutableOptionsTest) {
} }
#endif // !ROCKSDB_LITE #endif // !ROCKSDB_LITE
#ifndef ROCKSDB_LITE
// This method loads existing test classes into the ObjectRegistry
static int RegisterTestObjects(ObjectLibrary& library,
const std::string& /*arg*/) {
size_t num_types;
library.Register<TableFactory>(
"MockTable",
[](const std::string& /*uri*/, std::unique_ptr<TableFactory>* guard,
std::string* /* errmsg */) {
guard->reset(new mock::MockTableFactory());
return guard->get();
});
return static_cast<int>(library.GetFactoryCount(&num_types));
}
static int RegisterLocalObjects(ObjectLibrary& library,
const std::string& /*arg*/) {
size_t num_types;
// Load any locally defined objects here
return static_cast<int>(library.GetFactoryCount(&num_types));
}
class LoadCustomizableTest : public testing::Test {
public:
LoadCustomizableTest() { config_options_.ignore_unsupported_options = false; }
bool RegisterTests(const std::string& arg) {
#ifndef ROCKSDB_LITE
config_options_.registry->AddLibrary("custom-tests", RegisterTestObjects,
arg);
config_options_.registry->AddLibrary("local-tests", RegisterLocalObjects,
arg);
return true;
#else
(void)arg;
return false;
#endif // !ROCKSDB_LITE
}
protected:
DBOptions db_opts_;
ColumnFamilyOptions cf_opts_;
ConfigOptions config_options_;
};
TEST_F(LoadCustomizableTest, LoadTableFactoryTest) {
std::shared_ptr<TableFactory> factory;
ASSERT_NOK(
TableFactory::CreateFromString(config_options_, "MockTable", &factory));
ASSERT_OK(TableFactory::CreateFromString(
config_options_, TableFactory::kBlockBasedTableName(), &factory));
ASSERT_NE(factory, nullptr);
ASSERT_STREQ(factory->Name(), TableFactory::kBlockBasedTableName());
if (RegisterTests("Test")) {
ASSERT_OK(
TableFactory::CreateFromString(config_options_, "MockTable", &factory));
ASSERT_NE(factory, nullptr);
ASSERT_STREQ(factory->Name(), "MockTable");
}
}
#endif // !ROCKSDB_LITE
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) { int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);

View File

@ -28,6 +28,20 @@
#include "util/string_util.h" #include "util/string_util.h"
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
ConfigOptions::ConfigOptions()
#ifndef ROCKSDB_LITE
: registry(ObjectRegistry::NewInstance())
#endif
{
env = Env::Default();
}
ConfigOptions::ConfigOptions(const DBOptions& db_opts) : env(db_opts.env) {
#ifndef ROCKSDB_LITE
registry = ObjectRegistry::NewInstance();
#endif
}
Status ValidateOptions(const DBOptions& db_opts, Status ValidateOptions(const DBOptions& db_opts,
const ColumnFamilyOptions& cf_opts) { const ColumnFamilyOptions& cf_opts) {
Status s; Status s;
@ -703,7 +717,7 @@ Status GetStringFromMutableDBOptions(const ConfigOptions& config_options,
Status GetStringFromDBOptions(std::string* opt_string, Status GetStringFromDBOptions(std::string* opt_string,
const DBOptions& db_options, const DBOptions& db_options,
const std::string& delimiter) { const std::string& delimiter) {
ConfigOptions config_options; ConfigOptions config_options(db_options);
config_options.delimiter = delimiter; config_options.delimiter = delimiter;
return GetStringFromDBOptions(config_options, db_options, opt_string); return GetStringFromDBOptions(config_options, db_options, opt_string);
} }
@ -816,7 +830,7 @@ Status GetDBOptionsFromMap(
const std::unordered_map<std::string, std::string>& opts_map, const std::unordered_map<std::string, std::string>& opts_map,
DBOptions* new_options, bool input_strings_escaped, DBOptions* new_options, bool input_strings_escaped,
bool ignore_unknown_options) { bool ignore_unknown_options) {
ConfigOptions config_options; ConfigOptions config_options(base_options);
config_options.input_strings_escaped = input_strings_escaped; config_options.input_strings_escaped = input_strings_escaped;
config_options.ignore_unknown_options = ignore_unknown_options; config_options.ignore_unknown_options = ignore_unknown_options;
return GetDBOptionsFromMap(config_options, base_options, opts_map, return GetDBOptionsFromMap(config_options, base_options, opts_map,
@ -844,7 +858,7 @@ Status GetDBOptionsFromMap(
Status GetDBOptionsFromString(const DBOptions& base_options, Status GetDBOptionsFromString(const DBOptions& base_options,
const std::string& opts_str, const std::string& opts_str,
DBOptions* new_options) { DBOptions* new_options) {
ConfigOptions config_options; ConfigOptions config_options(base_options);
config_options.input_strings_escaped = false; config_options.input_strings_escaped = false;
config_options.ignore_unknown_options = false; config_options.ignore_unknown_options = false;
@ -868,7 +882,7 @@ Status GetDBOptionsFromString(const ConfigOptions& config_options,
Status GetOptionsFromString(const Options& base_options, Status GetOptionsFromString(const Options& base_options,
const std::string& opts_str, Options* new_options) { const std::string& opts_str, Options* new_options) {
ConfigOptions config_options; ConfigOptions config_options(base_options);
config_options.input_strings_escaped = false; config_options.input_strings_escaped = false;
config_options.ignore_unknown_options = false; config_options.ignore_unknown_options = false;
@ -883,6 +897,7 @@ Status GetOptionsFromString(const ConfigOptions& config_options,
std::unordered_map<std::string, std::string> unused_opts; std::unordered_map<std::string, std::string> unused_opts;
std::unordered_map<std::string, std::string> opts_map; std::unordered_map<std::string, std::string> opts_map;
assert(new_options);
*new_options = base_options; *new_options = base_options;
Status s = StringToMap(opts_str, &opts_map); Status s = StringToMap(opts_str, &opts_map);
if (!s.ok()) { if (!s.ok()) {

View File

@ -1808,6 +1808,23 @@ TEST_F(OptionsTest, ConvertOptionsTest) {
} }
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
const static std::string kCustomEnvName = "Custom";
const static std::string kCustomEnvProp = "env=" + kCustomEnvName;
class CustomEnv : public EnvWrapper {
public:
explicit CustomEnv(Env* _target) : EnvWrapper(_target) {}
};
static int RegisterCustomEnv(ObjectLibrary& library, const std::string& arg) {
library.Register<Env>(
arg, [](const std::string& /*name*/, std::unique_ptr<Env>* /*env_guard*/,
std::string* /* errmsg */) {
static CustomEnv env(Env::Default());
return &env;
});
return 1;
}
// This test suite tests the old APIs into the Configure options methods. // This test suite tests the old APIs into the Configure options methods.
// Once those APIs are officially deprecated, this test suite can be deleted. // Once those APIs are officially deprecated, this test suite can be deleted.
class OptionsOldApiTest : public testing::Test {}; class OptionsOldApiTest : public testing::Test {};
@ -2507,14 +2524,8 @@ TEST_F(OptionsOldApiTest, GetOptionsFromStringTest) {
NewBlockBasedTableFactory(block_based_table_options)); NewBlockBasedTableFactory(block_based_table_options));
// Register an Env with object registry. // Register an Env with object registry.
const static char* kCustomEnvName = "CustomEnv";
class CustomEnv : public EnvWrapper {
public:
explicit CustomEnv(Env* _target) : EnvWrapper(_target) {}
};
ObjectLibrary::Default()->Register<Env>( ObjectLibrary::Default()->Register<Env>(
kCustomEnvName, "CustomEnvDefault",
[](const std::string& /*name*/, std::unique_ptr<Env>* /*env_guard*/, [](const std::string& /*name*/, std::unique_ptr<Env>* /*env_guard*/,
std::string* /* errmsg */) { std::string* /* errmsg */) {
static CustomEnv env(Env::Default()); static CustomEnv env(Env::Default());
@ -2528,7 +2539,7 @@ TEST_F(OptionsOldApiTest, GetOptionsFromStringTest) {
"compression_opts=4:5:6;create_if_missing=true;max_open_files=1;" "compression_opts=4:5:6;create_if_missing=true;max_open_files=1;"
"bottommost_compression_opts=5:6:7;create_if_missing=true;max_open_files=" "bottommost_compression_opts=5:6:7;create_if_missing=true;max_open_files="
"1;" "1;"
"rate_limiter_bytes_per_sec=1024;env=CustomEnv", "rate_limiter_bytes_per_sec=1024;env=CustomEnvDefault",
&new_options)); &new_options));
ASSERT_EQ(new_options.compression_opts.window_bits, 4); ASSERT_EQ(new_options.compression_opts.window_bits, 4);
@ -2562,7 +2573,7 @@ TEST_F(OptionsOldApiTest, GetOptionsFromStringTest) {
ASSERT_EQ(new_options.max_open_files, 1); ASSERT_EQ(new_options.max_open_files, 1);
ASSERT_TRUE(new_options.rate_limiter.get() != nullptr); ASSERT_TRUE(new_options.rate_limiter.get() != nullptr);
Env* newEnv = new_options.env; Env* newEnv = new_options.env;
ASSERT_OK(Env::LoadEnv(kCustomEnvName, &newEnv)); ASSERT_OK(Env::LoadEnv("CustomEnvDefault", &newEnv));
ASSERT_EQ(newEnv, new_options.env); ASSERT_EQ(newEnv, new_options.env);
} }
@ -3981,6 +3992,46 @@ TEST_F(OptionTypeInfoTest, TestVectorType) {
ASSERT_EQ(vec1[1], "b1|b2"); ASSERT_EQ(vec1[1], "b1|b2");
ASSERT_EQ(vec1[2], "c1|c2|{d1|d2}"); ASSERT_EQ(vec1[2], "c1|c2|{d1|d2}");
} }
class ConfigOptionsTest : public testing::Test {};
TEST_F(ConfigOptionsTest, EnvFromConfigOptions) {
ConfigOptions config_options;
DBOptions db_opts;
Options opts;
Env* mem_env = NewMemEnv(Env::Default());
config_options.registry->AddLibrary("custom-env", RegisterCustomEnv,
kCustomEnvName);
config_options.env = mem_env;
// First test that we can get the env as expected
ASSERT_OK(GetDBOptionsFromString(config_options, DBOptions(), kCustomEnvProp,
&db_opts));
ASSERT_OK(
GetOptionsFromString(config_options, Options(), kCustomEnvProp, &opts));
ASSERT_NE(config_options.env, db_opts.env);
ASSERT_EQ(opts.env, db_opts.env);
Env* custom_env = db_opts.env;
// Now try a "bad" env" and check that nothing changed
config_options.ignore_unsupported_options = true;
ASSERT_OK(
GetDBOptionsFromString(config_options, db_opts, "env=unknown", &db_opts));
ASSERT_OK(GetOptionsFromString(config_options, opts, "env=unknown", &opts));
ASSERT_EQ(config_options.env, mem_env);
ASSERT_EQ(db_opts.env, custom_env);
ASSERT_EQ(opts.env, db_opts.env);
// Now try a "bad" env" ignoring unknown objects
config_options.ignore_unsupported_options = false;
ASSERT_NOK(
GetDBOptionsFromString(config_options, db_opts, "env=unknown", &db_opts));
ASSERT_EQ(config_options.env, mem_env);
ASSERT_EQ(db_opts.env, custom_env);
ASSERT_EQ(opts.env, db_opts.env);
delete mem_env;
}
#endif // !ROCKSDB_LITE #endif // !ROCKSDB_LITE
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE

View File

@ -32,6 +32,15 @@ void ObjectLibrary::AddEntry(const std::string &type,
entries.emplace_back(std::move(entry)); entries.emplace_back(std::move(entry));
} }
size_t ObjectLibrary::GetFactoryCount(size_t *types) const {
*types = entries_.size();
size_t factories = 0;
for (const auto &e : entries_) {
factories += e.second.size();
}
return factories;
}
void ObjectLibrary::Dump(Logger *logger) const { void ObjectLibrary::Dump(Logger *logger) const {
for (const auto &iter : entries_) { for (const auto &iter : entries_) {
ROCKS_LOG_HEADER(logger, " Registered factories for type[%s] ", ROCKS_LOG_HEADER(logger, " Registered factories for type[%s] ",
@ -50,17 +59,23 @@ void ObjectLibrary::Dump(Logger *logger) const {
// This instance will contain most of the "standard" registered objects // This instance will contain most of the "standard" registered objects
std::shared_ptr<ObjectLibrary> &ObjectLibrary::Default() { std::shared_ptr<ObjectLibrary> &ObjectLibrary::Default() {
static std::shared_ptr<ObjectLibrary> instance = static std::shared_ptr<ObjectLibrary> instance =
std::make_shared<ObjectLibrary>(); std::make_shared<ObjectLibrary>("default");
return instance;
}
std::shared_ptr<ObjectRegistry> ObjectRegistry::Default() {
static std::shared_ptr<ObjectRegistry> instance(
new ObjectRegistry(ObjectLibrary::Default()));
return instance; return instance;
} }
std::shared_ptr<ObjectRegistry> ObjectRegistry::NewInstance() { std::shared_ptr<ObjectRegistry> ObjectRegistry::NewInstance() {
std::shared_ptr<ObjectRegistry> instance = std::make_shared<ObjectRegistry>(); return std::make_shared<ObjectRegistry>(Default());
return instance;
} }
ObjectRegistry::ObjectRegistry() { std::shared_ptr<ObjectRegistry> ObjectRegistry::NewInstance(
libraries_.push_back(ObjectLibrary::Default()); const std::shared_ptr<ObjectRegistry> &parent) {
return std::make_shared<ObjectRegistry>(parent);
} }
// Searches (from back to front) the libraries looking for the // Searches (from back to front) the libraries looking for the
@ -74,13 +89,20 @@ const ObjectLibrary::Entry *ObjectRegistry::FindEntry(
return entry; return entry;
} }
} }
if (parent_ != nullptr) {
return parent_->FindEntry(type, name);
} else {
return nullptr; return nullptr;
}
} }
void ObjectRegistry::Dump(Logger *logger) const { void ObjectRegistry::Dump(Logger *logger) const {
for (auto iter = libraries_.crbegin(); iter != libraries_.crend(); ++iter) { for (auto iter = libraries_.crbegin(); iter != libraries_.crend(); ++iter) {
iter->get()->Dump(logger); iter->get()->Dump(logger);
} }
if (parent_ != nullptr) {
parent_->Dump(logger);
}
} }
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE

View File

@ -62,7 +62,8 @@ TEST_F(EnvRegistryTest, LocalRegistry) {
std::string msg; std::string msg;
std::unique_ptr<Env> guard; std::unique_ptr<Env> guard;
auto registry = ObjectRegistry::NewInstance(); auto registry = ObjectRegistry::NewInstance();
std::shared_ptr<ObjectLibrary> library = std::make_shared<ObjectLibrary>(); std::shared_ptr<ObjectLibrary> library =
std::make_shared<ObjectLibrary>("local");
registry->AddLibrary(library); registry->AddLibrary(library);
library->Register<Env>( library->Register<Env>(
"test-local", "test-local",
@ -87,7 +88,8 @@ TEST_F(EnvRegistryTest, LocalRegistry) {
TEST_F(EnvRegistryTest, CheckShared) { TEST_F(EnvRegistryTest, CheckShared) {
std::shared_ptr<Env> shared; std::shared_ptr<Env> shared;
std::shared_ptr<ObjectRegistry> registry = ObjectRegistry::NewInstance(); std::shared_ptr<ObjectRegistry> registry = ObjectRegistry::NewInstance();
std::shared_ptr<ObjectLibrary> library = std::make_shared<ObjectLibrary>(); std::shared_ptr<ObjectLibrary> library =
std::make_shared<ObjectLibrary>("shared");
registry->AddLibrary(library); registry->AddLibrary(library);
library->Register<Env>( library->Register<Env>(
"unguarded", "unguarded",
@ -111,7 +113,8 @@ TEST_F(EnvRegistryTest, CheckShared) {
TEST_F(EnvRegistryTest, CheckStatic) { TEST_F(EnvRegistryTest, CheckStatic) {
Env* env = nullptr; Env* env = nullptr;
std::shared_ptr<ObjectRegistry> registry = ObjectRegistry::NewInstance(); std::shared_ptr<ObjectRegistry> registry = ObjectRegistry::NewInstance();
std::shared_ptr<ObjectLibrary> library = std::make_shared<ObjectLibrary>(); std::shared_ptr<ObjectLibrary> library =
std::make_shared<ObjectLibrary>("static");
registry->AddLibrary(library); registry->AddLibrary(library);
library->Register<Env>( library->Register<Env>(
"unguarded", "unguarded",
@ -135,7 +138,8 @@ TEST_F(EnvRegistryTest, CheckStatic) {
TEST_F(EnvRegistryTest, CheckUnique) { TEST_F(EnvRegistryTest, CheckUnique) {
std::unique_ptr<Env> unique; std::unique_ptr<Env> unique;
std::shared_ptr<ObjectRegistry> registry = ObjectRegistry::NewInstance(); std::shared_ptr<ObjectRegistry> registry = ObjectRegistry::NewInstance();
std::shared_ptr<ObjectLibrary> library = std::make_shared<ObjectLibrary>(); std::shared_ptr<ObjectLibrary> library =
std::make_shared<ObjectLibrary>("unique");
registry->AddLibrary(library); registry->AddLibrary(library);
library->Register<Env>( library->Register<Env>(
"unguarded", "unguarded",
@ -156,6 +160,51 @@ TEST_F(EnvRegistryTest, CheckUnique) {
ASSERT_EQ(unique, nullptr); ASSERT_EQ(unique, nullptr);
} }
TEST_F(EnvRegistryTest, TestRegistryParents) {
auto grand = ObjectRegistry::Default();
auto parent = ObjectRegistry::NewInstance(); // parent with a grandparent
auto uncle = ObjectRegistry::NewInstance(grand);
auto child = ObjectRegistry::NewInstance(parent);
auto cousin = ObjectRegistry::NewInstance(uncle);
auto library = parent->AddLibrary("parent");
library->Register<Env>(
"parent", [](const std::string& /*uri*/, std::unique_ptr<Env>* guard,
std::string* /* errmsg */) {
guard->reset(new EnvWrapper(Env::Default()));
return guard->get();
});
library = cousin->AddLibrary("cousin");
library->Register<Env>(
"cousin", [](const std::string& /*uri*/, std::unique_ptr<Env>* guard,
std::string* /* errmsg */) {
guard->reset(new EnvWrapper(Env::Default()));
return guard->get();
});
std::unique_ptr<Env> guard;
std::string msg;
// a:://* is registered in Default, so they should all workd
ASSERT_NE(parent->NewObject<Env>("a://test", &guard, &msg), nullptr);
ASSERT_NE(child->NewObject<Env>("a://test", &guard, &msg), nullptr);
ASSERT_NE(uncle->NewObject<Env>("a://test", &guard, &msg), nullptr);
ASSERT_NE(cousin->NewObject<Env>("a://test", &guard, &msg), nullptr);
// The parent env is only registered for parent, not uncle,
// So parent and child should return success and uncle and cousin should fail
ASSERT_OK(parent->NewUniqueObject<Env>("parent", &guard));
ASSERT_OK(child->NewUniqueObject<Env>("parent", &guard));
ASSERT_NOK(uncle->NewUniqueObject<Env>("parent", &guard));
ASSERT_NOK(cousin->NewUniqueObject<Env>("parent", &guard));
// The cousin is only registered in the cousin, so all of the others should
// fail
ASSERT_OK(cousin->NewUniqueObject<Env>("cousin", &guard));
ASSERT_NOK(parent->NewUniqueObject<Env>("cousin", &guard));
ASSERT_NOK(child->NewUniqueObject<Env>("cousin", &guard));
ASSERT_NOK(uncle->NewUniqueObject<Env>("cousin", &guard));
}
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) { int main(int argc, char** argv) {

View File

@ -121,7 +121,7 @@ Status CheckOptionsCompatibility(
const std::string& dbpath, Env* env, const DBOptions& db_options, const std::string& dbpath, Env* env, const DBOptions& db_options,
const std::vector<ColumnFamilyDescriptor>& cf_descs, const std::vector<ColumnFamilyDescriptor>& cf_descs,
bool ignore_unknown_options) { bool ignore_unknown_options) {
ConfigOptions config_options; ConfigOptions config_options(db_options);
config_options.sanity_level = ConfigOptions::kSanityLevelLooselyCompatible; config_options.sanity_level = ConfigOptions::kSanityLevelLooselyCompatible;
config_options.ignore_unknown_options = ignore_unknown_options; config_options.ignore_unknown_options = ignore_unknown_options;
config_options.input_strings_escaped = true; config_options.input_strings_escaped = true;