]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/options/configurable.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / rocksdb / options / configurable.cc
CommitLineData
20effc67
TL
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).
5
6#include "rocksdb/configurable.h"
7
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"
17
18namespace ROCKSDB_NAMESPACE {
19
20void 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;
24 opts.name = name;
25#ifndef ROCKSDB_LITE
26 opts.type_map = type_map;
27#else
28 (void)type_map;
29#endif // ROCKSDB_LITE
30 opts.opt_ptr = opt_ptr;
31 configurable.options_.emplace_back(opts);
32}
33
34//*************************************************************************
35//
36// Methods for Initializing and Validating Configurable Objects
37//
38//*************************************************************************
39
40Status Configurable::PrepareOptions(const ConfigOptions& opts) {
41 Status status = Status::OK();
42#ifndef ROCKSDB_LITE
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);
53 if (!status.ok()) {
54 return status;
55 }
56 }
57 }
58 }
59 }
60 }
61#else
62 (void)opts;
63#endif // ROCKSDB_LITE
64 if (status.ok()) {
65 prepared_ = true;
66 }
67 return status;
68}
69
70Status Configurable::ValidateOptions(const DBOptions& db_opts,
71 const ColumnFamilyOptions& cf_opts) const {
72 Status status;
73#ifndef ROCKSDB_LITE
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()) {
84 status =
85 Status::NotFound("Missing configurable object", map_iter.first);
86 }
87 if (!status.ok()) {
88 return status;
89 }
90 }
91 }
92 }
93 }
94#else
95 (void)db_opts;
96 (void)cf_opts;
97#endif // ROCKSDB_LITE
98 return status;
99}
100
101/*********************************************************************************/
102/* */
103/* Methods for Retrieving Options from Configurables */
104/* */
105/*********************************************************************************/
106
107const void* Configurable::GetOptionsPtr(const std::string& name) const {
108 for (auto o : options_) {
109 if (o.name == name) {
110 return o.opt_ptr;
111 }
112 }
113 return nullptr;
114}
115
116std::string Configurable::GetOptionName(const std::string& opt_name) const {
117 return opt_name;
118}
119
120#ifndef ROCKSDB_LITE
121const 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;
129 return opt_info;
130 }
131 }
132 return nullptr;
133}
134#endif // ROCKSDB_LITE
135
136//*************************************************************************
137//
138// Methods for Configuring Options from Strings/Name-Value Pairs/Maps
139//
140//*************************************************************************
141
142Status 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);
146 return s;
147}
148
149Status 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);
154}
155
156Status 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;
161#ifndef ROCKSDB_LITE
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();
165 }
166#endif // ROCKSDB_LITE
167 Status s = ConfigurableHelper::ConfigureOptions(config_options, *this,
168 opts_map, unused);
169 if (config_options.invoke_prepare_options && s.ok()) {
170 s = PrepareOptions(config_options);
171 }
172#ifndef ROCKSDB_LITE
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();
179 }
180#endif // ROCKSDB_LITE
181 return s;
182}
183
184Status Configurable::ParseStringOptions(const ConfigOptions& /*config_options*/,
185 const std::string& /*opts_str*/) {
186 return Status::OK();
187}
188
189Status Configurable::ConfigureFromString(const ConfigOptions& config_options,
190 const std::string& opts_str) {
191 Status s;
192 if (!opts_str.empty()) {
193#ifndef ROCKSDB_LITE
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);
198 if (s.ok()) {
199 s = ConfigureFromMap(config_options, opt_map, nullptr);
200 }
201 } else {
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);
206 }
207#ifndef ROCKSDB_LITE
208 }
209#endif // ROCKSDB_LITE
210 } else if (config_options.invoke_prepare_options) {
211 s = PrepareOptions(config_options);
212 } else {
213 s = Status::OK();
214 }
215 return s;
216}
217
218#ifndef ROCKSDB_LITE
219/**
220 * Sets the value of the named property to the input value, returning OK on
221 * succcess.
222 */
223Status 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,
228 opt_name, value);
229}
230
231/**
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.
236 */
237
238Status 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);
246 } else {
247 return opt_info.Parse(config_options, opt_name, opt_value, opt_ptr);
248 }
249}
250
251#endif // ROCKSDB_LITE
252
253Status 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()) {
260#ifndef ROCKSDB_LITE
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?
265 break;
266 } else if (!s.ok()) {
267 break;
268 }
269 }
270#else
271 (void)configurable;
272 if (!config_options.ignore_unknown_options) {
273 s = Status::NotSupported("ConfigureFromMap not supported in LITE mode");
274 }
275#endif // ROCKSDB_LITE
276 }
277 if (unused != nullptr && !remaining.empty()) {
278 unused->insert(remaining.begin(), remaining.end());
279 }
280 if (config_options.ignore_unknown_options) {
281 s = Status::OK();
282 } else if (s.ok() && unused == nullptr && !remaining.empty()) {
283 s = Status::NotFound("Could not find option: ", remaining.begin()->first);
284 }
285 return s;
286}
287
288#ifndef ROCKSDB_LITE
289/**
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.
293
294 * Returns:
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
298 is OFF
299 * - OK, if no option was invalid or not supported (or ignored)
300 */
301Status 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;
308 int found = 1;
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()) {
313 found = 0;
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
321 ++it;
322 } else {
323 Status s = ConfigureOption(config_options, configurable, *opt_info,
324 opt_name, elem_name, opt_value, opt_ptr);
325 if (s.IsNotFound()) {
326 ++it;
327 } else if (s.IsNotSupported()) {
328 notsup = s;
329 unsupported.insert(it->first);
330 ++it; // Skip it for now
331 } else {
332 found++;
333 it = options->erase(it);
334 if (!s.ok()) {
335 result = s;
336 }
337 }
338 }
339 } // End for all remaining options
340 } // End while found one or options remain
341
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()) {
346 options->erase(it);
347 }
348 }
349 if (config_options.ignore_unknown_options) {
350 if (!result.ok()) result.PermitUncheckedError();
351 if (!notsup.ok()) notsup.PermitUncheckedError();
352 return Status::OK();
353 } else if (!result.ok()) {
354 if (!notsup.ok()) notsup.PermitUncheckedError();
355 return result;
356 } else if (config_options.ignore_unsupported_options) {
357 if (!notsup.ok()) notsup.PermitUncheckedError();
358 return Status::OK();
359 } else {
360 return notsup;
361 }
362}
363
364Status 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);
373 } else {
374 return ConfigureOption(config_options, configurable, *opt_info, name,
375 opt_name, value, opt_ptr);
376 }
377}
378
379Status 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,
385 opt_ptr);
386 } else if (opt_info.IsCustomizable() &&
387 EndsWith(opt_name, ConfigurableHelper::kIdPropSuffix)) {
388 return configurable.ParseOption(config_options, opt_info, name, value,
389 opt_ptr);
390
391 } else if (opt_info.IsCustomizable()) {
392 Customizable* custom = opt_info.AsRawPointer<Customizable>(opt_ptr);
393 if (value.empty()) {
394 return Status::OK();
395 } else if (custom == nullptr || !StartsWith(name, custom->GetId() + ".")) {
396 return configurable.ParseOption(config_options, opt_info, name, value,
397 opt_ptr);
398 } else if (value.find("=") != std::string::npos) {
399 return custom->ConfigureFromString(config_options, value);
400 } else {
401 return custom->ConfigureOption(config_options, name, value);
402 }
403 } else if (opt_info.IsStruct() || opt_info.IsConfigurable()) {
404 return configurable.ParseOption(config_options, opt_info, name, value,
405 opt_ptr);
406 } else {
407 return Status::NotFound("Could not find option: ", name);
408 }
409}
410#endif // ROCKSDB_LITE
411
412Status 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()) {
420#ifndef ROCKSDB_LITE
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);
424 if (!status.ok()) {
425 return status;
426 }
427#endif // ROCKSDB_LITE
428 }
429 if (!opts.empty()) {
430 return object->ConfigureFromMap(config_options, opts);
431 }
432 } else if (!opts.empty()) { // No object but no map. This is OK
433 return Status::InvalidArgument("Cannot configure null object ", id);
434 }
435 return Status::OK();
436}
437
438//*******************************************************************************
439//
440// Methods for Converting Options into strings
441//
442//*******************************************************************************
443
444Status Configurable::GetOptionString(const ConfigOptions& config_options,
445 std::string* result) const {
446 assert(result);
447 result->clear();
448#ifndef ROCKSDB_LITE
449 return ConfigurableHelper::SerializeOptions(config_options, *this, "",
450 result);
451#else
452 (void)config_options;
453 return Status::NotSupported("GetOptionString not supported in LITE mode");
454#endif // ROCKSDB_LITE
455}
456
457#ifndef ROCKSDB_LITE
458std::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) {
462 return result;
463 } else {
464 return "{" + result + "}";
465 }
466}
467
468std::string Configurable::SerializeOptions(const ConfigOptions& config_options,
469 const std::string& header) const {
470 std::string result;
471 Status s = ConfigurableHelper::SerializeOptions(config_options, *this, header,
472 &result);
473 assert(s.ok());
474 return result;
475}
476
477Status 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);
482}
483
484Status 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
489 assert(value);
490 value->clear();
491
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);
507 }
508 }
509 }
510 return Status::NotFound("Cannot find option: ", short_name);
511}
512
513Status ConfigurableHelper::SerializeOptions(const ConfigOptions& config_options,
514 const Configurable& configurable,
515 const std::string& prefix,
516 std::string* result) {
517 assert(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()) {
523 std::string value;
524 Status s = opt_info.Serialize(config_options, prefix + opt_name,
525 opt_iter.opt_ptr, &value);
526 if (!s.ok()) {
527 return s;
528 } else if (!value.empty()) {
529 // <prefix><opt_name>=<value><delimiter>
530 result->append(prefix + opt_name + "=" + value +
531 config_options.delimiter);
532 }
533 }
534 }
535 }
536 return Status::OK();
537}
538#endif // ROCKSDB_LITE
539
540//********************************************************************************
541//
542// Methods for listing the options from Configurables
543//
544//********************************************************************************
545#ifndef ROCKSDB_LITE
546Status Configurable::GetOptionNames(
547 const ConfigOptions& config_options,
548 std::unordered_set<std::string>* result) const {
549 assert(result);
550 return ConfigurableHelper::ListOptions(config_options, *this, "", result);
551}
552
553Status ConfigurableHelper::ListOptions(
554 const ConfigOptions& /*config_options*/, const Configurable& configurable,
555 const std::string& prefix, std::unordered_set<std::string>* result) {
556 Status status;
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);
565 }
566 }
567 }
568 return status;
569}
570#endif // ROCKSDB_LITE
571
572//*******************************************************************************
573//
574// Methods for Comparing Configurables
575//
576//*******************************************************************************
577
578bool Configurable::AreEquivalent(const ConfigOptions& config_options,
579 const Configurable* other,
580 std::string* name) const {
581 assert(name);
582 name->clear();
583 if (this == other || config_options.IsCheckDisabled()) {
584 return true;
585 } else if (other != nullptr) {
586#ifndef ROCKSDB_LITE
587 return ConfigurableHelper::AreEquivalent(config_options, *this, *other,
588 name);
589#else
590 return true;
591#endif // ROCKSDB_LITE
592 } else {
593 return false;
594 }
595}
596
597#ifndef ROCKSDB_LITE
598bool 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,
605 mismatch)) {
606 return true;
607 } else if (opt_info.AreEqualByName(config_options, opt_name, this_ptr,
608 that_ptr)) {
609 *mismatch = "";
610 return true;
611 } else {
612 return false;
613 }
614}
615
616bool 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) {
626 return false;
627 } else {
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)) {
633 return false;
634 }
635 }
636 }
637 }
638 }
639 return true;
640}
641#endif // ROCKSDB_LITE
642
643Status 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);
647}
648
649Status ConfigurableHelper::GetOptionsMap(
650 const std::string& value, const std::string& default_id, std::string* id,
651 std::unordered_map<std::string, std::string>* props) {
652 assert(id);
653 assert(props);
654 Status status;
655 if (value.empty() || value == kNullptrString) {
656 *id = default_id;
657 } else if (value.find('=') == std::string::npos) {
658 *id = value;
659#ifndef ROCKSDB_LITE
660 } else {
661 status = StringToMap(value, props);
662 if (status.ok()) {
663 auto iter = props->find(ConfigurableHelper::kIdPropName);
664 if (iter != props->end()) {
665 *id = iter->second;
666 props->erase(iter);
667 } else if (default_id.empty()) { // Should this be an error??
668 status = Status::InvalidArgument("Name property is missing");
669 } else {
670 *id = default_id;
671 }
672 }
673#else
674 } else {
675 *id = value;
676 props->clear();
677#endif
678 }
679 return status;
680}
681} // namespace ROCKSDB_NAMESPACE