1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
6 #include "rocksdb/configurable.h"
8 #include "logging/logging.h"
9 #include "options/configurable_helper.h"
10 #include "options/options_helper.h"
11 #include "rocksdb/customizable.h"
12 #include "rocksdb/status.h"
13 #include "rocksdb/utilities/object_registry.h"
14 #include "rocksdb/utilities/options_type.h"
15 #include "util/coding.h"
16 #include "util/string_util.h"
18 namespace ROCKSDB_NAMESPACE
{
20 void ConfigurableHelper::RegisterOptions(
21 Configurable
& configurable
, const std::string
& name
, void* opt_ptr
,
22 const std::unordered_map
<std::string
, OptionTypeInfo
>* type_map
) {
23 Configurable::RegisteredOptions opts
;
26 opts
.type_map
= type_map
;
29 #endif // ROCKSDB_LITE
30 opts
.opt_ptr
= opt_ptr
;
31 configurable
.options_
.emplace_back(opts
);
34 //*************************************************************************
36 // Methods for Initializing and Validating Configurable Objects
38 //*************************************************************************
40 Status
Configurable::PrepareOptions(const ConfigOptions
& opts
) {
41 Status status
= Status::OK();
43 for (auto opt_iter
: options_
) {
44 for (auto map_iter
: *(opt_iter
.type_map
)) {
45 auto& opt_info
= map_iter
.second
;
46 if (!opt_info
.IsDeprecated() && !opt_info
.IsAlias() &&
47 opt_info
.IsConfigurable()) {
48 if (!opt_info
.IsEnabled(OptionTypeFlags::kDontPrepare
)) {
49 Configurable
* config
=
50 opt_info
.AsRawPointer
<Configurable
>(opt_iter
.opt_ptr
);
51 if (config
!= nullptr) {
52 status
= config
->PrepareOptions(opts
);
63 #endif // ROCKSDB_LITE
70 Status
Configurable::ValidateOptions(const DBOptions
& db_opts
,
71 const ColumnFamilyOptions
& cf_opts
) const {
74 for (auto opt_iter
: options_
) {
75 for (auto map_iter
: *(opt_iter
.type_map
)) {
76 auto& opt_info
= map_iter
.second
;
77 if (!opt_info
.IsDeprecated() && !opt_info
.IsAlias()) {
78 if (opt_info
.IsConfigurable()) {
79 const Configurable
* config
=
80 opt_info
.AsRawPointer
<Configurable
>(opt_iter
.opt_ptr
);
81 if (config
!= nullptr) {
82 status
= config
->ValidateOptions(db_opts
, cf_opts
);
83 } else if (!opt_info
.CanBeNull()) {
85 Status::NotFound("Missing configurable object", map_iter
.first
);
97 #endif // ROCKSDB_LITE
101 /*********************************************************************************/
103 /* Methods for Retrieving Options from Configurables */
105 /*********************************************************************************/
107 const void* Configurable::GetOptionsPtr(const std::string
& name
) const {
108 for (auto o
: options_
) {
109 if (o
.name
== name
) {
116 std::string
Configurable::GetOptionName(const std::string
& opt_name
) const {
121 const OptionTypeInfo
* ConfigurableHelper::FindOption(
122 const std::vector
<Configurable::RegisteredOptions
>& options
,
123 const std::string
& short_name
, std::string
* opt_name
, void** opt_ptr
) {
124 for (auto iter
: options
) {
125 const auto opt_info
=
126 OptionTypeInfo::Find(short_name
, *(iter
.type_map
), opt_name
);
127 if (opt_info
!= nullptr) {
128 *opt_ptr
= iter
.opt_ptr
;
134 #endif // ROCKSDB_LITE
136 //*************************************************************************
138 // Methods for Configuring Options from Strings/Name-Value Pairs/Maps
140 //*************************************************************************
142 Status
Configurable::ConfigureFromMap(
143 const ConfigOptions
& config_options
,
144 const std::unordered_map
<std::string
, std::string
>& opts_map
) {
145 Status s
= ConfigureFromMap(config_options
, opts_map
, nullptr);
149 Status
Configurable::ConfigureFromMap(
150 const ConfigOptions
& config_options
,
151 const std::unordered_map
<std::string
, std::string
>& opts_map
,
152 std::unordered_map
<std::string
, std::string
>* unused
) {
153 return ConfigureOptions(config_options
, opts_map
, unused
);
156 Status
Configurable::ConfigureOptions(
157 const ConfigOptions
& config_options
,
158 const std::unordered_map
<std::string
, std::string
>& opts_map
,
159 std::unordered_map
<std::string
, std::string
>* unused
) {
160 std::string curr_opts
;
162 if (!config_options
.ignore_unknown_options
) {
163 // If we are not ignoring unused, get the defaults in case we need to reset
164 GetOptionString(config_options
, &curr_opts
).PermitUncheckedError();
166 #endif // ROCKSDB_LITE
167 Status s
= ConfigurableHelper::ConfigureOptions(config_options
, *this,
169 if (config_options
.invoke_prepare_options
&& s
.ok()) {
170 s
= PrepareOptions(config_options
);
173 if (!s
.ok() && !curr_opts
.empty()) {
174 ConfigOptions reset
= config_options
;
175 reset
.ignore_unknown_options
= true;
176 reset
.invoke_prepare_options
= true;
177 // There are some options to reset from this current error
178 ConfigureFromString(reset
, curr_opts
).PermitUncheckedError();
180 #endif // ROCKSDB_LITE
184 Status
Configurable::ParseStringOptions(const ConfigOptions
& /*config_options*/,
185 const std::string
& /*opts_str*/) {
189 Status
Configurable::ConfigureFromString(const ConfigOptions
& config_options
,
190 const std::string
& opts_str
) {
192 if (!opts_str
.empty()) {
194 if (opts_str
.find(';') != std::string::npos
||
195 opts_str
.find('=') != std::string::npos
) {
196 std::unordered_map
<std::string
, std::string
> opt_map
;
197 s
= StringToMap(opts_str
, &opt_map
);
199 s
= ConfigureFromMap(config_options
, opt_map
, nullptr);
202 #endif // ROCKSDB_LITE
203 s
= ParseStringOptions(config_options
, opts_str
);
204 if (s
.ok() && config_options
.invoke_prepare_options
) {
205 s
= PrepareOptions(config_options
);
209 #endif // ROCKSDB_LITE
210 } else if (config_options
.invoke_prepare_options
) {
211 s
= PrepareOptions(config_options
);
220 * Sets the value of the named property to the input value, returning OK on
223 Status
Configurable::ConfigureOption(const ConfigOptions
& config_options
,
224 const std::string
& name
,
225 const std::string
& value
) {
226 const std::string
& opt_name
= GetOptionName(name
);
227 return ConfigurableHelper::ConfigureSingleOption(config_options
, *this,
232 * Looks for the named option amongst the options for this type and sets
233 * the value for it to be the input value.
234 * If the name was found, found_option will be set to true and the resulting
235 * status should be returned.
238 Status
Configurable::ParseOption(const ConfigOptions
& config_options
,
239 const OptionTypeInfo
& opt_info
,
240 const std::string
& opt_name
,
241 const std::string
& opt_value
, void* opt_ptr
) {
242 if (opt_info
.IsMutable() || opt_info
.IsConfigurable()) {
243 return opt_info
.Parse(config_options
, opt_name
, opt_value
, opt_ptr
);
244 } else if (prepared_
) {
245 return Status::InvalidArgument("Option not changeable: " + opt_name
);
247 return opt_info
.Parse(config_options
, opt_name
, opt_value
, opt_ptr
);
251 #endif // ROCKSDB_LITE
253 Status
ConfigurableHelper::ConfigureOptions(
254 const ConfigOptions
& config_options
, Configurable
& configurable
,
255 const std::unordered_map
<std::string
, std::string
>& opts_map
,
256 std::unordered_map
<std::string
, std::string
>* unused
) {
257 std::unordered_map
<std::string
, std::string
> remaining
= opts_map
;
258 Status s
= Status::OK();
259 if (!opts_map
.empty()) {
261 for (const auto& iter
: configurable
.options_
) {
262 s
= ConfigureSomeOptions(config_options
, configurable
, *(iter
.type_map
),
263 &remaining
, iter
.opt_ptr
);
264 if (remaining
.empty()) { // Are there more options left?
266 } else if (!s
.ok()) {
272 if (!config_options
.ignore_unknown_options
) {
273 s
= Status::NotSupported("ConfigureFromMap not supported in LITE mode");
275 #endif // ROCKSDB_LITE
277 if (unused
!= nullptr && !remaining
.empty()) {
278 unused
->insert(remaining
.begin(), remaining
.end());
280 if (config_options
.ignore_unknown_options
) {
282 } else if (s
.ok() && unused
== nullptr && !remaining
.empty()) {
283 s
= Status::NotFound("Could not find option: ", remaining
.begin()->first
);
290 * Updates the object with the named-value property values, returning OK on
291 * succcess. Any properties that were found are removed from the options list;
292 * upon return only options that were not found in this opt_map remain.
295 * - OK if ignore_unknown_options is set
296 * - InvalidArgument, if any option was invalid
297 * - NotSupported, if any option is unsupported and ignore_unsupported_options
299 * - OK, if no option was invalid or not supported (or ignored)
301 Status
ConfigurableHelper::ConfigureSomeOptions(
302 const ConfigOptions
& config_options
, Configurable
& configurable
,
303 const std::unordered_map
<std::string
, OptionTypeInfo
>& type_map
,
304 std::unordered_map
<std::string
, std::string
>* options
, void* opt_ptr
) {
305 Status result
= Status::OK(); // The last non-OK result (if any)
306 Status notsup
= Status::OK(); // The last NotSupported result (if any)
307 std::string elem_name
;
309 std::unordered_set
<std::string
> unsupported
;
310 // While there are unused properties and we processed at least one,
311 // go through the remaining unused properties and attempt to configure them.
312 while (found
> 0 && !options
->empty()) {
314 notsup
= Status::OK();
315 for (auto it
= options
->begin(); it
!= options
->end();) {
316 const std::string
& opt_name
= configurable
.GetOptionName(it
->first
);
317 const std::string
& opt_value
= it
->second
;
318 const auto opt_info
=
319 OptionTypeInfo::Find(opt_name
, type_map
, &elem_name
);
320 if (opt_info
== nullptr) { // Did not find the option. Skip it
323 Status s
= ConfigureOption(config_options
, configurable
, *opt_info
,
324 opt_name
, elem_name
, opt_value
, opt_ptr
);
325 if (s
.IsNotFound()) {
327 } else if (s
.IsNotSupported()) {
329 unsupported
.insert(it
->first
);
330 ++it
; // Skip it for now
333 it
= options
->erase(it
);
339 } // End for all remaining options
340 } // End while found one or options remain
342 // Now that we have been through the list, remove any unsupported
343 for (auto u
: unsupported
) {
344 auto it
= options
->find(u
);
345 if (it
!= options
->end()) {
349 if (config_options
.ignore_unknown_options
) {
350 if (!result
.ok()) result
.PermitUncheckedError();
351 if (!notsup
.ok()) notsup
.PermitUncheckedError();
353 } else if (!result
.ok()) {
354 if (!notsup
.ok()) notsup
.PermitUncheckedError();
356 } else if (config_options
.ignore_unsupported_options
) {
357 if (!notsup
.ok()) notsup
.PermitUncheckedError();
364 Status
ConfigurableHelper::ConfigureSingleOption(
365 const ConfigOptions
& config_options
, Configurable
& configurable
,
366 const std::string
& name
, const std::string
& value
) {
367 std::string opt_name
;
368 void* opt_ptr
= nullptr;
369 const auto opt_info
=
370 FindOption(configurable
.options_
, name
, &opt_name
, &opt_ptr
);
371 if (opt_info
== nullptr) {
372 return Status::NotFound("Could not find option: ", name
);
374 return ConfigureOption(config_options
, configurable
, *opt_info
, name
,
375 opt_name
, value
, opt_ptr
);
379 Status
ConfigurableHelper::ConfigureOption(
380 const ConfigOptions
& config_options
, Configurable
& configurable
,
381 const OptionTypeInfo
& opt_info
, const std::string
& opt_name
,
382 const std::string
& name
, const std::string
& value
, void* opt_ptr
) {
383 if (opt_name
== name
) {
384 return configurable
.ParseOption(config_options
, opt_info
, opt_name
, value
,
386 } else if (opt_info
.IsCustomizable() &&
387 EndsWith(opt_name
, ConfigurableHelper::kIdPropSuffix
)) {
388 return configurable
.ParseOption(config_options
, opt_info
, name
, value
,
391 } else if (opt_info
.IsCustomizable()) {
392 Customizable
* custom
= opt_info
.AsRawPointer
<Customizable
>(opt_ptr
);
395 } else if (custom
== nullptr || !StartsWith(name
, custom
->GetId() + ".")) {
396 return configurable
.ParseOption(config_options
, opt_info
, name
, value
,
398 } else if (value
.find("=") != std::string::npos
) {
399 return custom
->ConfigureFromString(config_options
, value
);
401 return custom
->ConfigureOption(config_options
, name
, value
);
403 } else if (opt_info
.IsStruct() || opt_info
.IsConfigurable()) {
404 return configurable
.ParseOption(config_options
, opt_info
, name
, value
,
407 return Status::NotFound("Could not find option: ", name
);
410 #endif // ROCKSDB_LITE
412 Status
ConfigurableHelper::ConfigureNewObject(
413 const ConfigOptions
& config_options_in
, Configurable
* object
,
414 const std::string
& id
, const std::string
& base_opts
,
415 const std::unordered_map
<std::string
, std::string
>& opts
) {
416 if (object
!= nullptr) {
417 ConfigOptions config_options
= config_options_in
;
418 config_options
.invoke_prepare_options
= false;
419 if (!base_opts
.empty()) {
421 // Don't run prepare options on the base, as we would do that on the
422 // overlay opts instead
423 Status status
= object
->ConfigureFromString(config_options
, base_opts
);
427 #endif // ROCKSDB_LITE
430 return object
->ConfigureFromMap(config_options
, opts
);
432 } else if (!opts
.empty()) { // No object but no map. This is OK
433 return Status::InvalidArgument("Cannot configure null object ", id
);
438 //*******************************************************************************
440 // Methods for Converting Options into strings
442 //*******************************************************************************
444 Status
Configurable::GetOptionString(const ConfigOptions
& config_options
,
445 std::string
* result
) const {
449 return ConfigurableHelper::SerializeOptions(config_options
, *this, "",
452 (void)config_options
;
453 return Status::NotSupported("GetOptionString not supported in LITE mode");
454 #endif // ROCKSDB_LITE
458 std::string
Configurable::ToString(const ConfigOptions
& config_options
,
459 const std::string
& prefix
) const {
460 std::string result
= SerializeOptions(config_options
, prefix
);
461 if (result
.empty() || result
.find('=') == std::string::npos
) {
464 return "{" + result
+ "}";
468 std::string
Configurable::SerializeOptions(const ConfigOptions
& config_options
,
469 const std::string
& header
) const {
471 Status s
= ConfigurableHelper::SerializeOptions(config_options
, *this, header
,
477 Status
Configurable::GetOption(const ConfigOptions
& config_options
,
478 const std::string
& name
,
479 std::string
* value
) const {
480 return ConfigurableHelper::GetOption(config_options
, *this,
481 GetOptionName(name
), value
);
484 Status
ConfigurableHelper::GetOption(const ConfigOptions
& config_options
,
485 const Configurable
& configurable
,
486 const std::string
& short_name
,
487 std::string
* value
) {
488 // Look for option directly
492 std::string opt_name
;
493 void* opt_ptr
= nullptr;
494 const auto opt_info
=
495 FindOption(configurable
.options_
, short_name
, &opt_name
, &opt_ptr
);
496 if (opt_info
!= nullptr) {
497 ConfigOptions embedded
= config_options
;
498 embedded
.delimiter
= ";";
499 if (short_name
== opt_name
) {
500 return opt_info
->Serialize(embedded
, opt_name
, opt_ptr
, value
);
501 } else if (opt_info
->IsStruct()) {
502 return opt_info
->Serialize(embedded
, opt_name
, opt_ptr
, value
);
503 } else if (opt_info
->IsConfigurable()) {
504 auto const* config
= opt_info
->AsRawPointer
<Configurable
>(opt_ptr
);
505 if (config
!= nullptr) {
506 return config
->GetOption(embedded
, opt_name
, value
);
510 return Status::NotFound("Cannot find option: ", short_name
);
513 Status
ConfigurableHelper::SerializeOptions(const ConfigOptions
& config_options
,
514 const Configurable
& configurable
,
515 const std::string
& prefix
,
516 std::string
* result
) {
518 for (auto const& opt_iter
: configurable
.options_
) {
519 for (const auto& map_iter
: *(opt_iter
.type_map
)) {
520 const auto& opt_name
= map_iter
.first
;
521 const auto& opt_info
= map_iter
.second
;
522 if (opt_info
.ShouldSerialize()) {
524 Status s
= opt_info
.Serialize(config_options
, prefix
+ opt_name
,
525 opt_iter
.opt_ptr
, &value
);
528 } else if (!value
.empty()) {
529 // <prefix><opt_name>=<value><delimiter>
530 result
->append(prefix
+ opt_name
+ "=" + value
+
531 config_options
.delimiter
);
538 #endif // ROCKSDB_LITE
540 //********************************************************************************
542 // Methods for listing the options from Configurables
544 //********************************************************************************
546 Status
Configurable::GetOptionNames(
547 const ConfigOptions
& config_options
,
548 std::unordered_set
<std::string
>* result
) const {
550 return ConfigurableHelper::ListOptions(config_options
, *this, "", result
);
553 Status
ConfigurableHelper::ListOptions(
554 const ConfigOptions
& /*config_options*/, const Configurable
& configurable
,
555 const std::string
& prefix
, std::unordered_set
<std::string
>* result
) {
557 for (auto const& opt_iter
: configurable
.options_
) {
558 for (const auto& map_iter
: *(opt_iter
.type_map
)) {
559 const auto& opt_name
= map_iter
.first
;
560 const auto& opt_info
= map_iter
.second
;
561 // If the option is no longer used in rocksdb and marked as deprecated,
562 // we skip it in the serialization.
563 if (!opt_info
.IsDeprecated() && !opt_info
.IsAlias()) {
564 result
->emplace(prefix
+ opt_name
);
570 #endif // ROCKSDB_LITE
572 //*******************************************************************************
574 // Methods for Comparing Configurables
576 //*******************************************************************************
578 bool Configurable::AreEquivalent(const ConfigOptions
& config_options
,
579 const Configurable
* other
,
580 std::string
* name
) const {
583 if (this == other
|| config_options
.IsCheckDisabled()) {
585 } else if (other
!= nullptr) {
587 return ConfigurableHelper::AreEquivalent(config_options
, *this, *other
,
591 #endif // ROCKSDB_LITE
598 bool Configurable::OptionsAreEqual(const ConfigOptions
& config_options
,
599 const OptionTypeInfo
& opt_info
,
600 const std::string
& opt_name
,
601 const void* const this_ptr
,
602 const void* const that_ptr
,
603 std::string
* mismatch
) const {
604 if (opt_info
.AreEqual(config_options
, opt_name
, this_ptr
, that_ptr
,
607 } else if (opt_info
.AreEqualByName(config_options
, opt_name
, this_ptr
,
616 bool ConfigurableHelper::AreEquivalent(const ConfigOptions
& config_options
,
617 const Configurable
& this_one
,
618 const Configurable
& that_one
,
619 std::string
* mismatch
) {
620 assert(mismatch
!= nullptr);
621 for (auto const& o
: this_one
.options_
) {
622 const auto this_offset
= this_one
.GetOptionsPtr(o
.name
);
623 const auto that_offset
= that_one
.GetOptionsPtr(o
.name
);
624 if (this_offset
!= that_offset
) {
625 if (this_offset
== nullptr || that_offset
== nullptr) {
628 for (const auto& map_iter
: *(o
.type_map
)) {
629 if (config_options
.IsCheckEnabled(map_iter
.second
.GetSanityLevel()) &&
630 !this_one
.OptionsAreEqual(config_options
, map_iter
.second
,
631 map_iter
.first
, this_offset
,
632 that_offset
, mismatch
)) {
641 #endif // ROCKSDB_LITE
643 Status
ConfigurableHelper::GetOptionsMap(
644 const std::string
& value
, std::string
* id
,
645 std::unordered_map
<std::string
, std::string
>* props
) {
646 return GetOptionsMap(value
, "", id
, props
);
649 Status
ConfigurableHelper::GetOptionsMap(
650 const std::string
& value
, const std::string
& default_id
, std::string
* id
,
651 std::unordered_map
<std::string
, std::string
>* props
) {
655 if (value
.empty() || value
== kNullptrString
) {
657 } else if (value
.find('=') == std::string::npos
) {
661 status
= StringToMap(value
, props
);
663 auto iter
= props
->find(ConfigurableHelper::kIdPropName
);
664 if (iter
!= props
->end()) {
667 } else if (default_id
.empty()) { // Should this be an error??
668 status
= Status::InvalidArgument("Name property is missing");
681 } // namespace ROCKSDB_NAMESPACE