|
|
|
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
|
|
|
// This source code is licensed under the BSD-style license found in the
|
|
|
|
// LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
// of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
#include "rocksdb/slice.h"
|
|
|
|
#include "rocksdb/merge_operator.h"
|
|
|
|
#include "utilities/merge_operators.h"
|
|
|
|
|
|
|
|
using namespace rocksdb;
|
|
|
|
|
|
|
|
namespace { // anonymous namespace
|
|
|
|
|
|
|
|
// A merge operator that mimics Put semantics
|
[RocksDB] [MergeOperator] The new Merge Interface! Uses merge sequences.
Summary:
Here are the major changes to the Merge Interface. It has been expanded
to handle cases where the MergeOperator is not associative. It does so by stacking
up merge operations while scanning through the key history (i.e.: during Get() or
Compaction), until a valid Put/Delete/end-of-history is encountered; it then
applies all of the merge operations in the correct sequence starting with the
base/sentinel value.
I have also introduced an "AssociativeMerge" function which allows the user to
take advantage of associative merge operations (such as in the case of counters).
The implementation will always attempt to merge the operations/operands themselves
together when they are encountered, and will resort to the "stacking" method if
and only if the "associative-merge" fails.
This implementation is conjectured to allow MergeOperator to handle the general
case, while still providing the user with the ability to take advantage of certain
efficiencies in their own merge-operator / data-structure.
NOTE: This is a preliminary diff. This must still go through a lot of review,
revision, and testing. Feedback welcome!
Test Plan:
-This is a preliminary diff. I have only just begun testing/debugging it.
-I will be testing this with the existing MergeOperator use-cases and unit-tests
(counters, string-append, and redis-lists)
-I will be "desk-checking" and walking through the code with the help gdb.
-I will find a way of stress-testing the new interface / implementation using
db_bench, db_test, merge_test, and/or db_stress.
-I will ensure that my tests cover all cases: Get-Memtable,
Get-Immutable-Memtable, Get-from-Disk, Iterator-Range-Scan, Flush-Memtable-to-L0,
Compaction-L0-L1, Compaction-Ln-L(n+1), Put/Delete found, Put/Delete not-found,
end-of-history, end-of-file, etc.
-A lot of feedback from the reviewers.
Reviewers: haobo, dhruba, zshao, emayanke
Reviewed By: haobo
CC: leveldb
Differential Revision: https://reviews.facebook.net/D11499
12 years ago
|
|
|
// Since this merge-operator will not be used in production,
|
|
|
|
// it is implemented as a non-associative merge operator to illustrate the
|
|
|
|
// new interface and for testing purposes. (That is, we inherit from
|
|
|
|
// the MergeOperator class rather than the AssociativeMergeOperator
|
|
|
|
// which would be simpler in this case).
|
|
|
|
//
|
|
|
|
// From the client-perspective, semantics are the same.
|
|
|
|
class PutOperator : public MergeOperator {
|
|
|
|
public:
|
|
|
|
virtual bool FullMerge(const Slice& key,
|
|
|
|
const Slice* existing_value,
|
|
|
|
const std::deque<std::string>& operand_sequence,
|
|
|
|
std::string* new_value,
|
|
|
|
Logger* logger) const override {
|
[RocksDB] [MergeOperator] The new Merge Interface! Uses merge sequences.
Summary:
Here are the major changes to the Merge Interface. It has been expanded
to handle cases where the MergeOperator is not associative. It does so by stacking
up merge operations while scanning through the key history (i.e.: during Get() or
Compaction), until a valid Put/Delete/end-of-history is encountered; it then
applies all of the merge operations in the correct sequence starting with the
base/sentinel value.
I have also introduced an "AssociativeMerge" function which allows the user to
take advantage of associative merge operations (such as in the case of counters).
The implementation will always attempt to merge the operations/operands themselves
together when they are encountered, and will resort to the "stacking" method if
and only if the "associative-merge" fails.
This implementation is conjectured to allow MergeOperator to handle the general
case, while still providing the user with the ability to take advantage of certain
efficiencies in their own merge-operator / data-structure.
NOTE: This is a preliminary diff. This must still go through a lot of review,
revision, and testing. Feedback welcome!
Test Plan:
-This is a preliminary diff. I have only just begun testing/debugging it.
-I will be testing this with the existing MergeOperator use-cases and unit-tests
(counters, string-append, and redis-lists)
-I will be "desk-checking" and walking through the code with the help gdb.
-I will find a way of stress-testing the new interface / implementation using
db_bench, db_test, merge_test, and/or db_stress.
-I will ensure that my tests cover all cases: Get-Memtable,
Get-Immutable-Memtable, Get-from-Disk, Iterator-Range-Scan, Flush-Memtable-to-L0,
Compaction-L0-L1, Compaction-Ln-L(n+1), Put/Delete found, Put/Delete not-found,
end-of-history, end-of-file, etc.
-A lot of feedback from the reviewers.
Reviewers: haobo, dhruba, zshao, emayanke
Reviewed By: haobo
CC: leveldb
Differential Revision: https://reviews.facebook.net/D11499
12 years ago
|
|
|
// Put basically only looks at the current/latest value
|
|
|
|
assert(!operand_sequence.empty());
|
|
|
|
assert(new_value != nullptr);
|
|
|
|
new_value->assign(operand_sequence.back());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool PartialMerge(const Slice& key,
|
|
|
|
const Slice& left_operand,
|
|
|
|
const Slice& right_operand,
|
|
|
|
std::string* new_value,
|
|
|
|
Logger* logger) const override {
|
|
|
|
new_value->assign(right_operand.data(), right_operand.size());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
using MergeOperator::PartialMergeMulti;
|
|
|
|
virtual bool PartialMergeMulti(const Slice& key,
|
|
|
|
const std::deque<Slice>& operand_list,
|
|
|
|
std::string* new_value, Logger* logger) const
|
|
|
|
override {
|
|
|
|
new_value->assign(operand_list.back().data(), operand_list.back().size());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual const char* Name() const override {
|
|
|
|
return "PutOperator";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // end of anonymous namespace
|
|
|
|
|
|
|
|
namespace rocksdb {
|
|
|
|
|
|
|
|
std::shared_ptr<MergeOperator> MergeOperators::CreatePutOperator() {
|
|
|
|
return std::make_shared<PutOperator>();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|