Bring the Configurable options together (#5753)
Summary: This PR merges the functionality of making the ColumnFamilyOptions, TableFactory, and DBOptions into Configurable into a single PR, resolving any merge conflicts Pull Request resolved: https://github.com/facebook/rocksdb/pull/5753 Reviewed By: ajkr Differential Revision: D23385030 Pulled By: zhichao-cao fbshipit-source-id: 8b977a7731556230b9b8c5a081b98e49ee4f160amain
parent
18a3227b12
commit
7d472accdc
@ -0,0 +1,364 @@ |
|||||||
|
// 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).
|
||||||
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <string> |
||||||
|
#include <unordered_map> |
||||||
|
#include <unordered_set> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "rocksdb/rocksdb_namespace.h" |
||||||
|
#include "rocksdb/status.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
class Logger; |
||||||
|
class ObjectRegistry; |
||||||
|
class OptionTypeInfo; |
||||||
|
struct ColumnFamilyOptions; |
||||||
|
struct ConfigOptions; |
||||||
|
struct DBOptions; |
||||||
|
|
||||||
|
// Configurable is a base class used by the rocksdb that describes a
|
||||||
|
// standard way of configuring objects. A Configurable object can:
|
||||||
|
// -> Populate itself given:
|
||||||
|
// - One or more "name/value" pair strings
|
||||||
|
// - A string repesenting the set of name=value properties
|
||||||
|
// - A map of name/value properties.
|
||||||
|
// -> Convert itself into its string representation
|
||||||
|
// -> Dump itself to a Logger
|
||||||
|
// -> Compare itself to another Configurable object to see if the two objects
|
||||||
|
// have equivalent options settings
|
||||||
|
//
|
||||||
|
// If a derived class calls RegisterOptions to register (by name) how its
|
||||||
|
// options objects are to be processed, this functionality can typically be
|
||||||
|
// handled by this class without additional overrides. Otherwise, the derived
|
||||||
|
// class will need to implement the methods for handling the corresponding
|
||||||
|
// functionality.
|
||||||
|
class Configurable { |
||||||
|
protected: |
||||||
|
friend class ConfigurableHelper; |
||||||
|
struct RegisteredOptions { |
||||||
|
// The name of the options being registered
|
||||||
|
std::string name; |
||||||
|
// Pointer to the object being registered
|
||||||
|
void* opt_ptr; |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
// The map of options being registered
|
||||||
|
const std::unordered_map<std::string, OptionTypeInfo>* type_map; |
||||||
|
#endif |
||||||
|
}; |
||||||
|
|
||||||
|
public: |
||||||
|
Configurable() : prepared_(false) {} |
||||||
|
virtual ~Configurable() {} |
||||||
|
|
||||||
|
// Returns the raw pointer of the named options that is used by this
|
||||||
|
// object, or nullptr if this function is not supported.
|
||||||
|
// Since the return value is a raw pointer, the object owns the
|
||||||
|
// pointer and the caller should not delete the pointer.
|
||||||
|
//
|
||||||
|
// Note that changing the underlying options while the object
|
||||||
|
// is currently used by any open DB is undefined behavior.
|
||||||
|
// Developers should use DB::SetOption() instead to dynamically change
|
||||||
|
// options while the DB is open.
|
||||||
|
template <typename T> |
||||||
|
const T* GetOptions() const { |
||||||
|
return GetOptions<T>(T::kName()); |
||||||
|
} |
||||||
|
template <typename T> |
||||||
|
T* GetOptions() { |
||||||
|
return GetOptions<T>(T::kName()); |
||||||
|
} |
||||||
|
template <typename T> |
||||||
|
const T* GetOptions(const std::string& name) const { |
||||||
|
return reinterpret_cast<const T*>(GetOptionsPtr(name)); |
||||||
|
} |
||||||
|
template <typename T> |
||||||
|
T* GetOptions(const std::string& name) { |
||||||
|
return reinterpret_cast<T*>(const_cast<void*>(GetOptionsPtr(name))); |
||||||
|
} |
||||||
|
|
||||||
|
// Configures the options for this class based on the input parameters.
|
||||||
|
// On successful completion, the object is updated with the settings from
|
||||||
|
// the opt_map.
|
||||||
|
// If this method fails, an attempt is made to revert the object to original
|
||||||
|
// state. Note that the revert may not be the original state but may be an
|
||||||
|
// equivalent. For example, if the object contains an option that is a
|
||||||
|
// shared_ptr, the shared_ptr may not be the original one but a copy (e.g. not
|
||||||
|
// the Cache object that was passed in, but a Cache object of the same size).
|
||||||
|
//
|
||||||
|
// The acceptable values of the name/value pairs are documented with the
|
||||||
|
// specific class/instance.
|
||||||
|
//
|
||||||
|
// @param config_options Controls how the arguments are processed.
|
||||||
|
// @param opt_map Name/value pairs of the options to update
|
||||||
|
// @param unused If specified, this value will return the name/value
|
||||||
|
// pairs from opt_map that were NotFound for this object.
|
||||||
|
// @return OK If all values in the map were successfully updated
|
||||||
|
// If invoke_prepare_options is true, OK also implies
|
||||||
|
// PrepareOptions ran successfully.
|
||||||
|
// @return NotFound If any of the names in the opt_map were not valid
|
||||||
|
// for this object. If unused is specified, it will contain the
|
||||||
|
// collection of NotFound names.
|
||||||
|
// @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. This can also be returned if PrepareOptions encounters an
|
||||||
|
// error.
|
||||||
|
// @see ConfigOptions for a description of the controls.
|
||||||
|
Status ConfigureFromMap( |
||||||
|
const ConfigOptions& config_options, |
||||||
|
const std::unordered_map<std::string, std::string>& opt_map); |
||||||
|
Status ConfigureFromMap( |
||||||
|
const ConfigOptions& config_options, |
||||||
|
const std::unordered_map<std::string, std::string>& opt_map, |
||||||
|
std::unordered_map<std::string, std::string>* unused); |
||||||
|
|
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
// Updates the named option to the input value, returning OK if successful.
|
||||||
|
// Note that ConfigureOption does not cause PrepareOptions to be invoked.
|
||||||
|
// @param config_options Controls how the name/value is processed.
|
||||||
|
// @param name The name of the option to update
|
||||||
|
// @param value The value to set for the named option
|
||||||
|
// @return OK If the named field was successfully updated to value.
|
||||||
|
// @return NotFound If the name is not valid for this object.
|
||||||
|
// @return NotSupported If the name is 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 the value cannot be successfully parsed.
|
||||||
|
Status ConfigureOption(const ConfigOptions& config_options, |
||||||
|
const std::string& name, const std::string& value); |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
|
||||||
|
// Configures the options for this class based on the input parameters.
|
||||||
|
// On successful completion, the object is updated with the settings from
|
||||||
|
// the opt_map. If this method fails, an attempt is made to revert the
|
||||||
|
// object to original state. Note that the revert may not be the original
|
||||||
|
// state but may be an equivalent.
|
||||||
|
// @see ConfigureFromMap for more details
|
||||||
|
// @param config_options Controls how the arguments are processed.
|
||||||
|
// @param opt_str string containing the values to update.
|
||||||
|
// @param unused If specified, this value will return the name/value
|
||||||
|
// pairs from opt_map that were NotFound for this object.
|
||||||
|
// @return OK If all specified values were successfully updated
|
||||||
|
// If invoke_prepare_options is true, OK also implies
|
||||||
|
// PrepareOptions ran successfully.
|
||||||
|
// @return NotFound If any of the names were not valid for this object.
|
||||||
|
// If unused is specified, it will contain the collection of NotFound
|
||||||
|
// names.
|
||||||
|
// @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. This can also be returned if PrepareOptions encounters an
|
||||||
|
// error.
|
||||||
|
Status ConfigureFromString(const ConfigOptions& config_options, |
||||||
|
const std::string& opts); |
||||||
|
|
||||||
|
// Fills in result with the serialized options for this object.
|
||||||
|
// This is the inverse of ConfigureFromString.
|
||||||
|
// @param config_options Controls how serialization happens.
|
||||||
|
// @param result The string representation of this object.
|
||||||
|
// @return OK If the options for this object wer successfully serialized.
|
||||||
|
// @return InvalidArgument If one or more of the options could not be
|
||||||
|
// serialized.
|
||||||
|
Status GetOptionString(const ConfigOptions& config_options, |
||||||
|
std::string* result) const; |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
// Returns the serialized options for this object.
|
||||||
|
// This method is similar to GetOptionString with no errors.
|
||||||
|
// @param config_options Controls how serialization happens.
|
||||||
|
// @param prefix A string to prepend to every option.
|
||||||
|
// @return The serialized representation of the options for this object
|
||||||
|
std::string ToString(const ConfigOptions& config_options) const { |
||||||
|
return ToString(config_options, ""); |
||||||
|
} |
||||||
|
std::string ToString(const ConfigOptions& config_options, |
||||||
|
const std::string& prefix) const; |
||||||
|
|
||||||
|
// Returns the list of option names associated with this configurable
|
||||||
|
// @param config_options Controls how the names are returned
|
||||||
|
// @param result The set of option names for this object. Note that
|
||||||
|
// options that are deprecated or aliases are not returned.
|
||||||
|
// @return OK on success.
|
||||||
|
Status GetOptionNames(const ConfigOptions& config_options, |
||||||
|
std::unordered_set<std::string>* result) const; |
||||||
|
|
||||||
|
// Returns the value of the option associated with the input name
|
||||||
|
// This method is the functional inverse of ConfigureOption
|
||||||
|
// @param config_options Controls how the value is returned
|
||||||
|
// @param name The name of the option to return a value for.
|
||||||
|
// @param value The returned value associated with the named option.
|
||||||
|
// @return OK If the named field was successfully updated to value.
|
||||||
|
// @return NotFound If the name is not valid for this object.
|
||||||
|
// @param InvalidArgument If the name is valid for this object but
|
||||||
|
// its value cannot be serialized.
|
||||||
|
virtual Status GetOption(const ConfigOptions& config_options, |
||||||
|
const std::string& name, std::string* value) const; |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
|
||||||
|
// Checks to see if this Configurable is equivalent to other.
|
||||||
|
// This method assumes that the two objects are of the same class.
|
||||||
|
// @param config_options Controls how the options are compared.
|
||||||
|
// @param other The other object to compare to.
|
||||||
|
// @param mismatch If the objects do not match, this parameter contains
|
||||||
|
// the name of the option that triggered the match failure.
|
||||||
|
// @param True if the objects match, false otherwise.
|
||||||
|
virtual bool AreEquivalent(const ConfigOptions& config_options, |
||||||
|
const Configurable* other, |
||||||
|
std::string* name) const; |
||||||
|
|
||||||
|
// Returns a pretty-printed, human-readable version of the options.
|
||||||
|
// This method is typically used to dump the options to a log file.
|
||||||
|
// Classes should override this method
|
||||||
|
virtual std::string GetPrintableOptions() const { return ""; } |
||||||
|
|
||||||
|
// Validates that the settings are valid/consistent and performs any object
|
||||||
|
// initialization required by this object. This method may be called as part
|
||||||
|
// of Configure (if invoke_prepare_options is set), or may be invoked
|
||||||
|
// separately.
|
||||||
|
//
|
||||||
|
// Once an object has been prepared, non-mutable options can no longer be
|
||||||
|
// updated.
|
||||||
|
//
|
||||||
|
// Classes must override this method to provide any implementation-specific
|
||||||
|
// initialization, such as opening log files or setting up cache parameters.
|
||||||
|
// Implementations should be idempotent (e.g. don't re-open the log file or
|
||||||
|
// reconfigure the cache), as there is the potential this method can be called
|
||||||
|
// more than once.
|
||||||
|
//
|
||||||
|
// By default, this method will also prepare all nested (Inner and
|
||||||
|
// OptionType::kConfigurable) objects.
|
||||||
|
//
|
||||||
|
// @param config_options Controls how the object is prepared. Also contains
|
||||||
|
// a Logger and Env that can be used to initialize this object.
|
||||||
|
// @return OK If the object was successfully initialized.
|
||||||
|
// @return InvalidArgument If this object could not be successfull
|
||||||
|
// initialized.
|
||||||
|
virtual Status PrepareOptions(const ConfigOptions& config_options); |
||||||
|
|
||||||
|
// Checks to see if the settings are valid for this object.
|
||||||
|
// This method checks to see if the input DBOptions and ColumnFamilyOptions
|
||||||
|
// are valid for the settings of this object. For example, an Env might not
|
||||||
|
// support certain mmap modes or a TableFactory might require certain
|
||||||
|
// settings.
|
||||||
|
//
|
||||||
|
// By default, this method will also validate all nested (Inner and
|
||||||
|
// OptionType::kConfigurable) objects.
|
||||||
|
//
|
||||||
|
// @param db_opts The DBOptions to validate
|
||||||
|
// @param cf_opts The ColumnFamilyOptions to validate
|
||||||
|
// @return OK if the options are valid
|
||||||
|
// @return InvalidArgument If the arguments are not valid for the options
|
||||||
|
// of the current object.
|
||||||
|
virtual Status ValidateOptions(const DBOptions& db_opts, |
||||||
|
const ColumnFamilyOptions& cf_opts) const; |
||||||
|
|
||||||
|
// Returns true if this object has been initialized via PrepareOptions, false
|
||||||
|
// otherwise. Once an object has been prepared, only mutable options may be
|
||||||
|
// changed.
|
||||||
|
virtual bool IsPrepared() const { return prepared_; } |
||||||
|
|
||||||
|
protected: |
||||||
|
// True once the object is prepared. Once the object is prepared, only
|
||||||
|
// mutable options can be configured.
|
||||||
|
bool prepared_; |
||||||
|
// If this class is a wrapper (has-a), this method should be
|
||||||
|
// over-written to return the inner configurable (like an EnvWrapper).
|
||||||
|
// This method should NOT recurse, but should instead return the
|
||||||
|
// direct Inner object.
|
||||||
|
virtual Configurable* Inner() const { return nullptr; } |
||||||
|
|
||||||
|
// Returns the raw pointer for the associated named option.
|
||||||
|
// The name is typically the name of an option registered via the
|
||||||
|
// Classes may override this method to provide further specialization (such as
|
||||||
|
// returning a sub-option)
|
||||||
|
//
|
||||||
|
// The default implemntation looks at the registered options. If the
|
||||||
|
// input name matches that of a registered option, the pointer registered
|
||||||
|
// with that name is returned.
|
||||||
|
// e.g,, RegisterOptions("X", &my_ptr, ...); GetOptionsPtr("X") returns
|
||||||
|
// "my_ptr"
|
||||||
|
virtual const void* GetOptionsPtr(const std::string& name) const; |
||||||
|
|
||||||
|
// Method for allowing options to be configured outside of the normal
|
||||||
|
// registered options framework. Classes may override this method if they
|
||||||
|
// wish to support non-standard options implementations (such as configuring
|
||||||
|
// themselves from constant or simple ":"-separated strings.
|
||||||
|
//
|
||||||
|
// The default implementation does nothing and returns OK
|
||||||
|
virtual Status ParseStringOptions(const ConfigOptions& config_options, |
||||||
|
const std::string& opts_str); |
||||||
|
|
||||||
|
// Internal method to configure an object from a map of name-value options.
|
||||||
|
// This method uses the input config_options to drive the configuration of
|
||||||
|
// the options in opt_map. Any option name that cannot be found from the
|
||||||
|
// input set will be returned in "unused".
|
||||||
|
//
|
||||||
|
// Classes may override this method to extend the functionality if required.
|
||||||
|
// @param config_options Controls how the options are configured and errors
|
||||||
|
// handled.
|
||||||
|
// @param opts_map The set of options to configure
|
||||||
|
// @param unused Any options from opt_map that were not configured.
|
||||||
|
// @returns a Status based on the rules outlined in ConfigureFromMap
|
||||||
|
virtual Status ConfigureOptions( |
||||||
|
const ConfigOptions& config_options, |
||||||
|
const std::unordered_map<std::string, std::string>& opts_map, |
||||||
|
std::unordered_map<std::string, std::string>* unused); |
||||||
|
|
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
// Method that configures a the specific opt_name from opt_value.
|
||||||
|
// By default, this method calls opt_info.ParseOption with the
|
||||||
|
// input parameters.
|
||||||
|
// Classes may override this method to extend the functionality, or
|
||||||
|
// change the returned Status.
|
||||||
|
virtual Status ParseOption(const ConfigOptions& config_options, |
||||||
|
const OptionTypeInfo& opt_info, |
||||||
|
const std::string& opt_name, |
||||||
|
const std::string& opt_value, void* opt_ptr); |
||||||
|
|
||||||
|
// Internal method to see if the single option name/info matches for this and
|
||||||
|
// that Classes may override this value to change its behavior.
|
||||||
|
// @param config_options Controls how the options are being matched
|
||||||
|
// @param opt_info The OptionTypeInfo registered for this option name
|
||||||
|
// that controls what field is matched (offset) and how (type).
|
||||||
|
// @param name The name associated with this opt_info.
|
||||||
|
// @param this_ptr The base pointer to compare to. This is the object
|
||||||
|
// registered for
|
||||||
|
// for this OptionTypeInfo.
|
||||||
|
// @param that_ptr The other pointer to compare to. This is the object
|
||||||
|
// registered for
|
||||||
|
// for this OptionTypeInfo.
|
||||||
|
// @param bad_name If the match fails, the name of the option that failed to
|
||||||
|
// match.
|
||||||
|
virtual bool OptionsAreEqual(const ConfigOptions& config_options, |
||||||
|
const OptionTypeInfo& opt_info, |
||||||
|
const std::string& name, |
||||||
|
const void* const this_ptr, |
||||||
|
const void* const that_ptr, |
||||||
|
std::string* bad_name) const; |
||||||
|
#endif |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
// Internal method to serialize options (ToString)
|
||||||
|
// Classes may override this value to change its behavior.
|
||||||
|
virtual std::string SerializeOptions(const ConfigOptions& config_options, |
||||||
|
const std::string& header) const; |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
|
||||||
|
// Given a name (e.g. rocksdb.my.type.opt), returns the short name (opt)
|
||||||
|
virtual std::string GetOptionName(const std::string& long_name) const; |
||||||
|
|
||||||
|
private: |
||||||
|
// Contains the collection of options (name, opt_ptr, opt_map) associated with
|
||||||
|
// this object. This collection is typically set in the constructor of the
|
||||||
|
// Configurable option via
|
||||||
|
std::vector<RegisteredOptions> options_; |
||||||
|
}; |
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,610 @@ |
|||||||
|
// 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).
|
||||||
|
|
||||||
|
#include "rocksdb/configurable.h" |
||||||
|
|
||||||
|
#include "logging/logging.h" |
||||||
|
#include "options/configurable_helper.h" |
||||||
|
#include "options/options_helper.h" |
||||||
|
#include "rocksdb/status.h" |
||||||
|
#include "rocksdb/utilities/object_registry.h" |
||||||
|
#include "rocksdb/utilities/options_type.h" |
||||||
|
#include "util/coding.h" |
||||||
|
#include "util/string_util.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
|
||||||
|
void ConfigurableHelper::RegisterOptions( |
||||||
|
Configurable& configurable, const std::string& name, void* opt_ptr, |
||||||
|
const std::unordered_map<std::string, OptionTypeInfo>* type_map) { |
||||||
|
Configurable::RegisteredOptions opts; |
||||||
|
opts.name = name; |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
opts.type_map = type_map; |
||||||
|
#else |
||||||
|
(void)type_map; |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
opts.opt_ptr = opt_ptr; |
||||||
|
configurable.options_.emplace_back(opts); |
||||||
|
} |
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// Methods for Initializing and Validating Configurable Objects
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
|
||||||
|
Status Configurable::PrepareOptions(const ConfigOptions& opts) { |
||||||
|
Status status = Status::OK(); |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
for (auto opt_iter : options_) { |
||||||
|
for (auto map_iter : *(opt_iter.type_map)) { |
||||||
|
auto& opt_info = map_iter.second; |
||||||
|
if (!opt_info.IsDeprecated() && !opt_info.IsAlias() && |
||||||
|
opt_info.IsConfigurable()) { |
||||||
|
if (!opt_info.IsEnabled(OptionTypeFlags::kDontPrepare)) { |
||||||
|
Configurable* config = |
||||||
|
opt_info.AsRawPointer<Configurable>(opt_iter.opt_ptr); |
||||||
|
if (config != nullptr) { |
||||||
|
status = config->PrepareOptions(opts); |
||||||
|
if (!status.ok()) { |
||||||
|
return status; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
if (status.ok()) { |
||||||
|
auto inner = Inner(); |
||||||
|
if (inner != nullptr) { |
||||||
|
status = inner->PrepareOptions(opts); |
||||||
|
} |
||||||
|
} |
||||||
|
if (status.ok()) { |
||||||
|
prepared_ = true; |
||||||
|
} |
||||||
|
return status; |
||||||
|
} |
||||||
|
|
||||||
|
Status Configurable::ValidateOptions(const DBOptions& db_opts, |
||||||
|
const ColumnFamilyOptions& cf_opts) const { |
||||||
|
Status status; |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
for (auto opt_iter : options_) { |
||||||
|
for (auto map_iter : *(opt_iter.type_map)) { |
||||||
|
auto& opt_info = map_iter.second; |
||||||
|
if (!opt_info.IsDeprecated() && !opt_info.IsAlias()) { |
||||||
|
if (opt_info.IsConfigurable()) { |
||||||
|
const Configurable* config = |
||||||
|
opt_info.AsRawPointer<Configurable>(opt_iter.opt_ptr); |
||||||
|
if (config != nullptr) { |
||||||
|
status = config->ValidateOptions(db_opts, cf_opts); |
||||||
|
} else if (!opt_info.CanBeNull()) { |
||||||
|
status = |
||||||
|
Status::NotFound("Missing configurable object", map_iter.first); |
||||||
|
} |
||||||
|
if (!status.ok()) { |
||||||
|
return status; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
if (status.ok()) { |
||||||
|
const auto inner = Inner(); |
||||||
|
if (inner != nullptr) { |
||||||
|
status = inner->ValidateOptions(db_opts, cf_opts); |
||||||
|
} |
||||||
|
} |
||||||
|
return status; |
||||||
|
} |
||||||
|
|
||||||
|
/*********************************************************************************/ |
||||||
|
/* */ |
||||||
|
/* Methods for Retrieving Options from Configurables */ |
||||||
|
/* */ |
||||||
|
/*********************************************************************************/ |
||||||
|
|
||||||
|
const void* Configurable::GetOptionsPtr(const std::string& name) const { |
||||||
|
for (auto o : options_) { |
||||||
|
if (o.name == name) { |
||||||
|
return o.opt_ptr; |
||||||
|
} |
||||||
|
} |
||||||
|
auto inner = Inner(); |
||||||
|
if (inner != nullptr) { |
||||||
|
return inner->GetOptionsPtr(name); |
||||||
|
} else { |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::string Configurable::GetOptionName(const std::string& opt_name) const { |
||||||
|
return opt_name; |
||||||
|
} |
||||||
|
|
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
const OptionTypeInfo* ConfigurableHelper::FindOption( |
||||||
|
const std::vector<Configurable::RegisteredOptions>& options, |
||||||
|
const std::string& short_name, std::string* opt_name, void** opt_ptr) { |
||||||
|
for (auto iter : options) { |
||||||
|
const auto opt_info = |
||||||
|
OptionTypeInfo::Find(short_name, *(iter.type_map), opt_name); |
||||||
|
if (opt_info != nullptr) { |
||||||
|
*opt_ptr = iter.opt_ptr; |
||||||
|
return opt_info; |
||||||
|
} |
||||||
|
} |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// Methods for Configuring Options from Strings/Name-Value Pairs/Maps
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
|
||||||
|
Status Configurable::ConfigureFromMap( |
||||||
|
const ConfigOptions& config_options, |
||||||
|
const std::unordered_map<std::string, std::string>& opts_map) { |
||||||
|
Status s = ConfigureFromMap(config_options, opts_map, nullptr); |
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
Status Configurable::ConfigureFromMap( |
||||||
|
const ConfigOptions& config_options, |
||||||
|
const std::unordered_map<std::string, std::string>& opts_map, |
||||||
|
std::unordered_map<std::string, std::string>* unused) { |
||||||
|
return ConfigureOptions(config_options, opts_map, unused); |
||||||
|
} |
||||||
|
|
||||||
|
Status Configurable::ConfigureOptions( |
||||||
|
const ConfigOptions& config_options, |
||||||
|
const std::unordered_map<std::string, std::string>& opts_map, |
||||||
|
std::unordered_map<std::string, std::string>* unused) { |
||||||
|
std::string curr_opts; |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
if (!config_options.ignore_unknown_options) { |
||||||
|
// If we are not ignoring unused, get the defaults in case we need to reset
|
||||||
|
GetOptionString(config_options, &curr_opts).PermitUncheckedError(); |
||||||
|
} |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
Status s = ConfigurableHelper::ConfigureOptions(config_options, *this, |
||||||
|
opts_map, unused); |
||||||
|
if (config_options.invoke_prepare_options && s.ok()) { |
||||||
|
s = PrepareOptions(config_options); |
||||||
|
} |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
if (!s.ok() && !curr_opts.empty()) { |
||||||
|
ConfigOptions reset = config_options; |
||||||
|
reset.ignore_unknown_options = true; |
||||||
|
reset.invoke_prepare_options = true; |
||||||
|
// There are some options to reset from this current error
|
||||||
|
ConfigureFromString(reset, curr_opts).PermitUncheckedError(); |
||||||
|
} |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
Status Configurable::ParseStringOptions(const ConfigOptions& /*config_options*/, |
||||||
|
const std::string& /*opts_str*/) { |
||||||
|
return Status::OK(); |
||||||
|
} |
||||||
|
|
||||||
|
Status Configurable::ConfigureFromString(const ConfigOptions& config_options, |
||||||
|
const std::string& opts_str) { |
||||||
|
Status s; |
||||||
|
if (!opts_str.empty()) { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
if (opts_str.find(';') != std::string::npos || |
||||||
|
opts_str.find('=') != std::string::npos) { |
||||||
|
std::unordered_map<std::string, std::string> opt_map; |
||||||
|
s = StringToMap(opts_str, &opt_map); |
||||||
|
if (s.ok()) { |
||||||
|
s = ConfigureFromMap(config_options, opt_map, nullptr); |
||||||
|
} |
||||||
|
} else { |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
s = ParseStringOptions(config_options, opts_str); |
||||||
|
if (s.ok() && config_options.invoke_prepare_options) { |
||||||
|
s = PrepareOptions(config_options); |
||||||
|
} |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
} |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
} else if (config_options.invoke_prepare_options) { |
||||||
|
s = PrepareOptions(config_options); |
||||||
|
} else { |
||||||
|
s = Status::OK(); |
||||||
|
} |
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
/**
|
||||||
|
* Sets the value of the named property to the input value, returning OK on |
||||||
|
* succcess. |
||||||
|
*/ |
||||||
|
Status Configurable::ConfigureOption(const ConfigOptions& config_options, |
||||||
|
const std::string& name, |
||||||
|
const std::string& value) { |
||||||
|
const std::string& opt_name = GetOptionName(name); |
||||||
|
return ConfigurableHelper::ConfigureSingleOption(config_options, *this, |
||||||
|
opt_name, value); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for the named option amongst the options for this type and sets |
||||||
|
* the value for it to be the input value. |
||||||
|
* If the name was found, found_option will be set to true and the resulting |
||||||
|
* status should be returned. |
||||||
|
*/ |
||||||
|
|
||||||
|
Status Configurable::ParseOption(const ConfigOptions& config_options, |
||||||
|
const OptionTypeInfo& opt_info, |
||||||
|
const std::string& opt_name, |
||||||
|
const std::string& opt_value, void* opt_ptr) { |
||||||
|
if (opt_info.IsMutable() || opt_info.IsConfigurable()) { |
||||||
|
return opt_info.Parse(config_options, opt_name, opt_value, opt_ptr); |
||||||
|
} else if (prepared_) { |
||||||
|
return Status::InvalidArgument("Option not changeable: " + opt_name); |
||||||
|
} else { |
||||||
|
return opt_info.Parse(config_options, opt_name, opt_value, opt_ptr); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
|
||||||
|
Status ConfigurableHelper::ConfigureOptions( |
||||||
|
const ConfigOptions& config_options, Configurable& configurable, |
||||||
|
const std::unordered_map<std::string, std::string>& opts_map, |
||||||
|
std::unordered_map<std::string, std::string>* unused) { |
||||||
|
std::unordered_map<std::string, std::string> remaining = opts_map; |
||||||
|
Status s = Status::OK(); |
||||||
|
if (!opts_map.empty()) { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
for (const auto& iter : configurable.options_) { |
||||||
|
s = ConfigureSomeOptions(config_options, configurable, *(iter.type_map), |
||||||
|
&remaining, iter.opt_ptr); |
||||||
|
if (remaining.empty()) { // Are there more options left?
|
||||||
|
break; |
||||||
|
} else if (!s.ok()) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
#else |
||||||
|
(void)configurable; |
||||||
|
if (!config_options.ignore_unknown_options) { |
||||||
|
s = Status::NotSupported("ConfigureFromMap not supported in LITE mode"); |
||||||
|
} |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
} |
||||||
|
if (unused != nullptr && !remaining.empty()) { |
||||||
|
unused->insert(remaining.begin(), remaining.end()); |
||||||
|
} |
||||||
|
if (config_options.ignore_unknown_options) { |
||||||
|
s = Status::OK(); |
||||||
|
} else if (s.ok() && unused == nullptr && !remaining.empty()) { |
||||||
|
s = Status::NotFound("Could not find option: ", remaining.begin()->first); |
||||||
|
} |
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
/**
|
||||||
|
* Updates the object with the named-value property values, returning OK on |
||||||
|
* succcess. Any properties that were found are removed from the options list; |
||||||
|
* upon return only options that were not found in this opt_map remain. |
||||||
|
|
||||||
|
* Returns: |
||||||
|
* - OK if ignore_unknown_options is set |
||||||
|
* - InvalidArgument, if any option was invalid |
||||||
|
* - NotSupported, if any option is unsupported and ignore_unsupported_options |
||||||
|
is OFF |
||||||
|
* - OK, if no option was invalid or not supported (or ignored) |
||||||
|
*/ |
||||||
|
Status ConfigurableHelper::ConfigureSomeOptions( |
||||||
|
const ConfigOptions& config_options, Configurable& configurable, |
||||||
|
const std::unordered_map<std::string, OptionTypeInfo>& type_map, |
||||||
|
std::unordered_map<std::string, std::string>* options, void* opt_ptr) { |
||||||
|
Status result = Status::OK(); // The last non-OK result (if any)
|
||||||
|
Status notsup = Status::OK(); // The last NotSupported result (if any)
|
||||||
|
std::string elem_name; |
||||||
|
int found = 1; |
||||||
|
std::unordered_set<std::string> unsupported; |
||||||
|
// While there are unused properties and we processed at least one,
|
||||||
|
// go through the remaining unused properties and attempt to configure them.
|
||||||
|
while (found > 0 && !options->empty()) { |
||||||
|
found = 0; |
||||||
|
notsup = Status::OK(); |
||||||
|
for (auto it = options->begin(); it != options->end();) { |
||||||
|
const std::string& opt_name = configurable.GetOptionName(it->first); |
||||||
|
const std::string& opt_value = it->second; |
||||||
|
const auto opt_info = |
||||||
|
OptionTypeInfo::Find(opt_name, type_map, &elem_name); |
||||||
|
if (opt_info == nullptr) { // Did not find the option. Skip it
|
||||||
|
++it; |
||||||
|
} else { |
||||||
|
Status s = ConfigureOption(config_options, configurable, *opt_info, |
||||||
|
opt_name, elem_name, opt_value, opt_ptr); |
||||||
|
if (s.IsNotFound()) { |
||||||
|
++it; |
||||||
|
} else if (s.IsNotSupported()) { |
||||||
|
notsup = s; |
||||||
|
unsupported.insert(it->first); |
||||||
|
++it; // Skip it for now
|
||||||
|
} else { |
||||||
|
found++; |
||||||
|
it = options->erase(it); |
||||||
|
if (!s.ok()) { |
||||||
|
result = s; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} // End for all remaining options
|
||||||
|
} // End while found one or options remain
|
||||||
|
|
||||||
|
// Now that we have been through the list, remove any unsupported
|
||||||
|
for (auto u : unsupported) { |
||||||
|
auto it = options->find(u); |
||||||
|
if (it != options->end()) { |
||||||
|
options->erase(it); |
||||||
|
} |
||||||
|
} |
||||||
|
if (config_options.ignore_unknown_options) { |
||||||
|
if (!result.ok()) result.PermitUncheckedError(); |
||||||
|
if (!notsup.ok()) notsup.PermitUncheckedError(); |
||||||
|
return Status::OK(); |
||||||
|
} else if (!result.ok()) { |
||||||
|
if (!notsup.ok()) notsup.PermitUncheckedError(); |
||||||
|
return result; |
||||||
|
} else if (config_options.ignore_unsupported_options) { |
||||||
|
if (!notsup.ok()) notsup.PermitUncheckedError(); |
||||||
|
return Status::OK(); |
||||||
|
} else { |
||||||
|
return notsup; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Status ConfigurableHelper::ConfigureSingleOption( |
||||||
|
const ConfigOptions& config_options, Configurable& configurable, |
||||||
|
const std::string& name, const std::string& value) { |
||||||
|
std::string opt_name; |
||||||
|
void* opt_ptr = nullptr; |
||||||
|
const auto opt_info = |
||||||
|
FindOption(configurable.options_, name, &opt_name, &opt_ptr); |
||||||
|
if (opt_info == nullptr) { |
||||||
|
return Status::NotFound("Could not find option: ", name); |
||||||
|
} else { |
||||||
|
return ConfigureOption(config_options, configurable, *opt_info, name, |
||||||
|
opt_name, value, opt_ptr); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Status ConfigurableHelper::ConfigureOption( |
||||||
|
const ConfigOptions& config_options, Configurable& configurable, |
||||||
|
const OptionTypeInfo& opt_info, const std::string& opt_name, |
||||||
|
const std::string& name, const std::string& value, void* opt_ptr) { |
||||||
|
if (opt_name == name) { |
||||||
|
return configurable.ParseOption(config_options, opt_info, opt_name, value, |
||||||
|
opt_ptr); |
||||||
|
} else if (opt_info.IsStruct() || opt_info.IsConfigurable()) { |
||||||
|
return configurable.ParseOption(config_options, opt_info, name, value, |
||||||
|
opt_ptr); |
||||||
|
} else { |
||||||
|
return Status::NotFound("Could not find option: ", name); |
||||||
|
} |
||||||
|
} |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
|
||||||
|
//*******************************************************************************
|
||||||
|
//
|
||||||
|
// Methods for Converting Options into strings
|
||||||
|
//
|
||||||
|
//*******************************************************************************
|
||||||
|
|
||||||
|
Status Configurable::GetOptionString(const ConfigOptions& config_options, |
||||||
|
std::string* result) const { |
||||||
|
assert(result); |
||||||
|
result->clear(); |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
return ConfigurableHelper::SerializeOptions(config_options, *this, "", |
||||||
|
result); |
||||||
|
#else |
||||||
|
(void)config_options; |
||||||
|
return Status::NotSupported("GetOptionString not supported in LITE mode"); |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
} |
||||||
|
|
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
std::string Configurable::ToString(const ConfigOptions& config_options, |
||||||
|
const std::string& prefix) const { |
||||||
|
std::string result = SerializeOptions(config_options, prefix); |
||||||
|
if (result.empty() || result.find('=') == std::string::npos) { |
||||||
|
return result; |
||||||
|
} else { |
||||||
|
return "{" + result + "}"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::string Configurable::SerializeOptions(const ConfigOptions& config_options, |
||||||
|
const std::string& header) const { |
||||||
|
std::string result; |
||||||
|
Status s = ConfigurableHelper::SerializeOptions(config_options, *this, header, |
||||||
|
&result); |
||||||
|
assert(s.ok()); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
Status Configurable::GetOption(const ConfigOptions& config_options, |
||||||
|
const std::string& name, |
||||||
|
std::string* value) const { |
||||||
|
return ConfigurableHelper::GetOption(config_options, *this, |
||||||
|
GetOptionName(name), value); |
||||||
|
} |
||||||
|
|
||||||
|
Status ConfigurableHelper::GetOption(const ConfigOptions& config_options, |
||||||
|
const Configurable& configurable, |
||||||
|
const std::string& short_name, |
||||||
|
std::string* value) { |
||||||
|
// Look for option directly
|
||||||
|
assert(value); |
||||||
|
value->clear(); |
||||||
|
|
||||||
|
std::string opt_name; |
||||||
|
void* opt_ptr = nullptr; |
||||||
|
const auto opt_info = |
||||||
|
FindOption(configurable.options_, short_name, &opt_name, &opt_ptr); |
||||||
|
if (opt_info != nullptr) { |
||||||
|
ConfigOptions embedded = config_options; |
||||||
|
embedded.delimiter = ";"; |
||||||
|
if (short_name == opt_name) { |
||||||
|
return opt_info->Serialize(embedded, opt_name, opt_ptr, value); |
||||||
|
} else if (opt_info->IsStruct()) { |
||||||
|
return opt_info->Serialize(embedded, opt_name, opt_ptr, value); |
||||||
|
} else if (opt_info->IsConfigurable()) { |
||||||
|
auto const* config = opt_info->AsRawPointer<Configurable>(opt_ptr); |
||||||
|
if (config != nullptr) { |
||||||
|
return config->GetOption(embedded, opt_name, value); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return Status::NotFound("Cannot find option: ", short_name); |
||||||
|
} |
||||||
|
|
||||||
|
Status ConfigurableHelper::SerializeOptions(const ConfigOptions& config_options, |
||||||
|
const Configurable& configurable, |
||||||
|
const std::string& prefix, |
||||||
|
std::string* result) { |
||||||
|
assert(result); |
||||||
|
for (auto const& opt_iter : configurable.options_) { |
||||||
|
for (const auto& map_iter : *(opt_iter.type_map)) { |
||||||
|
const auto& opt_name = map_iter.first; |
||||||
|
const auto& opt_info = map_iter.second; |
||||||
|
if (opt_info.ShouldSerialize()) { |
||||||
|
std::string value; |
||||||
|
Status s = opt_info.Serialize(config_options, prefix + opt_name, |
||||||
|
opt_iter.opt_ptr, &value); |
||||||
|
if (!s.ok()) { |
||||||
|
return s; |
||||||
|
} else if (!value.empty()) { |
||||||
|
// <prefix><opt_name>=<value><delimiter>
|
||||||
|
result->append(prefix + opt_name + "=" + value + |
||||||
|
config_options.delimiter); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return Status::OK(); |
||||||
|
} |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
|
||||||
|
//********************************************************************************
|
||||||
|
//
|
||||||
|
// Methods for listing the options from Configurables
|
||||||
|
//
|
||||||
|
//********************************************************************************
|
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
Status Configurable::GetOptionNames( |
||||||
|
const ConfigOptions& config_options, |
||||||
|
std::unordered_set<std::string>* result) const { |
||||||
|
assert(result); |
||||||
|
return ConfigurableHelper::ListOptions(config_options, *this, "", result); |
||||||
|
} |
||||||
|
|
||||||
|
Status ConfigurableHelper::ListOptions( |
||||||
|
const ConfigOptions& /*config_options*/, const Configurable& configurable, |
||||||
|
const std::string& prefix, std::unordered_set<std::string>* result) { |
||||||
|
Status status; |
||||||
|
for (auto const& opt_iter : configurable.options_) { |
||||||
|
for (const auto& map_iter : *(opt_iter.type_map)) { |
||||||
|
const auto& opt_name = map_iter.first; |
||||||
|
const auto& opt_info = map_iter.second; |
||||||
|
// If the option is no longer used in rocksdb and marked as deprecated,
|
||||||
|
// we skip it in the serialization.
|
||||||
|
if (!opt_info.IsDeprecated() && !opt_info.IsAlias()) { |
||||||
|
result->emplace(prefix + opt_name); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return status; |
||||||
|
} |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
|
||||||
|
//*******************************************************************************
|
||||||
|
//
|
||||||
|
// Methods for Comparing Configurables
|
||||||
|
//
|
||||||
|
//*******************************************************************************
|
||||||
|
|
||||||
|
bool Configurable::AreEquivalent(const ConfigOptions& config_options, |
||||||
|
const Configurable* other, |
||||||
|
std::string* name) const { |
||||||
|
assert(name); |
||||||
|
name->clear(); |
||||||
|
if (this == other || config_options.IsCheckDisabled()) { |
||||||
|
return true; |
||||||
|
} else if (other != nullptr) { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
return ConfigurableHelper::AreEquivalent(config_options, *this, *other, |
||||||
|
name); |
||||||
|
#else |
||||||
|
return true; |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
bool Configurable::OptionsAreEqual(const ConfigOptions& config_options, |
||||||
|
const OptionTypeInfo& opt_info, |
||||||
|
const std::string& opt_name, |
||||||
|
const void* const this_ptr, |
||||||
|
const void* const that_ptr, |
||||||
|
std::string* mismatch) const { |
||||||
|
if (opt_info.AreEqual(config_options, opt_name, this_ptr, that_ptr, |
||||||
|
mismatch)) { |
||||||
|
return true; |
||||||
|
} else if (opt_info.AreEqualByName(config_options, opt_name, this_ptr, |
||||||
|
that_ptr)) { |
||||||
|
*mismatch = ""; |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool ConfigurableHelper::AreEquivalent(const ConfigOptions& config_options, |
||||||
|
const Configurable& this_one, |
||||||
|
const Configurable& that_one, |
||||||
|
std::string* mismatch) { |
||||||
|
assert(mismatch != nullptr); |
||||||
|
for (auto const& o : this_one.options_) { |
||||||
|
const auto this_offset = this_one.GetOptionsPtr(o.name); |
||||||
|
const auto that_offset = that_one.GetOptionsPtr(o.name); |
||||||
|
if (this_offset != that_offset) { |
||||||
|
if (this_offset == nullptr || that_offset == nullptr) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
for (const auto& map_iter : *(o.type_map)) { |
||||||
|
if (config_options.IsCheckEnabled(map_iter.second.GetSanityLevel()) && |
||||||
|
!this_one.OptionsAreEqual(config_options, map_iter.second, |
||||||
|
map_iter.first, this_offset, |
||||||
|
that_offset, mismatch)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,211 @@ |
|||||||
|
// 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 <map> |
||||||
|
#include <stdexcept> |
||||||
|
#include <string> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "rocksdb/configurable.h" |
||||||
|
#include "rocksdb/convenience.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
// Helper class defining static methods for supporting the Configurable
|
||||||
|
// class. The purpose of this class is to keep the Configurable class
|
||||||
|
// as tight as possible and provide methods for doing the actual work
|
||||||
|
// of configuring the objects.
|
||||||
|
class ConfigurableHelper { |
||||||
|
public: |
||||||
|
// Registers the input name with the options and associated map.
|
||||||
|
// When classes register their options in this manner, most of the
|
||||||
|
// functionality (excluding unknown options and validate/prepare) is
|
||||||
|
// implemented by the base class.
|
||||||
|
//
|
||||||
|
// This method should be called in the class constructor to register the
|
||||||
|
// option set for this object. For example, to register the options
|
||||||
|
// associated with the BlockBasedTableFactory, the constructor calls this
|
||||||
|
// method passing in:
|
||||||
|
// - the name of the options ("BlockBasedTableOptions");
|
||||||
|
// - the options object (the BlockBasedTableOptions object for this object;
|
||||||
|
// - the options type map for the BlockBasedTableOptions.
|
||||||
|
// This registration allows the Configurable class to process the option
|
||||||
|
// values associated with the BlockBasedTableOptions without further code in
|
||||||
|
// the derived class.
|
||||||
|
//
|
||||||
|
// @param name The name of this set of options (@see GetOptionsPtr)
|
||||||
|
// @param opt_ptr Pointer to the options to associate with this name
|
||||||
|
// @param opt_map Options map that controls how this option is configured.
|
||||||
|
template <typename T> |
||||||
|
static void RegisterOptions( |
||||||
|
Configurable& configurable, T* opt_ptr, |
||||||
|
const std::unordered_map<std::string, OptionTypeInfo>* opt_map) { |
||||||
|
RegisterOptions(configurable, T::kName(), opt_ptr, opt_map); |
||||||
|
} |
||||||
|
static void RegisterOptions( |
||||||
|
Configurable& configurable, const std::string& name, void* opt_ptr, |
||||||
|
const std::unordered_map<std::string, OptionTypeInfo>* opt_map); |
||||||
|
|
||||||
|
// Configures the input Configurable object based on the parameters.
|
||||||
|
// On successful completion, the Configurable is updated with the settings
|
||||||
|
// from the opt_map.
|
||||||
|
//
|
||||||
|
// The acceptable values of the name/value pairs are documented with the
|
||||||
|
// specific class/instance.
|
||||||
|
//
|
||||||
|
// @param config_options Controls how the arguments are processed.
|
||||||
|
// @param opt_map Name/value pairs of the options to update
|
||||||
|
// @param unused If specified, this value will return the name/value
|
||||||
|
// pairs from opt_map that were NotFound for this object.
|
||||||
|
// @return OK If all values in the map were successfully updated
|
||||||
|
// @return NotFound If any of the names in the opt_map were not valid
|
||||||
|
// for this object. If unused is specified, it will contain the
|
||||||
|
// collection of NotFound entries
|
||||||
|
// @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. This can also be returned if PrepareOptions encounters an
|
||||||
|
// error.
|
||||||
|
static Status ConfigureOptions( |
||||||
|
const ConfigOptions& config_options, Configurable& configurable, |
||||||
|
const std::unordered_map<std::string, std::string>& options, |
||||||
|
std::unordered_map<std::string, std::string>* unused); |
||||||
|
|
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
// Internal method to configure a set of options for this object.
|
||||||
|
// Classes may override this value to change its behavior.
|
||||||
|
// @param config_options Controls how the options are being configured
|
||||||
|
// @param type_name The name that was registered for this set of options
|
||||||
|
// @param type_map The map of options for this name
|
||||||
|
// @param opt_ptr Pointer to the object being configured for this option set.
|
||||||
|
// @param options The option name/values being updated. On return, any
|
||||||
|
// option that was found is removed from the list.
|
||||||
|
// @return OK If all of the options were successfully updated.
|
||||||
|
// @return InvalidArgument If an option was found but the value could not
|
||||||
|
// be updated.
|
||||||
|
// @return NotFound If an option name was not found in type_mape
|
||||||
|
// @return NotSupported If the option was found but no rule for converting
|
||||||
|
// the value could be found.
|
||||||
|
static Status ConfigureSomeOptions( |
||||||
|
const ConfigOptions& config_options, Configurable& configurable, |
||||||
|
const std::unordered_map<std::string, OptionTypeInfo>& type_map, |
||||||
|
std::unordered_map<std::string, std::string>* options, void* opt_ptr); |
||||||
|
|
||||||
|
// Configures a single option in the input Configurable.
|
||||||
|
// This method will look through the set of option names for this
|
||||||
|
// Configurable searching for one with the input name. If such an option
|
||||||
|
// is found, it will be configured via the input value.
|
||||||
|
//
|
||||||
|
// @param config_options Controls how the option is being configured
|
||||||
|
// @param configurable The object to configure
|
||||||
|
// @param name For options with sub-options (like Structs or
|
||||||
|
// Configurables),
|
||||||
|
// this value may be the name of the sub-field of the option being
|
||||||
|
// updated. For example, if the option is
|
||||||
|
// "compaction_options_fifo.allow_compaction", then field name would be
|
||||||
|
// "allow_compaction". For most options, field_name and opt_name will be
|
||||||
|
// equivalent.
|
||||||
|
// @param value The new value for this option.
|
||||||
|
// @param See ConfigureOptions for the possible return values
|
||||||
|
static Status ConfigureSingleOption(const ConfigOptions& config_options, |
||||||
|
Configurable& configurable, |
||||||
|
const std::string& name, |
||||||
|
const std::string& value); |
||||||
|
|
||||||
|
// Configures the option referenced by opt_info for this configurable
|
||||||
|
// This method configures the option based on opt_info for the input
|
||||||
|
// configurable.
|
||||||
|
// @param config_options Controls how the option is being configured
|
||||||
|
// @param configurable The object to configure
|
||||||
|
// @param opt_name The full option name
|
||||||
|
// @param name For options with sub-options (like Structs or
|
||||||
|
// Configurables),
|
||||||
|
// this value may be the name of the sub-field of the option being
|
||||||
|
// updated. For example, if the option is
|
||||||
|
// "compaction_options_fifo.allow_compaction", then field name would be
|
||||||
|
// "allow_compaction". For most options, field_name and opt_name will be
|
||||||
|
// equivalent.
|
||||||
|
// @param value The new value for this option.
|
||||||
|
// @param See ConfigureOptions for the possible return values
|
||||||
|
static Status ConfigureOption(const ConfigOptions& config_options, |
||||||
|
Configurable& configurable, |
||||||
|
const OptionTypeInfo& opt_info, |
||||||
|
const std::string& opt_name, |
||||||
|
const std::string& name, |
||||||
|
const std::string& value, void* opt_ptr); |
||||||
|
|
||||||
|
// Returns the value of the option associated with the input name
|
||||||
|
// This method is the functional inverse of ConfigureOption
|
||||||
|
// @param config_options Controls how the value is returned
|
||||||
|
// @param configurable The object from which to get the option.
|
||||||
|
// @param name The name of the option to return a value for.
|
||||||
|
// @param value The returned value associated with the named option.
|
||||||
|
// Note that value will be only the serialized version
|
||||||
|
// of the option and not "name=value"
|
||||||
|
// @return OK If the named field was successfully updated to value.
|
||||||
|
// @return NotFound If the name is not valid for this object.
|
||||||
|
// @param InvalidArgument If the name is valid for this object but
|
||||||
|
// its value cannot be serialized.
|
||||||
|
static Status GetOption(const ConfigOptions& config_options, |
||||||
|
const Configurable& configurable, |
||||||
|
const std::string& name, std::string* value); |
||||||
|
|
||||||
|
// Serializes the input Configurable into the output result.
|
||||||
|
// This is the inverse of ConfigureOptions
|
||||||
|
// @param config_options Controls how serialization happens.
|
||||||
|
// @param configurable The object to serialize
|
||||||
|
// @param prefix A prefix to add to the each option as it is serialized.
|
||||||
|
// @param result The string representation of the configurable.
|
||||||
|
// @return OK If the options for this object wer successfully serialized.
|
||||||
|
// @return InvalidArgument If one or more of the options could not be
|
||||||
|
// serialized.
|
||||||
|
static Status SerializeOptions(const ConfigOptions& config_options, |
||||||
|
const Configurable& configurable, |
||||||
|
const std::string& prefix, |
||||||
|
std::string* result); |
||||||
|
|
||||||
|
// Internal method to list the option names for this object.
|
||||||
|
// Classes may override this value to change its behavior.
|
||||||
|
// @see ListOptions for more details
|
||||||
|
static Status ListOptions(const ConfigOptions& config_options, |
||||||
|
const Configurable& configurable, |
||||||
|
const std::string& prefix, |
||||||
|
std::unordered_set<std::string>* result); |
||||||
|
|
||||||
|
// Checks to see if the two configurables are equivalent to one other.
|
||||||
|
// This method assumes that the two objects are of the same class.
|
||||||
|
// @param config_options Controls how the options are compared.
|
||||||
|
// @param this_one The object to compare to.
|
||||||
|
// @param that_one The other object being compared.
|
||||||
|
// @param mismatch If the objects do not match, this parameter contains
|
||||||
|
// the name of the option that triggered the match failure.
|
||||||
|
// @param True if the objects match, false otherwise.
|
||||||
|
static bool AreEquivalent(const ConfigOptions& config_options, |
||||||
|
const Configurable& this_one, |
||||||
|
const Configurable& that_one, |
||||||
|
std::string* mismatch); |
||||||
|
|
||||||
|
private: |
||||||
|
// Looks for the option specified by name in the RegisteredOptions.
|
||||||
|
// This method traverses the types in the input options vector. If an entry
|
||||||
|
// matching name is found, that entry, opt_name, and pointer are returned.
|
||||||
|
// @param options The vector of options to search through
|
||||||
|
// @param name The name of the option to search for in the OptionType map
|
||||||
|
// @param opt_name If the name was found, this value is set to the option name
|
||||||
|
// associated with the input name/type.
|
||||||
|
// @param opt_ptr If the name was found, this value is set to the option
|
||||||
|
// pointer
|
||||||
|
// in the RegisteredOptions vector associated with this entry
|
||||||
|
// @return A pointer to the OptionTypeInfo from the options if found,
|
||||||
|
// nullptr if the name was not found in the input options
|
||||||
|
static const OptionTypeInfo* FindOption( |
||||||
|
const std::vector<Configurable::RegisteredOptions>& options, |
||||||
|
const std::string& name, std::string* opt_name, void** opt_ptr); |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,791 @@ |
|||||||
|
// 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).
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||||
|
// 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/configurable_test.h" |
||||||
|
|
||||||
|
#include <cctype> |
||||||
|
#include <cinttypes> |
||||||
|
#include <cstring> |
||||||
|
#include <unordered_map> |
||||||
|
|
||||||
|
#include "options/configurable_helper.h" |
||||||
|
#include "options/options_helper.h" |
||||||
|
#include "options/options_parser.h" |
||||||
|
#include "rocksdb/configurable.h" |
||||||
|
#include "test_util/testharness.h" |
||||||
|
#include "test_util/testutil.h" |
||||||
|
|
||||||
|
#ifndef GFLAGS |
||||||
|
bool FLAGS_enable_print = false; |
||||||
|
#else |
||||||
|
#include "util/gflags_compat.h" |
||||||
|
using GFLAGS_NAMESPACE::ParseCommandLineFlags; |
||||||
|
DEFINE_bool(enable_print, false, "Print options generated to console."); |
||||||
|
#endif // GFLAGS
|
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
namespace test { |
||||||
|
class StringLogger : public Logger { |
||||||
|
public: |
||||||
|
using Logger::Logv; |
||||||
|
void Logv(const char* format, va_list ap) override { |
||||||
|
char buffer[1000]; |
||||||
|
vsnprintf(buffer, sizeof(buffer), format, ap); |
||||||
|
string_.append(buffer); |
||||||
|
} |
||||||
|
const std::string& str() const { return string_; } |
||||||
|
void clear() { string_.clear(); } |
||||||
|
|
||||||
|
private: |
||||||
|
std::string string_; |
||||||
|
}; |
||||||
|
|
||||||
|
class SimpleConfigurable : public TestConfigurable<Configurable> { |
||||||
|
public: |
||||||
|
static SimpleConfigurable* Create( |
||||||
|
const std::string& name = "simple", |
||||||
|
int mode = TestConfigMode::kDefaultMode, |
||||||
|
const std::unordered_map<std::string, OptionTypeInfo>* map = |
||||||
|
&simple_option_info) { |
||||||
|
return new SimpleConfigurable(name, mode, map); |
||||||
|
} |
||||||
|
|
||||||
|
SimpleConfigurable(const std::string& name, int mode, |
||||||
|
const std::unordered_map<std::string, OptionTypeInfo>* |
||||||
|
map = &simple_option_info) |
||||||
|
: TestConfigurable(name, mode, map) { |
||||||
|
if ((mode & TestConfigMode::kUniqueMode) != 0) { |
||||||
|
unique_.reset(SimpleConfigurable::Create("Unique" + name_)); |
||||||
|
ConfigurableHelper::RegisterOptions(*this, name_ + "Unique", &unique_, |
||||||
|
&unique_option_info); |
||||||
|
} |
||||||
|
if ((mode & TestConfigMode::kSharedMode) != 0) { |
||||||
|
shared_.reset(SimpleConfigurable::Create("Shared" + name_)); |
||||||
|
ConfigurableHelper::RegisterOptions(*this, name_ + "Shared", &shared_, |
||||||
|
&shared_option_info); |
||||||
|
} |
||||||
|
if ((mode & TestConfigMode::kRawPtrMode) != 0) { |
||||||
|
pointer_ = SimpleConfigurable::Create("Pointer" + name_); |
||||||
|
ConfigurableHelper::RegisterOptions(*this, name_ + "Pointer", &pointer_, |
||||||
|
&pointer_option_info); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}; // End class SimpleConfigurable
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> wrapped_option_info = { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
{"inner", |
||||||
|
{0, OptionType::kConfigurable, OptionVerificationType::kNormal, |
||||||
|
OptionTypeFlags::kShared}}, |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
}; |
||||||
|
class WrappedConfigurable : public SimpleConfigurable { |
||||||
|
public: |
||||||
|
WrappedConfigurable(const std::string& name, unsigned char mode, |
||||||
|
const std::shared_ptr<Configurable>& t) |
||||||
|
: SimpleConfigurable(name, mode, &simple_option_info), inner_(t) { |
||||||
|
ConfigurableHelper::RegisterOptions(*this, "WrappedOptions", &inner_, |
||||||
|
&wrapped_option_info); |
||||||
|
} |
||||||
|
|
||||||
|
protected: |
||||||
|
Configurable* Inner() const override { return inner_.get(); } |
||||||
|
|
||||||
|
private: |
||||||
|
std::shared_ptr<Configurable> inner_; |
||||||
|
}; |
||||||
|
|
||||||
|
using ConfigTestFactoryFunc = std::function<Configurable*()>; |
||||||
|
|
||||||
|
class ConfigurableTest : public testing::Test { |
||||||
|
public: |
||||||
|
ConfigurableTest() { config_options_.invoke_prepare_options = false; } |
||||||
|
|
||||||
|
ConfigOptions config_options_; |
||||||
|
}; |
||||||
|
|
||||||
|
class ConfigurableParamTest |
||||||
|
: public ConfigurableTest, |
||||||
|
virtual public ::testing::WithParamInterface< |
||||||
|
std::pair<std::string, ConfigTestFactoryFunc>> { |
||||||
|
public: |
||||||
|
ConfigurableParamTest() { |
||||||
|
configuration_ = GetParam().first; |
||||||
|
factory_ = GetParam().second; |
||||||
|
object_.reset(factory_()); |
||||||
|
} |
||||||
|
void TestConfigureOptions(const ConfigOptions& opts); |
||||||
|
ConfigTestFactoryFunc factory_; |
||||||
|
std::string configuration_; |
||||||
|
std::unique_ptr<Configurable> object_; |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, GetOptionsPtrTest) { |
||||||
|
std::string opt_str; |
||||||
|
std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create()); |
||||||
|
ASSERT_NE(configurable->GetOptions<TestOptions>("simple"), nullptr); |
||||||
|
ASSERT_EQ(configurable->GetOptions<TestOptions>("bad-opt"), nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, ConfigureFromMapTest) { |
||||||
|
std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create()); |
||||||
|
auto* opts = configurable->GetOptions<TestOptions>("simple"); |
||||||
|
ASSERT_OK(configurable->ConfigureFromMap(config_options_, {})); |
||||||
|
ASSERT_NE(opts, nullptr); |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
std::unordered_map<std::string, std::string> options_map = { |
||||||
|
{"int", "1"}, {"bool", "true"}, {"string", "string"}}; |
||||||
|
ASSERT_OK(configurable->ConfigureFromMap(config_options_, options_map)); |
||||||
|
ASSERT_EQ(opts->i, 1); |
||||||
|
ASSERT_EQ(opts->b, true); |
||||||
|
ASSERT_EQ(opts->s, "string"); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, ConfigureFromStringTest) { |
||||||
|
std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create()); |
||||||
|
auto* opts = configurable->GetOptions<TestOptions>("simple"); |
||||||
|
ASSERT_OK(configurable->ConfigureFromString(config_options_, "")); |
||||||
|
ASSERT_NE(opts, nullptr); |
||||||
|
#ifndef ROCKSDB_LITE // GetOptionsFromMap is not supported in ROCKSDB_LITE
|
||||||
|
ASSERT_OK(configurable->ConfigureFromString(config_options_, |
||||||
|
"int=1;bool=true;string=s")); |
||||||
|
ASSERT_EQ(opts->i, 1); |
||||||
|
ASSERT_EQ(opts->b, true); |
||||||
|
ASSERT_EQ(opts->s, "s"); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
#ifndef ROCKSDB_LITE // GetOptionsFromMap is not supported in ROCKSDB_LITE
|
||||||
|
TEST_F(ConfigurableTest, ConfigureIgnoreTest) { |
||||||
|
std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create()); |
||||||
|
std::unordered_map<std::string, std::string> options_map = {{"unused", "u"}}; |
||||||
|
ConfigOptions ignore = config_options_; |
||||||
|
ignore.ignore_unknown_options = true; |
||||||
|
ASSERT_NOK(configurable->ConfigureFromMap(config_options_, options_map)); |
||||||
|
ASSERT_OK(configurable->ConfigureFromMap(ignore, options_map)); |
||||||
|
ASSERT_NOK(configurable->ConfigureFromString(config_options_, "unused=u")); |
||||||
|
ASSERT_OK(configurable->ConfigureFromString(ignore, "unused=u")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, ConfigureNestedOptionsTest) { |
||||||
|
std::unique_ptr<Configurable> base, copy; |
||||||
|
std::string opt_str; |
||||||
|
std::string mismatch; |
||||||
|
|
||||||
|
base.reset(SimpleConfigurable::Create("simple", TestConfigMode::kAllOptMode)); |
||||||
|
copy.reset(SimpleConfigurable::Create("simple", TestConfigMode::kAllOptMode)); |
||||||
|
ASSERT_OK(base->ConfigureFromString(config_options_, |
||||||
|
"shared={int=10; string=10};" |
||||||
|
"unique={int=20; string=20};" |
||||||
|
"pointer={int=30; string=30};")); |
||||||
|
ASSERT_OK(base->GetOptionString(config_options_, &opt_str)); |
||||||
|
ASSERT_OK(copy->ConfigureFromString(config_options_, opt_str)); |
||||||
|
ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, GetOptionsTest) { |
||||||
|
std::unique_ptr<Configurable> simple; |
||||||
|
|
||||||
|
simple.reset( |
||||||
|
SimpleConfigurable::Create("simple", TestConfigMode::kAllOptMode)); |
||||||
|
int i = 11; |
||||||
|
for (auto opt : {"", "shared.", "unique.", "pointer."}) { |
||||||
|
std::string value; |
||||||
|
std::string expected = ToString(i); |
||||||
|
std::string opt_name = opt; |
||||||
|
ASSERT_OK( |
||||||
|
simple->ConfigureOption(config_options_, opt_name + "int", expected)); |
||||||
|
ASSERT_OK(simple->GetOption(config_options_, opt_name + "int", &value)); |
||||||
|
ASSERT_EQ(expected, value); |
||||||
|
ASSERT_OK(simple->ConfigureOption(config_options_, opt_name + "string", |
||||||
|
expected)); |
||||||
|
ASSERT_OK(simple->GetOption(config_options_, opt_name + "string", &value)); |
||||||
|
ASSERT_EQ(expected, value); |
||||||
|
|
||||||
|
ASSERT_NOK( |
||||||
|
simple->ConfigureOption(config_options_, opt_name + "bad", expected)); |
||||||
|
ASSERT_NOK(simple->GetOption(config_options_, "bad option", &value)); |
||||||
|
ASSERT_TRUE(value.empty()); |
||||||
|
i += 11; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, ConfigureBadOptionsTest) { |
||||||
|
std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create()); |
||||||
|
auto* opts = configurable->GetOptions<TestOptions>("simple"); |
||||||
|
ASSERT_NE(opts, nullptr); |
||||||
|
ASSERT_OK(configurable->ConfigureOption(config_options_, "int", "42")); |
||||||
|
ASSERT_EQ(opts->i, 42); |
||||||
|
ASSERT_NOK(configurable->ConfigureOption(config_options_, "int", "fred")); |
||||||
|
ASSERT_NOK(configurable->ConfigureOption(config_options_, "bool", "fred")); |
||||||
|
ASSERT_NOK( |
||||||
|
configurable->ConfigureFromString(config_options_, "int=33;unused=u")); |
||||||
|
ASSERT_EQ(opts->i, 42); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, InvalidOptionTest) { |
||||||
|
std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create()); |
||||||
|
std::unordered_map<std::string, std::string> options_map = { |
||||||
|
{"bad-option", "bad"}}; |
||||||
|
ASSERT_NOK(configurable->ConfigureFromMap(config_options_, options_map)); |
||||||
|
ASSERT_NOK( |
||||||
|
configurable->ConfigureFromString(config_options_, "bad-option=bad")); |
||||||
|
ASSERT_NOK( |
||||||
|
configurable->ConfigureOption(config_options_, "bad-option", "bad")); |
||||||
|
} |
||||||
|
|
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> validated_option_info = { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
{"validated", |
||||||
|
{0, OptionType::kBoolean, OptionVerificationType::kNormal, |
||||||
|
OptionTypeFlags::kNone}}, |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
}; |
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> prepared_option_info = { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
{"prepared", |
||||||
|
{0, OptionType::kInt, OptionVerificationType::kNormal, |
||||||
|
OptionTypeFlags::kMutable}}, |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
}; |
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> |
||||||
|
dont_prepare_option_info = { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
{"unique", |
||||||
|
{0, OptionType::kConfigurable, OptionVerificationType::kNormal, |
||||||
|
(OptionTypeFlags::kUnique | OptionTypeFlags::kDontPrepare)}}, |
||||||
|
|
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
}; |
||||||
|
|
||||||
|
class ValidatedConfigurable : public SimpleConfigurable { |
||||||
|
public: |
||||||
|
ValidatedConfigurable(const std::string& name, unsigned char mode, |
||||||
|
bool dont_prepare = false) |
||||||
|
: SimpleConfigurable(name, TestConfigMode::kDefaultMode), |
||||||
|
validated(false), |
||||||
|
prepared(0) { |
||||||
|
ConfigurableHelper::RegisterOptions(*this, "Validated", &validated, |
||||||
|
&validated_option_info); |
||||||
|
ConfigurableHelper::RegisterOptions(*this, "Prepared", &prepared, |
||||||
|
&prepared_option_info); |
||||||
|
if ((mode & TestConfigMode::kUniqueMode) != 0) { |
||||||
|
unique_.reset(new ValidatedConfigurable( |
||||||
|
"Unique" + name_, TestConfigMode::kDefaultMode, false)); |
||||||
|
if (dont_prepare) { |
||||||
|
ConfigurableHelper::RegisterOptions(*this, name_ + "Unique", &unique_, |
||||||
|
&dont_prepare_option_info); |
||||||
|
} else { |
||||||
|
ConfigurableHelper::RegisterOptions(*this, name_ + "Unique", &unique_, |
||||||
|
&unique_option_info); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Status PrepareOptions(const ConfigOptions& config_options) override { |
||||||
|
if (++prepared <= 0) { |
||||||
|
return Status::InvalidArgument("Cannot prepare option"); |
||||||
|
} else { |
||||||
|
return SimpleConfigurable::PrepareOptions(config_options); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Status ValidateOptions(const DBOptions& db_opts, |
||||||
|
const ColumnFamilyOptions& cf_opts) const override { |
||||||
|
if (!validated) { |
||||||
|
return Status::InvalidArgument("Not Validated"); |
||||||
|
} else { |
||||||
|
return SimpleConfigurable::ValidateOptions(db_opts, cf_opts); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
bool validated; |
||||||
|
int prepared; |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, ValidateOptionsTest) { |
||||||
|
std::unique_ptr<Configurable> configurable( |
||||||
|
new ValidatedConfigurable("validated", TestConfigMode::kDefaultMode)); |
||||||
|
ColumnFamilyOptions cf_opts; |
||||||
|
DBOptions db_opts; |
||||||
|
ASSERT_OK( |
||||||
|
configurable->ConfigureOption(config_options_, "validated", "false")); |
||||||
|
ASSERT_NOK(configurable->ValidateOptions(db_opts, cf_opts)); |
||||||
|
ASSERT_OK( |
||||||
|
configurable->ConfigureOption(config_options_, "validated", "true")); |
||||||
|
ASSERT_OK(configurable->ValidateOptions(db_opts, cf_opts)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, PrepareOptionsTest) { |
||||||
|
std::unique_ptr<Configurable> c( |
||||||
|
new ValidatedConfigurable("Simple", TestConfigMode::kUniqueMode, false)); |
||||||
|
auto cp = c->GetOptions<int>("Prepared"); |
||||||
|
auto u = c->GetOptions<std::unique_ptr<Configurable>>("SimpleUnique"); |
||||||
|
auto up = u->get()->GetOptions<int>("Prepared"); |
||||||
|
config_options_.invoke_prepare_options = false; |
||||||
|
|
||||||
|
ASSERT_NE(cp, nullptr); |
||||||
|
ASSERT_NE(up, nullptr); |
||||||
|
ASSERT_EQ(*cp, 0); |
||||||
|
ASSERT_EQ(*up, 0); |
||||||
|
ASSERT_OK(c->ConfigureFromMap(config_options_, {})); |
||||||
|
ASSERT_EQ(*cp, 0); |
||||||
|
ASSERT_EQ(*up, 0); |
||||||
|
config_options_.invoke_prepare_options = true; |
||||||
|
ASSERT_OK(c->ConfigureFromMap(config_options_, {})); |
||||||
|
ASSERT_EQ(*cp, 1); |
||||||
|
ASSERT_EQ(*up, 1); |
||||||
|
ASSERT_OK(c->ConfigureFromString(config_options_, "prepared=0")); |
||||||
|
ASSERT_EQ(*up, 2); |
||||||
|
ASSERT_EQ(*cp, 1); |
||||||
|
|
||||||
|
ASSERT_NOK(c->ConfigureFromString(config_options_, "prepared=-2")); |
||||||
|
|
||||||
|
c.reset( |
||||||
|
new ValidatedConfigurable("Simple", TestConfigMode::kUniqueMode, true)); |
||||||
|
cp = c->GetOptions<int>("Prepared"); |
||||||
|
u = c->GetOptions<std::unique_ptr<Configurable>>("SimpleUnique"); |
||||||
|
up = u->get()->GetOptions<int>("Prepared"); |
||||||
|
|
||||||
|
ASSERT_OK(c->ConfigureFromString(config_options_, "prepared=0")); |
||||||
|
ASSERT_EQ(*cp, 1); |
||||||
|
ASSERT_EQ(*up, 0); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, DeprecatedOptionsTest) { |
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> |
||||||
|
deprecated_option_info = { |
||||||
|
{"deprecated", |
||||||
|
{offsetof(struct TestOptions, b), OptionType::kBoolean, |
||||||
|
OptionVerificationType::kDeprecated, OptionTypeFlags::kNone}}}; |
||||||
|
std::unique_ptr<Configurable> orig; |
||||||
|
orig.reset(SimpleConfigurable::Create("simple", TestConfigMode::kDefaultMode, |
||||||
|
&deprecated_option_info)); |
||||||
|
auto* opts = orig->GetOptions<TestOptions>("simple"); |
||||||
|
ASSERT_NE(opts, nullptr); |
||||||
|
opts->d = true; |
||||||
|
ASSERT_OK(orig->ConfigureOption(config_options_, "deprecated", "false")); |
||||||
|
ASSERT_TRUE(opts->d); |
||||||
|
ASSERT_OK(orig->ConfigureFromString(config_options_, "deprecated=false")); |
||||||
|
ASSERT_TRUE(opts->d); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, AliasOptionsTest) { |
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> alias_option_info = { |
||||||
|
{"bool", |
||||||
|
{offsetof(struct TestOptions, b), OptionType::kBoolean, |
||||||
|
OptionVerificationType::kNormal, OptionTypeFlags::kNone}}, |
||||||
|
{"alias", |
||||||
|
{offsetof(struct TestOptions, b), OptionType::kBoolean, |
||||||
|
OptionVerificationType::kAlias, OptionTypeFlags::kNone, 0}}}; |
||||||
|
std::unique_ptr<Configurable> orig; |
||||||
|
orig.reset(SimpleConfigurable::Create("simple", TestConfigMode::kDefaultMode, |
||||||
|
&alias_option_info)); |
||||||
|
auto* opts = orig->GetOptions<TestOptions>("simple"); |
||||||
|
ASSERT_NE(opts, nullptr); |
||||||
|
ASSERT_OK(orig->ConfigureOption(config_options_, "bool", "false")); |
||||||
|
ASSERT_FALSE(opts->b); |
||||||
|
ASSERT_OK(orig->ConfigureOption(config_options_, "alias", "true")); |
||||||
|
ASSERT_TRUE(opts->b); |
||||||
|
std::string opts_str; |
||||||
|
ASSERT_OK(orig->GetOptionString(config_options_, &opts_str)); |
||||||
|
ASSERT_EQ(opts_str.find("alias"), std::string::npos); |
||||||
|
|
||||||
|
ASSERT_OK(orig->ConfigureOption(config_options_, "bool", "false")); |
||||||
|
ASSERT_FALSE(opts->b); |
||||||
|
ASSERT_OK(orig->GetOption(config_options_, "alias", &opts_str)); |
||||||
|
ASSERT_EQ(opts_str, "false"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, NestedUniqueConfigTest) { |
||||||
|
std::unique_ptr<Configurable> simple; |
||||||
|
simple.reset( |
||||||
|
SimpleConfigurable::Create("Outer", TestConfigMode::kAllOptMode)); |
||||||
|
const auto outer = simple->GetOptions<TestOptions>("Outer"); |
||||||
|
const auto unique = |
||||||
|
simple->GetOptions<std::unique_ptr<Configurable>>("OuterUnique"); |
||||||
|
ASSERT_NE(outer, nullptr); |
||||||
|
ASSERT_NE(unique, nullptr); |
||||||
|
ASSERT_OK( |
||||||
|
simple->ConfigureFromString(config_options_, "int=24;string=outer")); |
||||||
|
ASSERT_OK(simple->ConfigureFromString(config_options_, |
||||||
|
"unique={int=42;string=nested}")); |
||||||
|
const auto inner = unique->get()->GetOptions<TestOptions>("UniqueOuter"); |
||||||
|
ASSERT_NE(inner, nullptr); |
||||||
|
ASSERT_EQ(outer->i, 24); |
||||||
|
ASSERT_EQ(outer->s, "outer"); |
||||||
|
ASSERT_EQ(inner->i, 42); |
||||||
|
ASSERT_EQ(inner->s, "nested"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, NestedSharedConfigTest) { |
||||||
|
std::unique_ptr<Configurable> simple; |
||||||
|
simple.reset(SimpleConfigurable::Create( |
||||||
|
"Outer", TestConfigMode::kDefaultMode | TestConfigMode::kSharedMode)); |
||||||
|
ASSERT_OK( |
||||||
|
simple->ConfigureFromString(config_options_, "int=24;string=outer")); |
||||||
|
ASSERT_OK(simple->ConfigureFromString(config_options_, |
||||||
|
"shared={int=42;string=nested}")); |
||||||
|
const auto outer = simple->GetOptions<TestOptions>("Outer"); |
||||||
|
const auto shared = |
||||||
|
simple->GetOptions<std::shared_ptr<Configurable>>("OuterShared"); |
||||||
|
ASSERT_NE(outer, nullptr); |
||||||
|
ASSERT_NE(shared, nullptr); |
||||||
|
const auto inner = shared->get()->GetOptions<TestOptions>("SharedOuter"); |
||||||
|
ASSERT_NE(inner, nullptr); |
||||||
|
ASSERT_EQ(outer->i, 24); |
||||||
|
ASSERT_EQ(outer->s, "outer"); |
||||||
|
ASSERT_EQ(inner->i, 42); |
||||||
|
ASSERT_EQ(inner->s, "nested"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, NestedRawConfigTest) { |
||||||
|
std::unique_ptr<Configurable> simple; |
||||||
|
simple.reset(SimpleConfigurable::Create( |
||||||
|
"Outer", TestConfigMode::kDefaultMode | TestConfigMode::kRawPtrMode)); |
||||||
|
ASSERT_OK( |
||||||
|
simple->ConfigureFromString(config_options_, "int=24;string=outer")); |
||||||
|
ASSERT_OK(simple->ConfigureFromString(config_options_, |
||||||
|
"pointer={int=42;string=nested}")); |
||||||
|
const auto outer = simple->GetOptions<TestOptions>("Outer"); |
||||||
|
const auto pointer = simple->GetOptions<Configurable*>("OuterPointer"); |
||||||
|
ASSERT_NE(outer, nullptr); |
||||||
|
ASSERT_NE(pointer, nullptr); |
||||||
|
const auto inner = (*pointer)->GetOptions<TestOptions>("PointerOuter"); |
||||||
|
ASSERT_NE(inner, nullptr); |
||||||
|
ASSERT_EQ(outer->i, 24); |
||||||
|
ASSERT_EQ(outer->s, "outer"); |
||||||
|
ASSERT_EQ(inner->i, 42); |
||||||
|
ASSERT_EQ(inner->s, "nested"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, MatchesTest) { |
||||||
|
std::string mismatch; |
||||||
|
std::unique_ptr<Configurable> base, copy; |
||||||
|
base.reset(SimpleConfigurable::Create( |
||||||
|
"simple", TestConfigMode::kDefaultMode | TestConfigMode::kNestedMode)); |
||||||
|
copy.reset(SimpleConfigurable::Create( |
||||||
|
"simple", TestConfigMode::kDefaultMode | TestConfigMode::kNestedMode)); |
||||||
|
ASSERT_OK(base->ConfigureFromString( |
||||||
|
config_options_, |
||||||
|
"int=11;string=outer;unique={int=22;string=u};shared={int=33;string=s}")); |
||||||
|
ASSERT_OK(copy->ConfigureFromString( |
||||||
|
config_options_, |
||||||
|
"int=11;string=outer;unique={int=22;string=u};shared={int=33;string=s}")); |
||||||
|
ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||||
|
ASSERT_OK(base->ConfigureOption(config_options_, "shared", "int=44")); |
||||||
|
ASSERT_FALSE(base->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||||
|
ASSERT_EQ(mismatch, "shared.int"); |
||||||
|
std::string c1value, c2value; |
||||||
|
ASSERT_OK(base->GetOption(config_options_, mismatch, &c1value)); |
||||||
|
ASSERT_OK(copy->GetOption(config_options_, mismatch, &c2value)); |
||||||
|
ASSERT_NE(c1value, c2value); |
||||||
|
} |
||||||
|
|
||||||
|
static Configurable* SimpleStructFactory() { |
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> struct_option_info = { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
{"struct", OptionTypeInfo::Struct("struct", &simple_option_info, 0, |
||||||
|
OptionVerificationType::kNormal, |
||||||
|
OptionTypeFlags::kMutable)}, |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
}; |
||||||
|
return SimpleConfigurable::Create( |
||||||
|
"simple-struct", TestConfigMode::kDefaultMode, &struct_option_info); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, ConfigureStructTest) { |
||||||
|
std::unique_ptr<Configurable> base(SimpleStructFactory()); |
||||||
|
std::unique_ptr<Configurable> copy(SimpleStructFactory()); |
||||||
|
std::string opt_str, value; |
||||||
|
std::string mismatch; |
||||||
|
std::unordered_set<std::string> names; |
||||||
|
|
||||||
|
ASSERT_OK( |
||||||
|
base->ConfigureFromString(config_options_, "struct={int=10; string=10}")); |
||||||
|
ASSERT_OK(base->GetOptionString(config_options_, &opt_str)); |
||||||
|
ASSERT_OK(copy->ConfigureFromString(config_options_, opt_str)); |
||||||
|
ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||||
|
ASSERT_OK(base->GetOptionNames(config_options_, &names)); |
||||||
|
ASSERT_EQ(names.size(), 1); |
||||||
|
ASSERT_EQ(*(names.begin()), "struct"); |
||||||
|
ASSERT_OK( |
||||||
|
base->ConfigureFromString(config_options_, "struct={int=20; string=20}")); |
||||||
|
ASSERT_OK(base->GetOption(config_options_, "struct", &value)); |
||||||
|
ASSERT_OK(copy->ConfigureOption(config_options_, "struct", value)); |
||||||
|
ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||||
|
|
||||||
|
ASSERT_NOK(base->ConfigureFromString(config_options_, |
||||||
|
"struct={int=10; string=10; bad=11}")); |
||||||
|
ASSERT_OK(base->ConfigureOption(config_options_, "struct.int", "42")); |
||||||
|
ASSERT_NOK(base->ConfigureOption(config_options_, "struct.bad", "42")); |
||||||
|
ASSERT_NOK(base->GetOption(config_options_, "struct.bad", &value)); |
||||||
|
ASSERT_OK(base->GetOption(config_options_, "struct.int", &value)); |
||||||
|
ASSERT_EQ(value, "42"); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, ConfigurableEnumTest) { |
||||||
|
std::unique_ptr<Configurable> base, copy; |
||||||
|
base.reset(SimpleConfigurable::Create("e", TestConfigMode::kEnumMode)); |
||||||
|
copy.reset(SimpleConfigurable::Create("e", TestConfigMode::kEnumMode)); |
||||||
|
|
||||||
|
std::string opts_str; |
||||||
|
std::string mismatch; |
||||||
|
|
||||||
|
ASSERT_OK(base->ConfigureFromString(config_options_, "enum=B")); |
||||||
|
ASSERT_FALSE(base->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||||
|
ASSERT_OK(base->GetOptionString(config_options_, &opts_str)); |
||||||
|
ASSERT_OK(copy->ConfigureFromString(config_options_, opts_str)); |
||||||
|
ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||||
|
ASSERT_NOK(base->ConfigureOption(config_options_, "enum", "bad")); |
||||||
|
ASSERT_NOK(base->ConfigureOption(config_options_, "unknown", "bad")); |
||||||
|
} |
||||||
|
|
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> noserialize_option_info = |
||||||
|
{ |
||||||
|
{"int", |
||||||
|
{offsetof(struct TestOptions, i), OptionType::kInt, |
||||||
|
OptionVerificationType::kNormal, OptionTypeFlags::kDontSerialize}}, |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, TestNoSerialize) { |
||||||
|
std::unique_ptr<Configurable> base; |
||||||
|
base.reset(SimpleConfigurable::Create("c", TestConfigMode::kDefaultMode, |
||||||
|
&noserialize_option_info)); |
||||||
|
std::string opts_str, value; |
||||||
|
ASSERT_OK(base->ConfigureFromString(config_options_, "int=10")); |
||||||
|
ASSERT_OK(base->GetOptionString(config_options_, &opts_str)); |
||||||
|
ASSERT_EQ(opts_str, ""); |
||||||
|
ASSERT_NOK(base->GetOption(config_options_, "int", &value)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(ConfigurableTest, TestNoCompare) { |
||||||
|
std::unordered_map<std::string, OptionTypeInfo> nocomp_option_info = { |
||||||
|
{"int", |
||||||
|
{offsetof(struct TestOptions, i), OptionType::kInt, |
||||||
|
OptionVerificationType::kNormal, OptionTypeFlags::kCompareNever}}, |
||||||
|
}; |
||||||
|
std::unordered_map<std::string, OptionTypeInfo> normal_option_info = { |
||||||
|
{"int", |
||||||
|
{offsetof(struct TestOptions, i), OptionType::kInt, |
||||||
|
OptionVerificationType::kNormal, OptionTypeFlags::kNone}}, |
||||||
|
}; |
||||||
|
|
||||||
|
std::unique_ptr<Configurable> base, copy; |
||||||
|
base.reset(SimpleConfigurable::Create("c", TestConfigMode::kDefaultMode, |
||||||
|
&nocomp_option_info)); |
||||||
|
copy.reset(SimpleConfigurable::Create("c", TestConfigMode::kDefaultMode, |
||||||
|
&normal_option_info)); |
||||||
|
ASSERT_OK(base->ConfigureFromString(config_options_, "int=10")); |
||||||
|
ASSERT_OK(copy->ConfigureFromString(config_options_, "int=20")); |
||||||
|
std::string bvalue, cvalue, mismatch; |
||||||
|
ASSERT_OK(base->GetOption(config_options_, "int", &bvalue)); |
||||||
|
ASSERT_OK(copy->GetOption(config_options_, "int", &cvalue)); |
||||||
|
ASSERT_EQ(bvalue, "10"); |
||||||
|
ASSERT_EQ(cvalue, "20"); |
||||||
|
ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||||
|
ASSERT_FALSE(copy->AreEquivalent(config_options_, base.get(), &mismatch)); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
void ConfigurableParamTest::TestConfigureOptions( |
||||||
|
const ConfigOptions& config_options) { |
||||||
|
std::unique_ptr<Configurable> base, copy; |
||||||
|
std::unordered_set<std::string> names; |
||||||
|
std::string opt_str, mismatch; |
||||||
|
|
||||||
|
base.reset(factory_()); |
||||||
|
copy.reset(factory_()); |
||||||
|
|
||||||
|
ASSERT_OK(base->ConfigureFromString(config_options, configuration_)); |
||||||
|
ASSERT_OK(base->GetOptionString(config_options, &opt_str)); |
||||||
|
ASSERT_OK(copy->ConfigureFromString(config_options, opt_str)); |
||||||
|
ASSERT_OK(copy->GetOptionString(config_options, &opt_str)); |
||||||
|
ASSERT_TRUE(base->AreEquivalent(config_options, copy.get(), &mismatch)); |
||||||
|
|
||||||
|
copy.reset(factory_()); |
||||||
|
ASSERT_OK(base->GetOptionNames(config_options, &names)); |
||||||
|
std::unordered_map<std::string, std::string> unused; |
||||||
|
bool found_one = false; |
||||||
|
for (auto name : names) { |
||||||
|
std::string value; |
||||||
|
Status s = base->GetOption(config_options, name, &value); |
||||||
|
if (s.ok()) { |
||||||
|
s = copy->ConfigureOption(config_options, name, value); |
||||||
|
if (s.ok() || s.IsNotSupported()) { |
||||||
|
found_one = true; |
||||||
|
} else { |
||||||
|
unused[name] = value; |
||||||
|
} |
||||||
|
} else { |
||||||
|
ASSERT_TRUE(s.IsNotSupported()); |
||||||
|
} |
||||||
|
} |
||||||
|
ASSERT_TRUE(found_one || names.empty()); |
||||||
|
while (found_one && !unused.empty()) { |
||||||
|
found_one = false; |
||||||
|
for (auto iter = unused.begin(); iter != unused.end();) { |
||||||
|
if (copy->ConfigureOption(config_options, iter->first, iter->second) |
||||||
|
.ok()) { |
||||||
|
found_one = true; |
||||||
|
iter = unused.erase(iter); |
||||||
|
} else { |
||||||
|
++iter; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
ASSERT_EQ(0, unused.size()); |
||||||
|
ASSERT_TRUE(base->AreEquivalent(config_options, copy.get(), &mismatch)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ConfigurableParamTest, GetDefaultOptionsTest) { |
||||||
|
TestConfigureOptions(config_options_); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ConfigurableParamTest, ConfigureFromPropsTest) { |
||||||
|
std::string opt_str, mismatch; |
||||||
|
std::unordered_set<std::string> names; |
||||||
|
std::unique_ptr<Configurable> copy(factory_()); |
||||||
|
|
||||||
|
ASSERT_OK(object_->ConfigureFromString(config_options_, configuration_)); |
||||||
|
config_options_.delimiter = "\n"; |
||||||
|
ASSERT_OK(object_->GetOptionString(config_options_, &opt_str)); |
||||||
|
std::istringstream iss(opt_str); |
||||||
|
std::unordered_map<std::string, std::string> copy_map; |
||||||
|
std::string line; |
||||||
|
for (int line_num = 0; std::getline(iss, line); line_num++) { |
||||||
|
std::string name; |
||||||
|
std::string value; |
||||||
|
ASSERT_OK( |
||||||
|
RocksDBOptionsParser::ParseStatement(&name, &value, line, line_num)); |
||||||
|
copy_map[name] = value; |
||||||
|
} |
||||||
|
ASSERT_OK(copy->ConfigureFromMap(config_options_, copy_map)); |
||||||
|
ASSERT_TRUE(object_->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||||
|
} |
||||||
|
|
||||||
|
static Configurable* SimpleFactory() { |
||||||
|
return SimpleConfigurable::Create("simple"); |
||||||
|
} |
||||||
|
|
||||||
|
static Configurable* UniqueFactory() { |
||||||
|
return SimpleConfigurable::Create( |
||||||
|
"simple", TestConfigMode::kSimpleMode | TestConfigMode::kUniqueMode); |
||||||
|
} |
||||||
|
static Configurable* SharedFactory() { |
||||||
|
return SimpleConfigurable::Create( |
||||||
|
"simple", TestConfigMode::kSimpleMode | TestConfigMode::kSharedMode); |
||||||
|
} |
||||||
|
|
||||||
|
static Configurable* NestedFactory() { |
||||||
|
return SimpleConfigurable::Create( |
||||||
|
"simple", TestConfigMode::kSimpleMode | TestConfigMode::kNestedMode); |
||||||
|
} |
||||||
|
|
||||||
|
static Configurable* MutableFactory() { |
||||||
|
return SimpleConfigurable::Create("simple", TestConfigMode::kMutableMode | |
||||||
|
TestConfigMode::kSimpleMode | |
||||||
|
TestConfigMode::kNestedMode); |
||||||
|
} |
||||||
|
|
||||||
|
static Configurable* ThreeWrappedFactory() { |
||||||
|
std::shared_ptr<Configurable> child; |
||||||
|
child.reset( |
||||||
|
SimpleConfigurable::Create("child", TestConfigMode::kDefaultMode)); |
||||||
|
std::shared_ptr<Configurable> parent; |
||||||
|
parent.reset( |
||||||
|
new WrappedConfigurable("parent", TestConfigMode::kDefaultMode, child)); |
||||||
|
return new WrappedConfigurable("master", TestConfigMode::kDefaultMode, |
||||||
|
parent); |
||||||
|
} |
||||||
|
|
||||||
|
static Configurable* ThreeDeepFactory() { |
||||||
|
Configurable* simple = SimpleConfigurable::Create( |
||||||
|
"Simple", TestConfigMode::kUniqueMode | TestConfigMode::kDefaultMode); |
||||||
|
auto* unique = |
||||||
|
simple->GetOptions<std::unique_ptr<Configurable>>("SimpleUnique"); |
||||||
|
unique->reset(SimpleConfigurable::Create( |
||||||
|
"Child", TestConfigMode::kUniqueMode | TestConfigMode::kDefaultMode)); |
||||||
|
unique = |
||||||
|
unique->get()->GetOptions<std::unique_ptr<Configurable>>("ChildUnique"); |
||||||
|
unique->reset( |
||||||
|
SimpleConfigurable::Create("Child", TestConfigMode::kDefaultMode)); |
||||||
|
return simple; |
||||||
|
} |
||||||
|
|
||||||
|
static Configurable* DBOptionsFactory() { |
||||||
|
auto config = DBOptionsAsConfigurable(DBOptions()); |
||||||
|
return config.release(); |
||||||
|
} |
||||||
|
|
||||||
|
static Configurable* CFOptionsFactory() { |
||||||
|
auto config = CFOptionsAsConfigurable(ColumnFamilyOptions()); |
||||||
|
return config.release(); |
||||||
|
} |
||||||
|
|
||||||
|
static Configurable* BlockBasedFactory() { return NewBlockBasedTableFactory(); } |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P( |
||||||
|
ParamTest, ConfigurableParamTest, |
||||||
|
testing::Values( |
||||||
|
std::pair<std::string, ConfigTestFactoryFunc>( |
||||||
|
"int=42;bool=true;string=s", SimpleFactory), |
||||||
|
std::pair<std::string, ConfigTestFactoryFunc>( |
||||||
|
"int=42;unique={int=33;string=unique}", MutableFactory), |
||||||
|
std::pair<std::string, ConfigTestFactoryFunc>( |
||||||
|
"struct={int=33;bool=true;string=s;}", SimpleStructFactory), |
||||||
|
std::pair<std::string, ConfigTestFactoryFunc>( |
||||||
|
"int=33;bool=true;string=outer;" |
||||||
|
"shared={int=42;string=shared}", |
||||||
|
SharedFactory), |
||||||
|
std::pair<std::string, ConfigTestFactoryFunc>( |
||||||
|
"int=33;bool=true;string=outer;" |
||||||
|
"unique={int=42;string=unique}", |
||||||
|
UniqueFactory), |
||||||
|
std::pair<std::string, ConfigTestFactoryFunc>( |
||||||
|
"int=11;bool=true;string=outer;" |
||||||
|
"pointer={int=22;string=pointer};" |
||||||
|
"unique={int=33;string=unique};" |
||||||
|
"shared={int=44;string=shared}", |
||||||
|
NestedFactory), |
||||||
|
std::pair<std::string, ConfigTestFactoryFunc>( |
||||||
|
"int=11;bool=true;string=outer;" |
||||||
|
"inner={int=22;string=parent;" |
||||||
|
"inner={int=33;string=child}};", |
||||||
|
ThreeWrappedFactory), |
||||||
|
std::pair<std::string, ConfigTestFactoryFunc>( |
||||||
|
"int=11;bool=true;string=outer;" |
||||||
|
"unique={int=22;string=inner;" |
||||||
|
"unique={int=33;string=unique}};", |
||||||
|
ThreeDeepFactory), |
||||||
|
std::pair<std::string, ConfigTestFactoryFunc>("max_background_jobs=100;" |
||||||
|
"max_open_files=200;", |
||||||
|
DBOptionsFactory), |
||||||
|
std::pair<std::string, ConfigTestFactoryFunc>( |
||||||
|
"table_factory=BlockBasedTable;" |
||||||
|
"disable_auto_compactions=true;", |
||||||
|
CFOptionsFactory), |
||||||
|
std::pair<std::string, ConfigTestFactoryFunc>("block_size=1024;" |
||||||
|
"no_block_cache=true;", |
||||||
|
BlockBasedFactory))); |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
#ifdef GFLAGS |
||||||
|
ParseCommandLineFlags(&argc, &argv, true); |
||||||
|
#endif // GFLAGS
|
||||||
|
return RUN_ALL_TESTS(); |
||||||
|
} |
@ -0,0 +1,127 @@ |
|||||||
|
// 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).
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
#include <algorithm> |
||||||
|
#include <memory> |
||||||
|
#include <unordered_map> |
||||||
|
|
||||||
|
#include "options/configurable_helper.h" |
||||||
|
#include "rocksdb/configurable.h" |
||||||
|
#include "rocksdb/utilities/options_type.h" |
||||||
|
|
||||||
|
namespace ROCKSDB_NAMESPACE { |
||||||
|
struct ColumnFamilyOptions; |
||||||
|
struct DBOptions; |
||||||
|
|
||||||
|
namespace test { |
||||||
|
enum TestEnum { kTestA, kTestB }; |
||||||
|
|
||||||
|
static const std::unordered_map<std::string, int> test_enum_map = { |
||||||
|
{"A", TestEnum::kTestA}, |
||||||
|
{"B", TestEnum::kTestB}, |
||||||
|
}; |
||||||
|
|
||||||
|
struct TestOptions { |
||||||
|
int i = 0; |
||||||
|
bool b = false; |
||||||
|
bool d = true; |
||||||
|
TestEnum e = TestEnum::kTestA; |
||||||
|
std::string s = ""; |
||||||
|
std::string u = ""; |
||||||
|
}; |
||||||
|
|
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> simple_option_info = { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
{"int", |
||||||
|
{offsetof(struct TestOptions, i), OptionType::kInt, |
||||||
|
OptionVerificationType::kNormal, OptionTypeFlags::kMutable}}, |
||||||
|
{"bool", |
||||||
|
{offsetof(struct TestOptions, b), OptionType::kBoolean, |
||||||
|
OptionVerificationType::kNormal, OptionTypeFlags::kNone}}, |
||||||
|
{"string", |
||||||
|
{offsetof(struct TestOptions, s), OptionType::kString, |
||||||
|
OptionVerificationType::kNormal, OptionTypeFlags::kNone}}, |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
}; |
||||||
|
|
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> enum_option_info = { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
{"enum", |
||||||
|
OptionTypeInfo::Enum(offsetof(struct TestOptions, e), &test_enum_map)} |
||||||
|
#endif |
||||||
|
}; |
||||||
|
|
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> unique_option_info = { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
{"unique", |
||||||
|
{0, OptionType::kConfigurable, OptionVerificationType::kNormal, |
||||||
|
(OptionTypeFlags::kUnique | OptionTypeFlags::kMutable)}}, |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
}; |
||||||
|
|
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> shared_option_info = { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
{"shared", |
||||||
|
{0, OptionType::kConfigurable, OptionVerificationType::kNormal, |
||||||
|
(OptionTypeFlags::kShared)}}, |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
}; |
||||||
|
static std::unordered_map<std::string, OptionTypeInfo> pointer_option_info = { |
||||||
|
#ifndef ROCKSDB_LITE |
||||||
|
{"pointer", |
||||||
|
{0, OptionType::kConfigurable, OptionVerificationType::kNormal, |
||||||
|
OptionTypeFlags::kRawPointer}}, |
||||||
|
#endif // ROCKSDB_LITE
|
||||||
|
}; |
||||||
|
|
||||||
|
enum TestConfigMode { |
||||||
|
kEmptyMode = 0x0, // Don't register anything
|
||||||
|
kMutableMode = 0x01, // Configuration is mutable
|
||||||
|
kSimpleMode = 0x02, // Use the simple options
|
||||||
|
kEnumMode = 0x04, // Use the enum options
|
||||||
|
kDefaultMode = kSimpleMode, // Use no inner nested configurations
|
||||||
|
kSharedMode = 0x10, // Use shared configuration
|
||||||
|
kUniqueMode = 0x20, // Use unique configuration
|
||||||
|
kRawPtrMode = 0x40, // Use pointer configuration
|
||||||
|
kNestedMode = (kSharedMode | kUniqueMode | kRawPtrMode), |
||||||
|
kAllOptMode = (kNestedMode | kEnumMode | kSimpleMode), |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
class TestConfigurable : public Configurable { |
||||||
|
protected: |
||||||
|
std::string name_; |
||||||
|
std::string prefix_; |
||||||
|
TestOptions options_; |
||||||
|
|
||||||
|
public: |
||||||
|
std::unique_ptr<T> unique_; |
||||||
|
std::shared_ptr<T> shared_; |
||||||
|
T* pointer_; |
||||||
|
|
||||||
|
TestConfigurable(const std::string& name, int mode, |
||||||
|
const std::unordered_map<std::string, OptionTypeInfo>* map = |
||||||
|
&simple_option_info) |
||||||
|
: name_(name), pointer_(nullptr) { |
||||||
|
prefix_ = "test." + name + "."; |
||||||
|
if ((mode & TestConfigMode::kSimpleMode) != 0) { |
||||||
|
ConfigurableHelper::RegisterOptions(*this, name_, &options_, map); |
||||||
|
} |
||||||
|
if ((mode & TestConfigMode::kEnumMode) != 0) { |
||||||
|
ConfigurableHelper::RegisterOptions(*this, name_ + "Enum", &options_, |
||||||
|
&enum_option_info); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
~TestConfigurable() override { delete pointer_; } |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,49 @@ |
|||||||
|
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||||
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||||
|
// 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 "rocksdb/convenience.h" |
||||||
|
#include "rocksdb/table.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 { |
||||||
|
|
||||||
|
Status TableFactory::CreateFromString(const ConfigOptions& config_options_in, |
||||||
|
const std::string& id, |
||||||
|
std::shared_ptr<TableFactory>* factory) { |
||||||
|
Status status; |
||||||
|
std::string name = id; |
||||||
|
|
||||||
|
std::string existing_opts; |
||||||
|
|
||||||
|
ConfigOptions config_options = config_options_in; |
||||||
|
if (factory->get() != nullptr && name == factory->get()->Name()) { |
||||||
|
config_options.delimiter = ";"; |
||||||
|
|
||||||
|
status = factory->get()->GetOptionString(config_options, &existing_opts); |
||||||
|
if (!status.ok()) { |
||||||
|
return status; |
||||||
|
} |
||||||
|
} |
||||||
|
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
|
||||||
|
} else { |
||||||
|
return Status::NotSupported("Could not load table factory: ", name); |
||||||
|
} |
||||||
|
if (!existing_opts.empty()) { |
||||||
|
config_options.invoke_prepare_options = false; |
||||||
|
status = factory->get()->ConfigureFromString(config_options, existing_opts); |
||||||
|
} |
||||||
|
return status; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace ROCKSDB_NAMESPACE
|
Loading…
Reference in new issue