-
Notifications
You must be signed in to change notification settings - Fork 6.4k
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
Make Comparator into a Customizable Object #8336
Changes from 3 commits
fd1ad05
286a415
ce7e30e
16e9958
743a57a
cdc40cf
2bda2c6
d930a29
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ | |
|
||
#include <string> | ||
|
||
#include "rocksdb/customizable.h" | ||
#include "rocksdb/rocksdb_namespace.h" | ||
|
||
namespace ROCKSDB_NAMESPACE { | ||
|
@@ -20,7 +21,7 @@ class Slice; | |
// used as keys in an sstable or a database. A Comparator implementation | ||
// must be thread-safe since rocksdb may invoke its methods concurrently | ||
// from multiple threads. | ||
class Comparator { | ||
class Comparator : public Customizable { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is a comparison of master and this branch for some operations from benchmark.sh (same performance tests as the performance Wiki): Master: Change: This change does not appear to have a noticeable performance impact. |
||
public: | ||
Comparator() : timestamp_size_(0) {} | ||
|
||
|
@@ -37,7 +38,17 @@ class Comparator { | |
|
||
virtual ~Comparator() {} | ||
|
||
static Status CreateFromString(const ConfigOptions& opts, | ||
const std::string& id, | ||
const Comparator** comp); | ||
static const char* Type() { return "Comparator"; } | ||
static const char* kBytewiseClassName() { | ||
return "leveldb.BytewiseComparator"; | ||
} | ||
static const char* kReverseBytewiseClassName() { | ||
return "rocksdb.ReverseBytewiseComparator"; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should not these be defined in derived class? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They are defined here so they can be used publicly. If they are in the derived class, then these constants cannot be used as strings/inputs to CreateFromString. Generally speaking, I have thought the names of the builtin Customizable objects should be public, but I can go either way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks odd to me that the derived class name is defined in base class. How about other compactors? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, the sampling is small (so far) as there are not that many Customizable classes. For TableFactory, the derived names (Block, Plain, Cuckoo) are in the base class. This is necessary because there is often casting that goes on based on the type and the derived classes are not in the public API. For Env, I could see the potential of needing the "default" or "posix" names in the base class. I am not certain that other names will have to be there. Default might be necessary so that one could cast an Env to the default or base implementation. If the name is not in the base class, then constructing a derived class can be more problematic. The options are:
I am not opposed to making it a "documentation" problem. I have also thought about ways of creating a tool that would allow a user to see what "instances" are available (via the ObjectRegistry) to discover what these names are and possibly make the features self-documenting. Since the names (for Comparators) are not really necessary in the public API at the moment, I can move them to the implementation classes if that is better. |
||
|
||
// Three-way comparison. Returns value: | ||
// < 0 iff "a" < "b", | ||
// == 0 iff "a" == "b", | ||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -8,20 +8,26 @@ | |||
// found in the LICENSE file. See the AUTHORS file for names of contributors. | ||||
|
||||
#include "rocksdb/comparator.h" | ||||
|
||||
#include <stdint.h> | ||||
|
||||
#include <algorithm> | ||||
#include <memory> | ||||
#include <mutex> | ||||
|
||||
#include "options/configurable_helper.h" | ||||
#include "port/port.h" | ||||
#include "rocksdb/slice.h" | ||||
#include "rocksdb/utilities/object_registry.h" | ||||
|
||||
namespace ROCKSDB_NAMESPACE { | ||||
|
||||
namespace { | ||||
class BytewiseComparatorImpl : public Comparator { | ||||
public: | ||||
BytewiseComparatorImpl() { } | ||||
|
||||
const char* Name() const override { return "leveldb.BytewiseComparator"; } | ||||
static const char* kClassName() { return kBytewiseClassName(); } | ||||
const char* Name() const override { return kClassName(); } | ||||
|
||||
int Compare(const Slice& a, const Slice& b) const override { | ||||
return a.compare(b); | ||||
|
@@ -139,9 +145,8 @@ class ReverseBytewiseComparatorImpl : public BytewiseComparatorImpl { | |||
public: | ||||
ReverseBytewiseComparatorImpl() { } | ||||
|
||||
const char* Name() const override { | ||||
return "rocksdb.ReverseBytewiseComparator"; | ||||
} | ||||
static const char* kClassName() { return kReverseBytewiseClassName(); } | ||||
const char* Name() const override { return kClassName(); } | ||||
|
||||
int Compare(const Slice& a, const Slice& b) const override { | ||||
return -a.compare(b); | ||||
|
@@ -220,4 +225,77 @@ const Comparator* ReverseBytewiseComparator() { | |||
return &rbytewise; | ||||
} | ||||
|
||||
#ifndef ROCKSDB_LITE | ||||
static int RegisterBuiltinComparators(ObjectLibrary& library, | ||||
const std::string& /*arg*/) { | ||||
library.Register<const Comparator>( | ||||
BytewiseComparatorImpl::kClassName(), | ||||
[](const std::string& /*uri*/, | ||||
std::unique_ptr<const Comparator>* /*guard */, | ||||
std::string* /* errmsg */) { return BytewiseComparator(); }); | ||||
library.Register<const Comparator>( | ||||
ReverseBytewiseComparatorImpl::kClassName(), | ||||
[](const std::string& /*uri*/, | ||||
std::unique_ptr<const Comparator>* /*guard */, | ||||
std::string* /* errmsg */) { return ReverseBytewiseComparator(); }); | ||||
return 2; | ||||
} | ||||
#endif // ROCKSDB_LITE | ||||
|
||||
Status Comparator::CreateFromString(const ConfigOptions& config_options, | ||||
const std::string& value, | ||||
const Comparator** result) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's counter intuitive to have the return result as rocksdb/include/rocksdb/options.h Line 113 in 2f1984d
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it is defined here as const because the option is const. This actually makes everything else slightly harder to do. If I make them non-const, it also will break (at least UBSAN) compatibility with any code that currently uses the ObjectRegistry to register their comparators. |
||||
#ifndef ROCKSDB_LITE | ||||
static std::once_flag once; | ||||
std::call_once(once, [&]() { | ||||
RegisterBuiltinComparators(*(ObjectLibrary::Default().get()), ""); | ||||
}); | ||||
#endif // ROCKSDB_LITE | ||||
std::string id; | ||||
std::unordered_map<std::string, std::string> opt_map; | ||||
Status status = | ||||
ConfigurableHelper::GetOptionsMap(value, *result, &id, &opt_map); | ||||
if (!status.ok()) { // GetOptionsMap failed | ||||
return status; | ||||
} | ||||
std::string curr_opts; | ||||
#ifndef ROCKSDB_LITE | ||||
if (*result != nullptr && (*result)->GetId() == id) { | ||||
// Try to get the existing options, ignoring any errors | ||||
ConfigOptions embedded = config_options; | ||||
embedded.delimiter = ";"; | ||||
(*result)->GetOptionString(embedded, &curr_opts).PermitUncheckedError(); | ||||
} | ||||
#endif | ||||
if (id == BytewiseComparatorImpl::kClassName()) { | ||||
*result = BytewiseComparator(); | ||||
} else if (id == ReverseBytewiseComparatorImpl::kClassName()) { | ||||
*result = ReverseBytewiseComparator(); | ||||
} else if (value.empty()) { | ||||
// No Id and no options. Clear the object | ||||
*result = nullptr; | ||||
return Status::OK(); | ||||
} else if (id.empty()) { // We have no Id but have options. Not good | ||||
return Status::NotSupported("Cannot reset object ", id); | ||||
} else { | ||||
#ifndef ROCKSDB_LITE | ||||
status = config_options.registry->NewStaticObject(id, result); | ||||
#else | ||||
status = Status::NotSupported("Cannot load object in LITE mode ", id); | ||||
#endif // ROCKSDB_LITE | ||||
if (!status.ok()) { | ||||
if (config_options.ignore_unsupported_options && | ||||
status.IsNotSupported()) { | ||||
return Status::OK(); | ||||
} else { | ||||
return status; | ||||
} | ||||
} else if (!curr_opts.empty() || !opt_map.empty()) { | ||||
Comparator* comparator = const_cast<Comparator*>(*result); | ||||
status = ConfigurableHelper::ConfigureNewObject( | ||||
config_options, comparator, id, curr_opts, opt_map); | ||||
} | ||||
} | ||||
return status; | ||||
} | ||||
} // namespace ROCKSDB_NAMESPACE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this is public interface, is there potential ABI breakage?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what the ABI breakage would be or how to test for it. Ideas?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine to break binary compatibility, especially for non patch release. I found a website which tracks our API binary compatibility: https://abi-laboratory.pro/?view=timeline&l=rocksdb