Merge operator for ttl

Summary: Implemented a TtlMergeOperator class which inherits from MergeOperator and is TTL aware. It strips out timestamp from existing_value and attaches timestamp to new_value, calling user-provided-Merge in between.

Test Plan: make all check

Reviewers: haobo, dhruba

Reviewed By: haobo

CC: leveldb

Differential Revision: https://reviews.facebook.net/D11775
This commit is contained in:
Mayank Agarwal 2013-07-22 16:49:55 -07:00
parent 59d0b02f8b
commit c42485f67c
3 changed files with 95 additions and 18 deletions

View file

@ -8,20 +8,29 @@
#include "leveldb/env.h"
#include "leveldb/merge_operator.h"
#include "db/dbformat.h"
#include "db/db_impl.h"
#include "utilities/merge_operators.h"
#include "util/testharness.h"
#include "utilities/utility_db.h"
using namespace std;
using namespace leveldb;
auto mergeOperator = MergeOperators::CreateUInt64AddOperator();
std::shared_ptr<DB> OpenDb() {
std::shared_ptr<DB> OpenDb(const string& dbname, const bool ttl = false) {
DB* db;
Options options;
options.create_if_missing = true;
options.merge_operator = mergeOperator.get();
Status s = DB::Open(options, test::TmpDir() + "/merge_testdb", &db);
Status s;
DestroyDB(dbname, Options());
if (ttl) {
cout << "Opening database with TTL\n";
s = UtilityDB::OpenTtlDB(options, test::TmpDir() + "/merge_testdbttl", &db);
} else {
s = DB::Open(options, test::TmpDir() + "/merge_testdb", &db);
}
if (!s.ok()) {
cerr << s.ToString() << endl;
assert(false);
@ -228,9 +237,8 @@ void testCounters(Counters& counters, DB* db, bool test_compaction) {
}
}
int main(int argc, char *argv[]) {
auto db = OpenDb();
void runTest(int argc, const string& dbname, const bool use_ttl = false) {
auto db = OpenDb(dbname, use_ttl);
{
cout << "Test read-modify-write counters... \n";
@ -250,5 +258,12 @@ int main(int argc, char *argv[]) {
testCounters(counters, db.get(), compact);
}
DestroyDB(dbname, Options());
}
int main(int argc, char *argv[]) {
//TODO: Make this test like a general rocksdb unit-test
runTest(argc, "/tmp/testdb");
runTest(argc, "/tmp/testdbttl", true); // Run test on TTL database
return 0;
}

View file

@ -21,6 +21,10 @@ DBWithTTL::DBWithTTL(const int32_t ttl,
assert(options.compaction_filter == nullptr);
Options options_to_open = options;
options_to_open.compaction_filter = this;
if (options.merge_operator) {
ttl_merge_op_.reset(new TtlMergeOperator(options.merge_operator));
options_to_open.merge_operator = ttl_merge_op_.get();
}
if (read_only) {
st = DB::OpenForReadOnly(options_to_open, dbname, &db_);
} else {
@ -125,15 +129,12 @@ Status DBWithTTL::StripTS(std::string* str) {
}
Status DBWithTTL::Put(
const WriteOptions& o,
const WriteOptions& opt,
const Slice& key,
const Slice& val) {
std::string value_with_ts;
Status st = AppendTS(val, value_with_ts);
if (!st.ok()) {
return st;
}
return db_->Put(o, key, value_with_ts);
WriteBatch batch;
batch.Put(key, val);
return Write(opt, &batch);
}
Status DBWithTTL::Get(const ReadOptions& options,
@ -169,10 +170,12 @@ Status DBWithTTL::Delete(const WriteOptions& wopts, const Slice& key) {
return db_->Delete(wopts, key);
}
Status DBWithTTL::Merge(const WriteOptions& options,
Status DBWithTTL::Merge(const WriteOptions& opt,
const Slice& key,
const Slice& value) {
return Status::NotSupported("Merge operation not supported.");
WriteBatch batch;
batch.Merge(key, value);
return Write(opt, &batch);
}
Status DBWithTTL::Write(const WriteOptions& opts, WriteBatch* updates) {
@ -190,8 +193,13 @@ Status DBWithTTL::Write(const WriteOptions& opts, WriteBatch* updates) {
}
}
virtual void Merge(const Slice& key, const Slice& value) {
// TTL doesn't support merge operation
batch_rewrite_status = Status::NotSupported("TTL doesn't support Merge");
std::string value_with_ts;
Status st = AppendTS(value, value_with_ts);
if (!st.ok()) {
batch_rewrite_status = st;
} else {
updates_ttl.Merge(key, value_with_ts);
}
}
virtual void Delete(const Slice& key) {
updates_ttl.Delete(key);

View file

@ -5,8 +5,10 @@
#ifndef LEVELDB_UTILITIES_TTL_DB_TTL_H_
#define LEVELDB_UTILITIES_TTL_DB_TTL_H_
#include "include/leveldb/db.h"
#include "include/leveldb/compaction_filter.h"
#include "leveldb/db.h"
#include "leveldb/env.h"
#include "leveldb/compaction_filter.h"
#include "leveldb/merge_operator.h"
#include "db/db_impl.h"
namespace leveldb {
@ -110,6 +112,7 @@ class DBWithTTL : public DB, CompactionFilter {
private:
DB* db_;
int32_t ttl_;
unique_ptr<MergeOperator> ttl_merge_op_;
};
class TtlIterator : public Iterator {
@ -173,5 +176,56 @@ class TtlIterator : public Iterator {
Iterator* iter_;
};
class TtlMergeOperator : public MergeOperator {
public:
explicit TtlMergeOperator(const MergeOperator* merge_op)
: user_merge_op_(merge_op) {
assert(merge_op);
}
virtual void Merge(const Slice& key,
const Slice* existing_value,
const Slice& value,
std::string* new_value,
Logger* logger) const {
const uint32_t& ts_len = DBWithTTL::kTSLength;
if ((existing_value && existing_value->size() < ts_len) ||
value.size() < ts_len) {
Log(logger, "Error: Could not remove timestamp correctly from value.");
assert(false);
//TODO: Remove assert and make this function return false.
//TODO: Change Merge semantics and add a counter here
}
Slice value_without_ts(value.data(), value.size() - ts_len);
if (existing_value) {
Slice existing_value_without_ts(existing_value->data(),
existing_value->size() - ts_len);
user_merge_op_->Merge(key, &existing_value_without_ts, value_without_ts,
new_value, logger);
} else {
user_merge_op_->Merge(key, nullptr, value_without_ts, new_value, logger);
}
int32_t curtime;
if (!DBWithTTL::GetCurrentTime(curtime).ok()) {
Log(logger, "Error: Could not get current time to be attached internally "
"to the new value.");
assert(false);
//TODO: Remove assert and make this function return false.
} else {
char ts_string[ts_len];
EncodeFixed32(ts_string, curtime);
new_value->append(ts_string, ts_len);
}
}
virtual const char* Name() const {
return "Merge By TTL";
}
private:
const MergeOperator* user_merge_op_;
};
}
#endif // LEVELDB_UTILITIES_TTL_DB_TTL_H_