mirror of
https://github.com/facebook/rocksdb.git
synced 2024-11-27 20:43:57 +00:00
c2d7826ced
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
66 lines
1.7 KiB
C++
66 lines
1.7 KiB
C++
#include <memory>
|
|
#include "leveldb/env.h"
|
|
#include "leveldb/merge_operator.h"
|
|
#include "leveldb/slice.h"
|
|
#include "util/coding.h"
|
|
#include "utilities/merge_operators.h"
|
|
|
|
using namespace leveldb;
|
|
|
|
namespace { // anonymous namespace
|
|
|
|
// A 'model' merge operator with uint64 addition semantics
|
|
// Implemented as an AssociativeMergeOperator for simplicty and example.
|
|
class UInt64AddOperator : public AssociativeMergeOperator {
|
|
public:
|
|
virtual bool Merge(const Slice& key,
|
|
const Slice* existing_value,
|
|
const Slice& value,
|
|
std::string* new_value,
|
|
Logger* logger) const override {
|
|
uint64_t orig_value = 0;
|
|
if (existing_value){
|
|
orig_value = DecodeInteger(*existing_value, logger);
|
|
}
|
|
uint64_t operand = DecodeInteger(value, logger);
|
|
|
|
assert(new_value);
|
|
new_value->clear();
|
|
PutFixed64(new_value, orig_value + operand);
|
|
|
|
return true; // Return true always since corruption will be treated as 0
|
|
}
|
|
|
|
virtual const char* Name() const override {
|
|
return "UInt64AddOperator";
|
|
}
|
|
|
|
private:
|
|
// Takes the string and decodes it into a uint64_t
|
|
// On error, prints a message and returns 0
|
|
uint64_t DecodeInteger(const Slice& value, Logger* logger) const {
|
|
uint64_t result = 0;
|
|
|
|
if (value.size() == sizeof(uint64_t)) {
|
|
result = DecodeFixed64(value.data());
|
|
} else if (logger != nullptr) {
|
|
// If value is corrupted, treat it as 0
|
|
Log(logger, "uint64 value corruption, size: %zu > %zu",
|
|
value.size(), sizeof(uint64_t));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
namespace leveldb {
|
|
|
|
std::shared_ptr<MergeOperator> MergeOperators::CreateUInt64AddOperator() {
|
|
return std::make_shared<UInt64AddOperator>();
|
|
}
|
|
|
|
}
|