Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialize only options that have changed from the some default #648

Merged
merged 10 commits into from
Oct 31, 2023
3 changes: 3 additions & 0 deletions include/rocksdb/convenience.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ struct ConfigOptions {
// The object registry to use for this options
std::shared_ptr<ObjectRegistry> registry;

// If set, only changes from this reference version will be serialized.
Configurable* compare_to = nullptr;

bool IsShallow() const { return depth == Depth::kDepthShallow; }
bool IsDetailed() const { return depth == Depth::kDepthDetailed; }

Expand Down
107 changes: 100 additions & 7 deletions options/configurable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -541,16 +541,90 @@ Status ConfigurableHelper::SerializeOptions(const ConfigOptions& config_options,
const std::string& prefix,
OptionProperties* props) {
assert(props);
for (auto const& opt_iter : configurable.options_) {
if (opt_iter.type_map != nullptr) {
Status s = OptionTypeInfo::SerializeType(config_options, prefix,
*(opt_iter.type_map),
opt_iter.opt_ptr, props);
if (!s.ok()) {
return s;
ConfigOptions copy = config_options;
auto compare_to = config_options.compare_to;
if (compare_to != nullptr && !MayBeEquivalent(configurable, *compare_to)) {
// If we are comparing this type to another, first see if the types
// are the same. If not, forget it
compare_to = nullptr;
}

Status s;
for (size_t i = 0; i < configurable.options_.size(); i++) {
const auto& opt = configurable.options_[i];
if (opt.type_map != nullptr) {
const auto opt_addr = opt.opt_ptr;
for (const auto& opt_iter : *(opt.type_map)) {
std::string single;
const auto& opt_name = opt_iter.first;
const auto& opt_info = opt_iter.second;
bool should_serialize = opt_info.ShouldSerialize();
if (should_serialize && compare_to != nullptr) {
// This option should be serialized but there is a possiblity that it
// matches the default. Check to see if we really should serialize it
std::string mismatch;
if (opt_info.IsConfigurable() &&
opt_info.IsEnabled(OptionTypeFlags::kStringNameOnly) &&
!config_options.IsDetailed()) {
// If it is a Configurable name-only and we are not printing the
// details, then compare loosely
copy.sanity_level = ConfigOptions::kSanityLevelLooselyCompatible;
if (opt_info.AreEqual(copy, opt_name, opt_addr,
compare_to->options_[i].opt_ptr, &mismatch)) {
should_serialize = false;
}
copy.sanity_level = config_options.sanity_level;
} else if (opt_info.AreEqual(config_options, opt_name, opt_addr,
compare_to->options_[i].opt_ptr,
&mismatch)) {
should_serialize = false;
}
}
if (should_serialize) {
if (compare_to != nullptr && opt_info.IsCustomizable()) {
copy.compare_to = opt_info.AsRawPointer<Customizable>(
compare_to->options_[i].opt_ptr);
} else {
copy.compare_to = compare_to;
}
s = SerializeOption(copy,
OptionTypeInfo::MakePrefix(prefix, opt_name),
opt_info, opt_addr, &single);
if (!s.ok()) {
return s;
} else if (!single.empty()) {
props->insert_or_assign(opt_name, single);
}
}
}
}
}
return s;
}

Status ConfigurableHelper::SerializeOption(const ConfigOptions& config_options,
const std::string& opt_name,
const OptionTypeInfo& opt_info,
const void* opt_addr,
std::string* value) {
if (opt_info.ShouldSerialize()) {
if (!config_options.mutable_options_only) {
return opt_info.Serialize(config_options, opt_name, opt_addr, value);
} else if (opt_info.IsMutable()) {
ConfigOptions copy = config_options;
copy.mutable_options_only = false;
return opt_info.Serialize(copy, opt_name, opt_addr, value);
} else if (opt_info.IsConfigurable()) {
// If it is a Configurable and we are either printing all of the
// details or not printing only the name, this option should be
// included in the list
if (config_options.IsDetailed() ||
!opt_info.IsEnabled(OptionTypeFlags::kStringNameOnly)) {
return opt_info.Serialize(config_options, opt_name, opt_addr, value);
}
}
}
value->clear();
return Status::OK();
}

Expand Down Expand Up @@ -596,6 +670,25 @@ Status ConfigurableHelper::ListOptions(
//
//*******************************************************************************

bool ConfigurableHelper::MayBeEquivalent(const Configurable& this_one,
const Configurable& that_one) {
if (this_one.options_.size() != that_one.options_.size()) {
// The two types do not have the same number of registered options,
// therefore they cannot be the same.
return false;
}

for (size_t i = 0; i < this_one.options_.size(); i++) {
const auto& this_opt = this_one.options_[i];
const auto& that_opt = that_one.options_[i];
if (this_opt.name != that_opt.name ||
this_opt.type_map != that_opt.type_map) {
return false;
}
}
return true;
}

bool Configurable::AreEquivalent(const ConfigOptions& config_options,
const Configurable* other,
std::string* name) const {
Expand Down
17 changes: 17 additions & 0 deletions options/configurable_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,19 @@ class ConfigurableHelper {
const std::string& prefix,
OptionProperties* props);

// Serializes a single option to its string representation
// @param opt_name The name of the option
// @param opt_info The type and related information of the option
// @param opt_addr The address of the option
// @param value The string representation of the option.
// @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 SerializeOption(const ConfigOptions& config_options,
const std::string& opt_name,
const OptionTypeInfo& opt_info,
const void* opt_addr, std::string* value);

// Internal method to list the option names for this object.
// Classes may override this value to change its behavior.
// @see ListOptions for more details
Expand All @@ -173,6 +186,10 @@ class ConfigurableHelper {
const Configurable& that_one,
std::string* mismatch);

// Checks to see if the two Configurable classes may be equivalent
static bool MayBeEquivalent(const Configurable& this_one,
const Configurable& that_one);

private:
// Looks for the option specified by name in the RegisteredOptions.
// This method traverses the types in the input options vector. If an entry
Expand Down
50 changes: 8 additions & 42 deletions options/options_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1136,38 +1136,22 @@ Status OptionTypeInfo::SerializeType(
const ConfigOptions& config_options, const std::string& prefix,
const std::unordered_map<std::string, OptionTypeInfo>& type_map,
const void* opt_addr, OptionProperties* props) {
Status status;
for (const auto& iter : type_map) {
std::string single;
const auto& opt_name = iter.first;
const auto& opt_info = iter.second;
if (opt_info.ShouldSerialize()) {
if (!config_options.mutable_options_only) {
status = opt_info.Serialize(
config_options, MakePrefix(prefix, opt_name), opt_addr, &single);
} else if (opt_info.IsMutable()) {
ConfigOptions copy = config_options;
copy.mutable_options_only = false;
status = opt_info.Serialize(copy, MakePrefix(prefix, opt_name),
opt_addr, &single);
} else if (opt_info.IsConfigurable()) {
// If it is a Configurable and we are either printing all of the
// details or not printing only the name, this option should be
// included in the list
if (config_options.IsDetailed() ||
!opt_info.IsEnabled(OptionTypeFlags::kStringNameOnly)) {
status = opt_info.Serialize(
config_options, MakePrefix(prefix, opt_name), opt_addr, &single);
}
}
Status status = ConfigurableHelper::SerializeOption(
config_options, MakePrefix(prefix, opt_name), opt_info, opt_addr,
&single);
if (!status.ok()) {
return status;
} else if (!single.empty()) {
props->insert_or_assign(iter.first, single);
}
}
}
return status;
return Status::OK();
}

Status OptionTypeInfo::SerializeStruct(
Expand Down Expand Up @@ -1297,7 +1281,8 @@ bool OptionTypeInfo::AreEqual(const ConfigOptions& config_options,
const void* const that_ptr,
std::string* mismatch) const {
auto level = GetSanityLevel();
if (!config_options.IsCheckEnabled(level)) {
if (config_options.compare_to == nullptr &&
!config_options.IsCheckEnabled(level)) {
return true; // If the sanity level is not being checked, skip it
}
if (this_ptr == nullptr || that_ptr == nullptr) {
Expand All @@ -1324,7 +1309,8 @@ bool OptionTypeInfo::AreEqual(const ConfigOptions& config_options,
} else if (this_config != nullptr && that_config != nullptr) {
std::string bad_name;
bool matches;
if (level < config_options.sanity_level) {
if (config_options.compare_to == nullptr &&
level < config_options.sanity_level) {
ConfigOptions copy = config_options;
copy.sanity_level = level;
matches = this_config->AreEquivalent(copy, that_config, &bad_name);
Expand Down Expand Up @@ -1406,26 +1392,6 @@ bool OptionTypeInfo::StructsAreEqual(
return matches;
}

bool MatchesOptionsTypeFromMap(
const ConfigOptions& config_options,
const std::unordered_map<std::string, OptionTypeInfo>& type_map,
const void* const this_ptr, const void* const that_ptr,
std::string* mismatch) {
for (auto& pair : type_map) {
// We skip checking deprecated variables as they might
// contain random values since they might not be initialized
if (config_options.IsCheckEnabled(pair.second.GetSanityLevel())) {
if (!pair.second.AreEqual(config_options, pair.first, this_ptr, that_ptr,
mismatch) &&
!pair.second.AreEqualByName(config_options, pair.first, this_ptr,
that_ptr)) {
return false;
}
}
}
return true;
}

bool OptionTypeInfo::AreEqualByName(const ConfigOptions& config_options,
const std::string& opt_name,
const void* const this_ptr,
Expand Down