From 89f66d44840f7e51055580ff8f7e37e10371adc1 Mon Sep 17 00:00:00 2001 From: mrambacher Date: Tue, 29 Jun 2021 09:07:10 -0700 Subject: [PATCH] Add customizable_util.h to the public API (#8301) Summary: Useful for allowing new classes to create and manage Customizable objects without using internal APIs. Pull Request resolved: https://github.com/facebook/rocksdb/pull/8301 Reviewed By: zhichao-cao Differential Revision: D29428303 Pulled By: mrambacher fbshipit-source-id: 3d33d5197cc8379fe35b54d3d169f91f0dfe7a47 --- HISTORY.md | 2 + include/rocksdb/customizable.h | 23 ++ include/rocksdb/utilities/customizable_util.h | 293 ++++++++++++++++++ options/configurable.cc | 43 +-- options/configurable_helper.h | 19 -- options/customizable.cc | 30 ++ options/customizable_helper.h | 221 ------------- options/customizable_test.cc | 5 +- table/table_factory.cc | 47 ++- util/comparator.cc | 21 +- 10 files changed, 398 insertions(+), 306 deletions(-) create mode 100644 include/rocksdb/utilities/customizable_util.h delete mode 100644 options/customizable_helper.h diff --git a/HISTORY.md b/HISTORY.md index 58ab48335..b080090ff 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -12,6 +12,8 @@ * The new BlobDB implementation now tracks the amount of garbage in each blob file in the MANIFEST. * Integrated BlobDB now supports Merge with base values (Put/Delete etc.). +### Public API change +* Added APIs to the Customizable class to allow developers to create their own Customizable classes. Created the utilities/customizable_util.h file to contain helper methods for developing new Customizable classes. ## 6.22.0 (2021-06-18) ### Behavior Changes * Added two additional tickers, MEMTABLE_PAYLOAD_BYTES_AT_FLUSH and MEMTABLE_GARBAGE_BYTES_AT_FLUSH. These stats can be used to estimate the ratio of "garbage" (outdated) bytes in the memtable that are discarded at flush time. diff --git a/include/rocksdb/customizable.h b/include/rocksdb/customizable.h index 24ddfa56c..48147e1f6 100644 --- a/include/rocksdb/customizable.h +++ b/include/rocksdb/customizable.h @@ -138,6 +138,29 @@ class Customizable : public Configurable { Status GetOption(const ConfigOptions& config_options, const std::string& name, std::string* value) const override; #endif // ROCKSDB_LITE + // Helper method for getting for parsing the opt_value into the corresponding + // options for use in potentially creating a new Customizable object (this + // method is primarily a support method for LoadSharedObject et al for new + // Customizable objects). The opt_value may be either name-value pairs + // separated by ";" (a=b; c=d), or a simple name (a). In order to create a new + // Customizable, the ID is determined by: + // - If the value is a simple name (e.g. "BlockBasedTable"), the id is this + // name; + // - Otherwise, if there is a "id=value", the id is set to "value" + // - Otherwise, if the input customizable is not null, custom->GetId is used + // - Otherwise, an error is returned. + // + // If the opt_value is name-value pairs, these pairs will be returned in + // options (without the id pair). If the ID being returned matches the ID of + // the input custom object, then the options from the input object will also + // be added to the returned options. + // + // This method returns non-OK if the ID could not be found, or if the + // opt_value could not be parsed into name-value pairs. + static Status GetOptionsMap( + const ConfigOptions& config_options, const Customizable* custom, + const std::string& opt_value, std::string* id, + std::unordered_map* options); // Returns the inner class when a Customizable implements a has-a (wrapped) // relationship. Derived classes that implement a has-a must override this diff --git a/include/rocksdb/utilities/customizable_util.h b/include/rocksdb/utilities/customizable_util.h new file mode 100644 index 000000000..447873fae --- /dev/null +++ b/include/rocksdb/utilities/customizable_util.h @@ -0,0 +1,293 @@ +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). + +#pragma once +#include +#include +#include + +#include "options/configurable_helper.h" +#include "rocksdb/convenience.h" +#include "rocksdb/customizable.h" +#include "rocksdb/status.h" +#include "rocksdb/utilities/object_registry.h" + +namespace ROCKSDB_NAMESPACE { +// The FactoryFunc functions are used to create a new customizable object +// without going through the ObjectRegistry. This methodology is especially +// useful in LITE mode, where there is no ObjectRegistry. The methods take +// in an ID of the object to create and a pointer to store the created object. +// If the factory successfully recognized the input ID, the method should return +// success; otherwise false should be returned. On success, the object +// parameter contains the new object. +template +using SharedFactoryFunc = + std::function*)>; + +template +using UniqueFactoryFunc = + std::function*)>; + +template +using StaticFactoryFunc = std::function; + +// Creates a new shared customizable instance object based on the +// input parameters using the object registry. +// +// The id parameter specifies the instance class of the object to create. +// The opt_map parameter specifies the configuration of the new instance. +// +// The config_options parameter controls the process and how errors are +// returned. If ignore_unknown_options=true, unknown values are ignored during +// the configuration. If ignore_unsupported_options=true, unknown instance types +// are ignored. If invoke_prepare_options=true, the resulting instance will be +// initialized (via PrepareOptions) +// +// @param config_options Controls how the instance is created and errors are +// handled +// @param id The identifier of the new object being created. This string +// will be used by the object registry to locate the appropriate object to +// create. +// @param opt_map Optional name-value pairs of properties to set for the newly +// created object +// @param result The newly created and configured instance. +template +static Status NewSharedObject( + const ConfigOptions& config_options, const std::string& id, + const std::unordered_map& opt_map, + std::shared_ptr* result) { + Status status; + if (!id.empty()) { +#ifndef ROCKSDB_LITE + status = config_options.registry->NewSharedObject(id, result); +#else + status = Status::NotSupported("Cannot load object in LITE mode ", id); +#endif // ROCKSDB_LITE + if (config_options.ignore_unsupported_options && status.IsNotSupported()) { + return Status::OK(); + } + } else { + status = Status::NotSupported("Cannot reset object "); + } + if (!status.ok() || opt_map.empty()) { + return status; + } else if (result->get() != nullptr) { + return result->get()->ConfigureFromMap(config_options, opt_map); + } else { + return Status::InvalidArgument("Cannot configure null object ", id); + } +} + +// Creates a new shared Customizable object based on the input parameters. +// This method parses the input value to determine the type of instance to +// create. If there is an existing instance (in result) and it is the same ID +// as the object being created, the existing configuration is stored and used as +// the default for the new object. +// +// The value parameter specified the instance class of the object to create. +// If it is a simple string (e.g. BlockBasedTable), then the instance will be +// created using the default settings. If the value is a set of name-value +// pairs, then the "id" value is used to determine the instance to create and +// the remaining parameters are used to configure the object. Id name-value +// pairs are specified, there should be an "id=value" pairing or an error may +// result. +// +// The config_options parameter controls the process and how errors are +// returned. If ignore_unknown_options=true, unknown values are ignored during +// the configuration. If ignore_unsupported_options=true, unknown instance types +// are ignored. If invoke_prepare_options=true, the resulting instance will be +// initialized (via PrepareOptions) +// +// @param config_options Controls how the instance is created and errors are +// handled +// @param value Either the simple name of the instance to create, or a set of +// name-value pairs to create and initailize the object +// @param func Optional function to call to attempt to create an instance +// @param result The newly created instance. +template +static Status LoadSharedObject(const ConfigOptions& config_options, + const std::string& value, + const SharedFactoryFunc& func, + std::shared_ptr* result) { + std::string id; + std::unordered_map opt_map; + + Status status = Customizable::GetOptionsMap(config_options, result->get(), + value, &id, &opt_map); + if (!status.ok()) { // GetOptionsMap failed + return status; + } else if (func == nullptr || + !func(id, result)) { // No factory, or it failed + if (value.empty()) { // No Id and no options. Clear the object + *result = nullptr; + return Status::OK(); + } else { + return NewSharedObject(config_options, id, opt_map, result); + } + } else if (opt_map.empty()) { + return status; + } else if (result->get() != nullptr) { + return result->get()->ConfigureFromMap(config_options, opt_map); + } else { + return Status::InvalidArgument("Cannot configure null object "); + } +} + +// Creates a new unique pointer customizable instance object based on the +// input parameters using the object registry. +// @see NewSharedObject for more information on the inner workings of this +// method. +// +// @param config_options Controls how the instance is created and errors are +// handled +// @param id The identifier of the new object being created. This string +// will be used by the object registry to locate the appropriate object to +// create. +// @param opt_map Optional name-value pairs of properties to set for the newly +// created object +// @param result The newly created and configured instance. +template +static Status NewUniqueObject( + const ConfigOptions& config_options, const std::string& id, + const std::unordered_map& opt_map, + std::unique_ptr* result) { + Status status; + if (id.empty()) { + status = Status::NotSupported("Cannot reset object "); + } else { +#ifndef ROCKSDB_LITE + status = config_options.registry->NewUniqueObject(id, result); +#else + status = Status::NotSupported("Cannot load object in LITE mode ", id); +#endif // ROCKSDB_LITE + if (config_options.ignore_unsupported_options && status.IsNotSupported()) { + return Status::OK(); + } + } + if (!status.ok() || opt_map.empty()) { + return status; + } else if (result->get() != nullptr) { + return result->get()->ConfigureFromMap(config_options, opt_map); + } else { + return Status::InvalidArgument("Cannot configure null object "); + } +} + +// Creates a new unique customizable instance object based on the input +// parameters. +// @see LoadSharedObject for more information on the inner workings of this +// method. +// +// @param config_options Controls how the instance is created and errors are +// handled +// @param value Either the simple name of the instance to create, or a set of +// name-value pairs to create and initailize the object +// @param func Optional function to call to attempt to create an instance +// @param result The newly created instance. +template +static Status LoadUniqueObject(const ConfigOptions& config_options, + const std::string& value, + const UniqueFactoryFunc& func, + std::unique_ptr* result) { + std::string id; + std::unordered_map opt_map; + Status status = Customizable::GetOptionsMap(config_options, result->get(), + value, &id, &opt_map); + if (!status.ok()) { // GetOptionsMap failed + return status; + } else if (func == nullptr || + !func(id, result)) { // No factory, or it failed + if (value.empty()) { // No Id and no options. Clear the object + *result = nullptr; + return Status::OK(); + } else { + return NewUniqueObject(config_options, id, opt_map, result); + } + } else if (opt_map.empty()) { + return status; + } else if (result->get() != nullptr) { + return result->get()->ConfigureFromMap(config_options, opt_map); + } else { + return Status::InvalidArgument("Cannot configure null object "); + } +} + +// Creates a new static (raw pointer) customizable instance object based on the +// input parameters using the object registry. +// @see NewSharedObject for more information on the inner workings of this +// method. +// +// @param config_options Controls how the instance is created and errors are +// handled +// @param id The identifier of the new object being created. This string +// will be used by the object registry to locate the appropriate object to +// create. +// @param opt_map Optional name-value pairs of properties to set for the newly +// created object +// @param result The newly created and configured instance. +template +static Status NewStaticObject( + const ConfigOptions& config_options, const std::string& id, + const std::unordered_map& opt_map, T** result) { + Status status; + if (id.empty()) { + status = Status::NotSupported("Cannot reset object "); + } else { +#ifndef ROCKSDB_LITE + status = config_options.registry->NewStaticObject(id, result); +#else + status = Status::NotSupported("Cannot load object in LITE mode ", id); +#endif // ROCKSDB_LITE + if (config_options.ignore_unsupported_options && status.IsNotSupported()) { + return Status::OK(); + } + } + if (!status.ok() || opt_map.empty()) { + return status; + } else if (*result != nullptr) { + return (*result)->ConfigureFromMap(config_options, opt_map); + } else { + return Status::InvalidArgument("Cannot configure null object "); + } +} + +// Creates a new static (raw pointer) customizable instance object based on the +// input parameters. +// @see LoadSharedObject for more information on the inner workings of this +// method. +// +// @param config_options Controls how the instance is created and errors are +// handled +// @param value Either the simple name of the instance to create, or a set of +// name-value pairs to create and initailize the object +// @param func Optional function to call to attempt to create an instance +// @param result The newly created instance. +template +static Status LoadStaticObject(const ConfigOptions& config_options, + const std::string& value, + const StaticFactoryFunc& func, T** result) { + std::string id; + std::unordered_map opt_map; + Status status = Customizable::GetOptionsMap(config_options, *result, value, + &id, &opt_map); + if (!status.ok()) { // GetOptionsMap failed + return status; + } else if (func == nullptr || + !func(id, result)) { // No factory, or it failed + if (value.empty()) { // No Id and no options. Clear the object + *result = nullptr; + return Status::OK(); + } else { + return NewStaticObject(config_options, id, opt_map, result); + } + } else if (opt_map.empty()) { + return status; + } else if (*result != nullptr) { + return (*result)->ConfigureFromMap(config_options, opt_map); + } else { + return Status::InvalidArgument("Cannot configure null object "); + } +} +} // namespace ROCKSDB_NAMESPACE diff --git a/options/configurable.cc b/options/configurable.cc index f425f193c..4444a408f 100644 --- a/options/configurable.cc +++ b/options/configurable.cc @@ -398,10 +398,9 @@ Status ConfigurableHelper::ConfigureCustomizableOption( if (opt_info.IsMutable() || !config_options.mutable_options_only) { // Either the option is mutable, or we are processing all of the options - if (opt_name == name || - EndsWith(opt_name, ConfigurableHelper::kIdPropSuffix) || - name == ConfigurableHelper::kIdPropName) { - return configurable.ParseOption(copy, opt_info, opt_name, value, opt_ptr); + if (opt_name == name || name == ConfigurableHelper::kIdPropName || + EndsWith(opt_name, ConfigurableHelper::kIdPropSuffix)) { + return configurable.ParseOption(copy, opt_info, name, value, opt_ptr); } else if (value.empty()) { return Status::OK(); } else if (custom == nullptr || !StartsWith(name, custom->GetId() + ".")) { @@ -480,32 +479,6 @@ Status ConfigurableHelper::ConfigureOption( } #endif // ROCKSDB_LITE -Status ConfigurableHelper::ConfigureNewObject( - const ConfigOptions& config_options_in, Configurable* object, - const std::string& id, const std::string& base_opts, - const std::unordered_map& opts) { - if (object != nullptr) { - ConfigOptions config_options = config_options_in; - config_options.invoke_prepare_options = false; - if (!base_opts.empty()) { -#ifndef ROCKSDB_LITE - // Don't run prepare options on the base, as we would do that on the - // overlay opts instead - Status status = object->ConfigureFromString(config_options, base_opts); - if (!status.ok()) { - return status; - } -#endif // ROCKSDB_LITE - } - if (!opts.empty()) { - return object->ConfigureFromMap(config_options, opts); - } - } else if (!opts.empty()) { // No object but no map. This is OK - return Status::InvalidArgument("Cannot configure null object ", id); - } - return Status::OK(); -} - //******************************************************************************* // // Methods for Converting Options into strings @@ -744,16 +717,6 @@ bool ConfigurableHelper::AreEquivalent(const ConfigOptions& config_options, } #endif // ROCKSDB_LITE -Status ConfigurableHelper::GetOptionsMap( - const std::string& value, const Customizable* customizable, std::string* id, - std::unordered_map* props) { - if (customizable != nullptr) { - return GetOptionsMap(value, customizable->GetId(), id, props); - } else { - return GetOptionsMap(value, "", id, props); - } -} - Status ConfigurableHelper::GetOptionsMap( const std::string& value, const std::string& default_id, std::string* id, std::unordered_map* props) { diff --git a/options/configurable_helper.h b/options/configurable_helper.h index b822b0b8e..b286fc2ee 100644 --- a/options/configurable_helper.h +++ b/options/configurable_helper.h @@ -48,22 +48,6 @@ class ConfigurableHelper { const std::unordered_map& options, std::unordered_map* unused); - // Helper method for configuring a new customizable object. - // If base_opts are set, this is the "default" options to use for the new - // object. Then any values in "new_opts" are applied to the object. - // Returns OK if the object could be successfully configured - // @return NotFound If any of the names in the base or new opts were not valid - // for this object. - // @return NotSupported If any of the names are valid but the object does - // not know how to convert the value. This can happen if, for example, - // there is some nested Configurable that cannot be created. - // @return InvalidArgument If any of the values cannot be successfully - // parsed. - static Status ConfigureNewObject( - const ConfigOptions& config_options, Configurable* object, - const std::string& id, const std::string& base_opts, - const std::unordered_map& new_opts); - // Splits the input opt_value into the ID field and the remaining options. // The input opt_value can be in the form of "name" or "name=value // [;name=value]". The first form uses the "name" as an id with no options The @@ -78,9 +62,6 @@ class ConfigurableHelper { // found. // @return InvalidArgument if the value could not be converted to a map or // there was or there is no id property in the map. - static Status GetOptionsMap( - const std::string& opt_value, const Customizable* custom, std::string* id, - std::unordered_map* options); static Status GetOptionsMap( const std::string& opt_value, const std::string& default_id, std::string* id, std::unordered_map* options); diff --git a/options/customizable.cc b/options/customizable.cc index 3488f326b..2c599e138 100644 --- a/options/customizable.cc +++ b/options/customizable.cc @@ -6,6 +6,7 @@ #include "rocksdb/customizable.h" #include "options/configurable_helper.h" +#include "options/options_helper.h" #include "rocksdb/convenience.h" #include "rocksdb/status.h" #include "util/string_util.h" @@ -74,4 +75,33 @@ bool Customizable::AreEquivalent(const ConfigOptions& config_options, return true; } +Status Customizable::GetOptionsMap( + const ConfigOptions& config_options, const Customizable* customizable, + const std::string& value, std::string* id, + std::unordered_map* props) { + if (customizable != nullptr) { + Status status = ConfigurableHelper::GetOptionsMap( + value, customizable->GetId(), id, props); +#ifdef ROCKSDB_LITE + (void)config_options; +#else + if (status.ok() && customizable->IsInstanceOf(*id)) { + // The new ID and the old ID match, so the objects are the same type. + // Try to get the existing options, ignoring any errors + ConfigOptions embedded = config_options; + embedded.delimiter = ";"; + std::string curr_opts; + if (customizable->GetOptionString(embedded, &curr_opts).ok()) { + std::unordered_map curr_props; + if (StringToMap(curr_opts, &curr_props).ok()) { + props->insert(curr_props.begin(), curr_props.end()); + } + } + } +#endif // ROCKSDB_LITE + return status; + } else { + return ConfigurableHelper::GetOptionsMap(value, "", id, props); + } +} } // namespace ROCKSDB_NAMESPACE diff --git a/options/customizable_helper.h b/options/customizable_helper.h deleted file mode 100644 index cd7cc26f8..000000000 --- a/options/customizable_helper.h +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. -// This source code is licensed under both the GPLv2 (found in the -// COPYING file in the root directory) and Apache 2.0 License -// (found in the LICENSE.Apache file in the root directory). - -#pragma once -#include -#include -#include - -#include "options/configurable_helper.h" -#include "rocksdb/convenience.h" -#include "rocksdb/customizable.h" -#include "rocksdb/status.h" -#include "rocksdb/utilities/object_registry.h" - -namespace ROCKSDB_NAMESPACE { -template -using SharedFactoryFunc = - std::function*)>; - -template -using UniqueFactoryFunc = - std::function*)>; - -template -using StaticFactoryFunc = std::function; - -// Creates a new shared Customizable object based on the input parameters. -// This method parses the input value to determine the type of instance to -// create. If there is an existing instance (in result) and it is the same type -// as the object being created, the existing configuration is stored and used as -// the default for the new object. -// -// The value parameter specified the instance class of the object to create. -// If it is a simple string (e.g. BlockBasedTable), then the instance will be -// created using the default settings. If the value is a set of name-value -// pairs, then the "id" value is used to determine the instance to create and -// the remaining parameters are used to configure the object. Id name-value -// pairs are specified, there must be an "id=value" pairing or an error will -// result. -// -// The config_options parameter controls the process and how errors are -// returned. If ignore_unknown_options=true, unknown values are ignored during -// the configuration If ignore_unsupported_options=true, unknown instance types -// are ignored If invoke_prepare_options=true, the resulting instance wll be -// initialized (via PrepareOptions -// -// @param config_options Controls how the instance is created and errors are -// handled -// @param value Either the simple name of the instance to create, or a set of -// name-value pairs to -// create and initailzie the object -// @param func Optional function to call to attempt to create an instance -// @param result The newly created instance. -template -static Status LoadSharedObject(const ConfigOptions& config_options, - const std::string& value, - const SharedFactoryFunc& func, - std::shared_ptr* result) { - std::string id; - std::unordered_map opt_map; - Status status = - ConfigurableHelper::GetOptionsMap(value, result->get(), &id, &opt_map); - if (!status.ok()) { // GetOptionsMap failed - return status; - } - std::string curr_opts; -#ifndef ROCKSDB_LITE - if (result->get() != nullptr && result->get()->GetId() == id) { - // Try to get the existing options, ignoring any errors - ConfigOptions embedded = config_options; - embedded.delimiter = ";"; - result->get()->GetOptionString(embedded, &curr_opts).PermitUncheckedError(); - } -#endif - if (func == nullptr || !func(id, result)) { // No factory, or it failed - if (value.empty()) { - // No Id and no options. Clear the object - result->reset(); - return Status::OK(); - } else if (id.empty()) { // We have no Id but have options. Not good - return Status::NotSupported("Cannot reset object ", id); - } else { -#ifndef ROCKSDB_LITE - status = config_options.registry->NewSharedObject(id, result); -#else - status = Status::NotSupported("Cannot load object in LITE mode ", id); -#endif - if (!status.ok()) { - if (config_options.ignore_unsupported_options && - status.IsNotSupported()) { - return Status::OK(); - } else { - return status; - } - } - } - } - return ConfigurableHelper::ConfigureNewObject(config_options, result->get(), - id, curr_opts, opt_map); -} - -// Creates a new unique customizable instance object based on the input -// parameters. -// @see LoadSharedObject for more information on the inner workings of this -// method. -// -// @param config_options Controls how the instance is created and errors are -// handled -// @param value Either the simple name of the instance to create, or a set of -// name-value pairs to -// create and initailzie the object -// @param func Optional function to call to attempt to create an instance -// @param result The newly created instance. -template -static Status LoadUniqueObject(const ConfigOptions& config_options, - const std::string& value, - const UniqueFactoryFunc& func, - std::unique_ptr* result) { - std::string id; - std::unordered_map opt_map; - Status status = - ConfigurableHelper::GetOptionsMap(value, result->get(), &id, &opt_map); - if (!status.ok()) { // GetOptionsMap failed - return status; - } - std::string curr_opts; -#ifndef ROCKSDB_LITE - if (result->get() != nullptr && result->get()->GetId() == id) { - // Try to get the existing options, ignoring any errors - ConfigOptions embedded = config_options; - embedded.delimiter = ";"; - result->get()->GetOptionString(embedded, &curr_opts).PermitUncheckedError(); - } -#endif - if (func == nullptr || !func(id, result)) { // No factory, or it failed - if (value.empty()) { - // No Id and no options. Clear the object - result->reset(); - return Status::OK(); - } else if (id.empty()) { // We have no Id but have options. Not good - return Status::NotSupported("Cannot reset object ", id); - } else { -#ifndef ROCKSDB_LITE - status = config_options.registry->NewUniqueObject(id, result); -#else - status = Status::NotSupported("Cannot load object in LITE mode ", id); -#endif // ROCKSDB_LITE - if (!status.ok()) { - if (config_options.ignore_unsupported_options && - status.IsNotSupported()) { - return Status::OK(); - } else { - return status; - } - } - } - } - return ConfigurableHelper::ConfigureNewObject(config_options, result->get(), - id, curr_opts, opt_map); -} -// Creates a new static (raw pointer) customizable instance object based on the -// input parameters. -// @see LoadSharedObject for more information on the inner workings of this -// method. -// -// @param config_options Controls how the instance is created and errors are -// handled -// @param value Either the simple name of the instance to create, or a set of -// name-value pairs to -// create and initailzie the object -// @param func Optional function to call to attempt to create an instance -// @param result The newly created instance. -template -static Status LoadStaticObject(const ConfigOptions& config_options, - const std::string& value, - const StaticFactoryFunc& func, T** result) { - std::string id; - std::unordered_map opt_map; - Status status = - ConfigurableHelper::GetOptionsMap(value, *result, &id, &opt_map); - if (!status.ok()) { // GetOptionsMap failed - return status; - } - std::string curr_opts; -#ifndef ROCKSDB_LITE - if (*result != nullptr && (*result)->GetId() == id) { - // Try to get the existing options, ignoring any errors - ConfigOptions embedded = config_options; - embedded.delimiter = ";"; - (*result)->GetOptionString(embedded, &curr_opts).PermitUncheckedError(); - } -#endif - if (func == nullptr || !func(id, result)) { // No factory, or it failed - if (value.empty()) { - // No Id and no options. Clear the object - *result = nullptr; - return Status::OK(); - } else if (id.empty()) { // We have no Id but have options. Not good - return Status::NotSupported("Cannot reset object ", id); - } else { -#ifndef ROCKSDB_LITE - status = config_options.registry->NewStaticObject(id, result); -#else - status = Status::NotSupported("Cannot load object in LITE mode ", id); -#endif // ROCKSDB_LITE - if (!status.ok()) { - if (config_options.ignore_unsupported_options && - status.IsNotSupported()) { - return Status::OK(); - } else { - return status; - } - } - } - } - return ConfigurableHelper::ConfigureNewObject(config_options, *result, id, - curr_opts, opt_map); -} -} // namespace ROCKSDB_NAMESPACE diff --git a/options/customizable_test.cc b/options/customizable_test.cc index 315994641..ea8dd51f7 100644 --- a/options/customizable_test.cc +++ b/options/customizable_test.cc @@ -15,10 +15,10 @@ #include #include "options/configurable_helper.h" -#include "options/customizable_helper.h" #include "options/options_helper.h" #include "options/options_parser.h" #include "rocksdb/convenience.h" +#include "rocksdb/utilities/customizable_util.h" #include "rocksdb/utilities/object_registry.h" #include "rocksdb/utilities/options_type.h" #include "table/mock_table.h" @@ -607,6 +607,9 @@ TEST_F(CustomizableTest, NewCustomizableTest) { ASSERT_OK(base->ConfigureFromString(config_options_, "unique={id=A_1;int=1;bool=false}")); ASSERT_EQ(A_count, 2); // Create another A_1 + ASSERT_OK(base->ConfigureFromString(config_options_, "unique={id=}")); + ASSERT_EQ(simple->cu, nullptr); + ASSERT_EQ(A_count, 2); ASSERT_OK(base->ConfigureFromString(config_options_, "unique={id=A_2;int=1;bool=false}")); ASSERT_EQ(A_count, 3); // Created another A diff --git a/table/table_factory.cc b/table/table_factory.cc index 962bad9ba..0fbb57cf5 100644 --- a/table/table_factory.cc +++ b/table/table_factory.cc @@ -3,35 +3,62 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. -#include "options/customizable_helper.h" +#include + #include "rocksdb/convenience.h" #include "rocksdb/table.h" +#include "rocksdb/utilities/customizable_util.h" +#include "rocksdb/utilities/object_registry.h" #include "table/block_based/block_based_table_factory.h" #include "table/cuckoo/cuckoo_table_factory.h" #include "table/plain/plain_table_factory.h" namespace ROCKSDB_NAMESPACE { +static void RegisterTableFactories(const std::string& /*arg*/) { +#ifndef ROCKSDB_LITE + static std::once_flag loaded; + std::call_once(loaded, []() { + auto library = ObjectLibrary::Default(); + library->Register( + TableFactory::kBlockBasedTableName(), + [](const std::string& /*uri*/, std::unique_ptr* guard, + std::string* /* errmsg */) { + guard->reset(new BlockBasedTableFactory()); + return guard->get(); + }); + library->Register( + TableFactory::kPlainTableName(), + [](const std::string& /*uri*/, std::unique_ptr* guard, + std::string* /* errmsg */) { + guard->reset(new PlainTableFactory()); + return guard->get(); + }); + library->Register( + TableFactory::kCuckooTableName(), + [](const std::string& /*uri*/, std::unique_ptr* guard, + std::string* /* errmsg */) { + guard->reset(new CuckooTableFactory()); + return guard->get(); + }); + }); +#endif // ROCKSDB_LITE +} + static bool LoadFactory(const std::string& name, std::shared_ptr* factory) { - bool success = true; if (name == TableFactory::kBlockBasedTableName()) { factory->reset(new BlockBasedTableFactory()); -#ifndef ROCKSDB_LITE - } else if (name == TableFactory::kPlainTableName()) { - factory->reset(new PlainTableFactory()); - } else if (name == TableFactory::kCuckooTableName()) { - factory->reset(new CuckooTableFactory()); -#endif // ROCKSDB_LITE + return true; } else { - success = false; + return false; } - return success; } Status TableFactory::CreateFromString(const ConfigOptions& config_options, const std::string& value, std::shared_ptr* factory) { + RegisterTableFactories(""); return LoadSharedObject(config_options, value, LoadFactory, factory); } diff --git a/util/comparator.cc b/util/comparator.cc index 0ed391f9b..0cdce3a36 100644 --- a/util/comparator.cc +++ b/util/comparator.cc @@ -15,9 +15,10 @@ #include #include -#include "options/configurable_helper.h" #include "port/port.h" +#include "rocksdb/convenience.h" #include "rocksdb/slice.h" +#include "rocksdb/utilities/customizable_util.h" #include "rocksdb/utilities/object_registry.h" namespace ROCKSDB_NAMESPACE { @@ -255,20 +256,11 @@ Status Comparator::CreateFromString(const ConfigOptions& config_options, #endif // ROCKSDB_LITE std::string id; std::unordered_map opt_map; - Status status = - ConfigurableHelper::GetOptionsMap(value, *result, &id, &opt_map); + Status status = Customizable::GetOptionsMap(config_options, *result, value, + &id, &opt_map); if (!status.ok()) { // GetOptionsMap failed return status; } - std::string curr_opts; -#ifndef ROCKSDB_LITE - if (*result != nullptr && (*result)->GetId() == id) { - // Try to get the existing options, ignoring any errors - ConfigOptions embedded = config_options; - embedded.delimiter = ";"; - (*result)->GetOptionString(embedded, &curr_opts).PermitUncheckedError(); - } -#endif if (id == BytewiseComparatorImpl::kClassName()) { *result = BytewiseComparator(); } else if (id == ReverseBytewiseComparatorImpl::kClassName()) { @@ -292,10 +284,9 @@ Status Comparator::CreateFromString(const ConfigOptions& config_options, } else { return status; } - } else if (!curr_opts.empty() || !opt_map.empty()) { + } else if (!opt_map.empty()) { Comparator* comparator = const_cast(*result); - status = ConfigurableHelper::ConfigureNewObject( - config_options, comparator, id, curr_opts, opt_map); + status = comparator->ConfigureFromMap(config_options, opt_map); } } return status;