#include "leveldb/merge_operator.h"
#include "utilities/merge_operators.h"
#include "utilities/merge_operators/string_append/stringappend.h"
+#include "utilities/merge_operators/string_append/stringappend2.h"
+#include "utilities/ttl/db_ttl.h"
#include "util/testharness.h"
#include "util/random.h"
const std::string kDbName = "/tmp/mergetestdb";
// OpenDb opens a (possibly new) rocksdb database with a StringAppendOperator
-std::shared_ptr<DB> OpenDb(StringAppendOperator* append_op) {
- DB* db;
- Options options;
- options.create_if_missing = true;
- options.merge_operator = append_op;
- Status s = DB::Open(options, kDbName, &db);
- if (!s.ok()) {
- std::cerr << s.ToString() << std::endl;
- assert(false);
- }
- return std::shared_ptr<DB>(db);
+std::shared_ptr<DB> OpenNormalDb(char delim_char) {
+ DB* db;
+ Options options;
+ options.create_if_missing = true;
+ options.merge_operator.reset(new StringAppendOperator(delim_char));
+ ASSERT_OK(DB::Open(options, kDbName, &db));
+ return std::shared_ptr<DB>(db);
+}
+
+// Open a TtlDB with a non-associative StringAppendTESTOperator
+std::shared_ptr<DB> OpenTtlDb(char delim_char) {
+ StackableDB* db;
+ Options options;
+ options.create_if_missing = true;
+ options.merge_operator.reset(new StringAppendTESTOperator(delim_char));
+ Status s;
+ db = new DBWithTTL(123456, options, kDbName, s, false);
+ ASSERT_OK(s);
+ return std::shared_ptr<DB>(db);
}
/// StringLists represents a set of string-lists, each with a key-index.
-/// Supports Append(list,string) and Get(list)
+/// Supports Append(list, string) and Get(list)
class StringLists {
public:
// Append string val onto the list defined by key; return true on success
bool Append(const std::string& key, const std::string& val){
- Slice valSlice(val.data(),val.size());
- auto s = db_->Merge(merge_option_,key,valSlice);
+ Slice valSlice(val.data(), val.size());
+ auto s = db_->Merge(merge_option_, key, valSlice);
if (s.ok()) {
return true;
// Returns the list of strings associated with key (or "" if does not exist)
bool Get(const std::string& key, std::string* const result){
assert(result != NULL); // we should have a place to store the result
-
auto s = db_->Get(get_option_, key, result);
if (s.ok()) {
return false;
}
+
private:
std::shared_ptr<DB> db_;
WriteOptions merge_option_;
};
+
+// The class for unit-testing
+class StringAppendOperatorTest {
+ public:
+ StringAppendOperatorTest() {
+ DestroyDB(kDbName, Options()); // Start each test with a fresh DB
+ }
+
+ typedef std::shared_ptr<DB> (* OpenFuncPtr)(char);
+
+ // Allows user to open databases with different configurations.
+ // e.g.: Can open a DB or a TtlDB, etc.
+ static void SetOpenDbFunction(OpenFuncPtr func) {
+ OpenDb = func;
+ }
+
+ protected:
+ static OpenFuncPtr OpenDb;
+};
+StringAppendOperatorTest::OpenFuncPtr StringAppendOperatorTest::OpenDb = nullptr;
+
// THE TEST CASES BEGIN HERE
-class StringAppendOperatorTest { };
TEST(StringAppendOperatorTest, IteratorTest) {
- DestroyDB(kDbName, Options()); // Start this test with a fresh DB
-
- StringAppendOperator append_op(',');
- auto db_ = OpenDb(&append_op);
+ auto db_ = OpenDb(',');
StringLists slists(db_);
- slists.Append("k1","v1");
- slists.Append("k1","v2");
- slists.Append("k1","v3");
+ slists.Append("k1", "v1");
+ slists.Append("k1", "v2");
+ slists.Append("k1", "v3");
- slists.Append("k2","a1");
- slists.Append("k2","a2");
- slists.Append("k2","a3");
+ slists.Append("k2", "a1");
+ slists.Append("k2", "a2");
+ slists.Append("k2", "a3");
std::string res;
std::unique_ptr<leveldb::Iterator> it(db_->NewIterator(ReadOptions()));
}
}
- slists.Append("k3","g1");
+ slists.Append("k3", "g1");
it.reset(db_->NewIterator(ReadOptions()));
first = true;
}
-TEST(StringAppendOperatorTest,SimpleTest) {
- DestroyDB(kDbName, Options()); // Start this test with a fresh DB
-
- StringAppendOperator append_op(',');
- auto db = OpenDb(&append_op);
+TEST(StringAppendOperatorTest, SimpleTest) {
+ auto db = OpenDb(',');
StringLists slists(db);
- slists.Append("k1","v1");
- slists.Append("k1","v2");
- slists.Append("k1","v3");
+ slists.Append("k1", "v1");
+ slists.Append("k1", "v2");
+ slists.Append("k1", "v3");
std::string res;
- bool status = slists.Get("k1",&res);
+ bool status = slists.Get("k1", &res);
ASSERT_TRUE(status);
- ASSERT_EQ(res,"v1,v2,v3");
+ ASSERT_EQ(res, "v1,v2,v3");
}
-TEST(StringAppendOperatorTest,SimpleDelimiterTest) {
- DestroyDB(kDbName, Options()); // Start this test with a fresh DB
-
- StringAppendOperator append_op('|');
- auto db = OpenDb(&append_op);
+TEST(StringAppendOperatorTest, SimpleDelimiterTest) {
+ auto db = OpenDb('|');
StringLists slists(db);
- slists.Append("k1","v1");
- slists.Append("k1","v2");
- slists.Append("k1","v3");
+ slists.Append("k1", "v1");
+ slists.Append("k1", "v2");
+ slists.Append("k1", "v3");
std::string res;
- slists.Get("k1",&res);
- ASSERT_EQ(res,"v1|v2|v3");
+ slists.Get("k1", &res);
+ ASSERT_EQ(res, "v1|v2|v3");
}
-TEST(StringAppendOperatorTest,OneValueNoDelimiterTest) {
- DestroyDB(kDbName, Options()); // Start this test with a fresh DB
-
- StringAppendOperator append_op('!');
- auto db = OpenDb(&append_op);
+TEST(StringAppendOperatorTest, OneValueNoDelimiterTest) {
+ auto db = OpenDb('!');
StringLists slists(db);
- slists.Append("random_key","single_val");
+ slists.Append("random_key", "single_val");
std::string res;
- slists.Get("random_key",&res);
- ASSERT_EQ(res,"single_val");
+ slists.Get("random_key", &res);
+ ASSERT_EQ(res, "single_val");
}
-TEST(StringAppendOperatorTest,VariousKeys) {
- DestroyDB(kDbName, Options()); // Start this test with a fresh DB
-
- StringAppendOperator append_op('\n');
- auto db = OpenDb(&append_op);
+TEST(StringAppendOperatorTest, VariousKeys) {
+ auto db = OpenDb('\n');
StringLists slists(db);
- slists.Append("c","asdasd");
- slists.Append("a","x");
- slists.Append("b","y");
- slists.Append("a","t");
- slists.Append("a","r");
- slists.Append("b","2");
- slists.Append("c","asdasd");
+ slists.Append("c", "asdasd");
+ slists.Append("a", "x");
+ slists.Append("b", "y");
+ slists.Append("a", "t");
+ slists.Append("a", "r");
+ slists.Append("b", "2");
+ slists.Append("c", "asdasd");
- std::string a,b,c;
- bool sa,sb,sc;
- sa = slists.Get("a",&a);
- sb = slists.Get("b",&b);
- sc = slists.Get("c",&c);
+ std::string a, b, c;
+ bool sa, sb, sc;
+ sa = slists.Get("a", &a);
+ sb = slists.Get("b", &b);
+ sc = slists.Get("c", &c);
ASSERT_TRUE(sa && sb && sc); // All three keys should have been found
- ASSERT_EQ(a,"x\nt\nr");
- ASSERT_EQ(b,"y\n2");
- ASSERT_EQ(c,"asdasd\nasdasd");
+ ASSERT_EQ(a, "x\nt\nr");
+ ASSERT_EQ(b, "y\n2");
+ ASSERT_EQ(c, "asdasd\nasdasd");
}
// Generate semi random keys/words from a small distribution.
-TEST(StringAppendOperatorTest,RandomMixGetAppend) {
- DestroyDB(kDbName, Options()); // Start this test with a fresh DB
-
- StringAppendOperator append_op(' ');
- auto db = OpenDb(&append_op);
+TEST(StringAppendOperatorTest, RandomMixGetAppend) {
+ auto db = OpenDb(' ');
StringLists slists(db);
// Generate a list of random keys and values
const int kWordCount = 15;
- std::string words[] = {"sdasd","triejf","fnjsdfn","dfjisdfsf","342839",
- "dsuha","mabuais","sadajsid","jf9834hf","2d9j89",
- "dj9823jd","a","dk02ed2dh","$(jd4h984$(*", "mabz"};
+ std::string words[] = {"sdasd", "triejf", "fnjsdfn", "dfjisdfsf", "342839",
+ "dsuha", "mabuais", "sadajsid", "jf9834hf", "2d9j89",
+ "dj9823jd", "a", "dk02ed2dh", "$(jd4h984$(*", "mabz"};
const int kKeyCount = 6;
- std::string keys[] = {"dhaiusdhu","denidw","daisda","keykey","muki",
+ std::string keys[] = {"dhaiusdhu", "denidw", "daisda", "keykey", "muki",
"shzassdianmd"};
// Will store a local copy of all data in order to verify correctness
- std::map<std::string,std::string> parallel_copy;
+ std::map<std::string, std::string> parallel_copy;
// Generate a bunch of random queries (Append and Get)!
enum query_t { APPEND_OP, GET_OP, NUM_OPS };
std::string key = keys[randomGen.Uniform((int)kKeyCount)];
std::string word = words[randomGen.Uniform((int)kWordCount)];
- // Debug message.
- //std::cout << (int)query << " " << key << " " << word << std::endl;
-
// Apply the query and any checks.
if (query == APPEND_OP) {
// Apply the rocksdb test-harness Append defined above
- slists.Append(key,word); //apply the rocksdb append
+ slists.Append(key, word); //apply the rocksdb append
// Apply the similar "Append" to the parallel copy
if (parallel_copy[key].size() > 0) {
} else if (query == GET_OP) {
// Assumes that a non-existent key just returns <empty>
std::string res;
- slists.Get(key,&res);
- ASSERT_EQ(res,parallel_copy[key]);
+ slists.Get(key, &res);
+ ASSERT_EQ(res, parallel_copy[key]);
}
}
}
-TEST(StringAppendOperatorTest,BIGRandomMixGetAppend) {
- DestroyDB(kDbName, Options()); // Start this test with a fresh DB
-
- StringAppendOperator append_op(' ');
- auto db = OpenDb(&append_op);
+TEST(StringAppendOperatorTest, BIGRandomMixGetAppend) {
+ auto db = OpenDb(' ');
StringLists slists(db);
// Generate a list of random keys and values
const int kWordCount = 15;
- std::string words[] = {"sdasd","triejf","fnjsdfn","dfjisdfsf","342839",
- "dsuha","mabuais","sadajsid","jf9834hf","2d9j89",
- "dj9823jd","a","dk02ed2dh","$(jd4h984$(*", "mabz"};
+ std::string words[] = {"sdasd", "triejf", "fnjsdfn", "dfjisdfsf", "342839",
+ "dsuha", "mabuais", "sadajsid", "jf9834hf", "2d9j89",
+ "dj9823jd", "a", "dk02ed2dh", "$(jd4h984$(*", "mabz"};
const int kKeyCount = 6;
- std::string keys[] = {"dhaiusdhu","denidw","daisda","keykey","muki",
+ std::string keys[] = {"dhaiusdhu", "denidw", "daisda", "keykey", "muki",
"shzassdianmd"};
// Will store a local copy of all data in order to verify correctness
- std::map<std::string,std::string> parallel_copy;
+ std::map<std::string, std::string> parallel_copy;
// Generate a bunch of random queries (Append and Get)!
enum query_t { APPEND_OP, GET_OP, NUM_OPS };
std::string key = keys[randomGen.Uniform((int)kKeyCount)];
std::string word = words[randomGen.Uniform((int)kWordCount)];
- // Debug message.
- //std::cout << (int)query << " " << key << " " << word << std::endl;
-
//Apply the query and any checks.
if (query == APPEND_OP) {
// Apply the rocksdb test-harness Append defined above
- slists.Append(key,word); //apply the rocksdb append
+ slists.Append(key, word); //apply the rocksdb append
// Apply the similar "Append" to the parallel copy
if (parallel_copy[key].size() > 0) {
} else if (query == GET_OP) {
// Assumes that a non-existent key just returns <empty>
std::string res;
- slists.Get(key,&res);
- ASSERT_EQ(res,parallel_copy[key]);
+ slists.Get(key, &res);
+ ASSERT_EQ(res, parallel_copy[key]);
}
}
}
-TEST(StringAppendOperatorTest,PersistentVariousKeys) {
- DestroyDB(kDbName, Options()); // Start this test with a fresh DB
-
+TEST(StringAppendOperatorTest, PersistentVariousKeys) {
// Perform the following operations in limited scope
{
- StringAppendOperator append_op('\n');
- auto db = OpenDb(&append_op);
+ auto db = OpenDb('\n');
StringLists slists(db);
- slists.Append("c","asdasd");
- slists.Append("a","x");
- slists.Append("b","y");
- slists.Append("a","t");
- slists.Append("a","r");
- slists.Append("b","2");
- slists.Append("c","asdasd");
-
- std::string a,b,c;
- slists.Get("a",&a);
- slists.Get("b",&b);
- slists.Get("c",&c);
-
- ASSERT_EQ(a,"x\nt\nr");
- ASSERT_EQ(b,"y\n2");
- ASSERT_EQ(c,"asdasd\nasdasd");
+ slists.Append("c", "asdasd");
+ slists.Append("a", "x");
+ slists.Append("b", "y");
+ slists.Append("a", "t");
+ slists.Append("a", "r");
+ slists.Append("b", "2");
+ slists.Append("c", "asdasd");
+
+ std::string a, b, c;
+ slists.Get("a", &a);
+ slists.Get("b", &b);
+ slists.Get("c", &c);
+
+ ASSERT_EQ(a, "x\nt\nr");
+ ASSERT_EQ(b, "y\n2");
+ ASSERT_EQ(c, "asdasd\nasdasd");
}
// Reopen the database (the previous changes should persist / be remembered)
{
- StringAppendOperator append_op('\n');
- auto db = OpenDb(&append_op);
+ auto db = OpenDb('\n');
StringLists slists(db);
- slists.Append("c","bbnagnagsx");
- slists.Append("a","sa");
- slists.Append("b","df");
- slists.Append("a","gh");
- slists.Append("a","jk");
- slists.Append("b","l;");
- slists.Append("c","rogosh");
+ slists.Append("c", "bbnagnagsx");
+ slists.Append("a", "sa");
+ slists.Append("b", "df");
+ slists.Append("a", "gh");
+ slists.Append("a", "jk");
+ slists.Append("b", "l;");
+ slists.Append("c", "rogosh");
// The previous changes should be on disk (L0)
// The most recent changes should be in memory (MemTable)
// Hence, this will test both Get() paths.
- std::string a,b,c;
- slists.Get("a",&a);
- slists.Get("b",&b);
- slists.Get("c",&c);
-
- ASSERT_EQ(a,"x\nt\nr\nsa\ngh\njk");
- ASSERT_EQ(b,"y\n2\ndf\nl;");
- ASSERT_EQ(c,"asdasd\nasdasd\nbbnagnagsx\nrogosh");
+ std::string a, b, c;
+ slists.Get("a", &a);
+ slists.Get("b", &b);
+ slists.Get("c", &c);
+
+ ASSERT_EQ(a, "x\nt\nr\nsa\ngh\njk");
+ ASSERT_EQ(b, "y\n2\ndf\nl;");
+ ASSERT_EQ(c, "asdasd\nasdasd\nbbnagnagsx\nrogosh");
}
// Reopen the database (the previous changes should persist / be remembered)
{
- StringAppendOperator append_op('\n');
- auto db = OpenDb(&append_op);
+ auto db = OpenDb('\n');
StringLists slists(db);
// All changes should be on disk. This will test VersionSet Get()
- std::string a,b,c;
- slists.Get("a",&a);
- slists.Get("b",&b);
- slists.Get("c",&c);
-
- ASSERT_EQ(a,"x\nt\nr\nsa\ngh\njk");
- ASSERT_EQ(b,"y\n2\ndf\nl;");
- ASSERT_EQ(c,"asdasd\nasdasd\nbbnagnagsx\nrogosh");
+ std::string a, b, c;
+ slists.Get("a", &a);
+ slists.Get("b", &b);
+ slists.Get("c", &c);
+
+ ASSERT_EQ(a, "x\nt\nr\nsa\ngh\njk");
+ ASSERT_EQ(b, "y\n2\ndf\nl;");
+ ASSERT_EQ(c, "asdasd\nasdasd\nbbnagnagsx\nrogosh");
}
}
-TEST(StringAppendOperatorTest,PersistentFlushAndCompaction) {
- DestroyDB(kDbName, Options()); // Start this test with a fresh DB
-
- StringAppendOperator append_op('\n');
-
+TEST(StringAppendOperatorTest, PersistentFlushAndCompaction) {
// Perform the following operations in limited scope
{
- auto db = OpenDb(&append_op);
+ auto db = OpenDb('\n');
StringLists slists(db);
- std::string a,b,c;
+ std::string a, b, c;
bool success;
// Append, Flush, Get
- slists.Append("c","asdasd");
+ slists.Append("c", "asdasd");
db->Flush(leveldb::FlushOptions());
- success = slists.Get("c",&c);
+ success = slists.Get("c", &c);
ASSERT_TRUE(success);
- ASSERT_EQ(c,"asdasd");
+ ASSERT_EQ(c, "asdasd");
// Append, Flush, Append, Get
- slists.Append("a","x");
- slists.Append("b","y");
+ slists.Append("a", "x");
+ slists.Append("b", "y");
db->Flush(leveldb::FlushOptions());
- slists.Append("a","t");
- slists.Append("a","r");
- slists.Append("b","2");
+ slists.Append("a", "t");
+ slists.Append("a", "r");
+ slists.Append("b", "2");
- success = slists.Get("a",&a);
+ success = slists.Get("a", &a);
assert(success == true);
- ASSERT_EQ(a,"x\nt\nr");
+ ASSERT_EQ(a, "x\nt\nr");
- success = slists.Get("b",&b);
+ success = slists.Get("b", &b);
assert(success == true);
- ASSERT_EQ(b,"y\n2");
+ ASSERT_EQ(b, "y\n2");
// Append, Get
- success = slists.Append("c","asdasd");
+ success = slists.Append("c", "asdasd");
assert(success);
- success = slists.Append("b","monkey");
+ success = slists.Append("b", "monkey");
assert(success);
// I omit the "assert(success)" checks here.
- slists.Get("a",&a);
- slists.Get("b",&b);
- slists.Get("c",&c);
+ slists.Get("a", &a);
+ slists.Get("b", &b);
+ slists.Get("c", &c);
- ASSERT_EQ(a,"x\nt\nr");
- ASSERT_EQ(b,"y\n2\nmonkey");
- ASSERT_EQ(c,"asdasd\nasdasd");
+ ASSERT_EQ(a, "x\nt\nr");
+ ASSERT_EQ(b, "y\n2\nmonkey");
+ ASSERT_EQ(c, "asdasd\nasdasd");
}
// Reopen the database (the previous changes should persist / be remembered)
{
- auto db = OpenDb(&append_op);
+ auto db = OpenDb('\n');
StringLists slists(db);
- std::string a,b,c;
+ std::string a, b, c;
// Get (Quick check for persistence of previous database)
- slists.Get("a",&a);
- ASSERT_EQ(a,"x\nt\nr");
+ slists.Get("a", &a);
+ ASSERT_EQ(a, "x\nt\nr");
//Append, Compact, Get
- slists.Append("c","bbnagnagsx");
- slists.Append("a","sa");
- slists.Append("b","df");
- db->CompactRange(nullptr,nullptr);
- slists.Get("a",&a);
- slists.Get("b",&b);
- slists.Get("c",&c);
- ASSERT_EQ(a,"x\nt\nr\nsa");
- ASSERT_EQ(b,"y\n2\nmonkey\ndf");
- ASSERT_EQ(c,"asdasd\nasdasd\nbbnagnagsx");
+ slists.Append("c", "bbnagnagsx");
+ slists.Append("a", "sa");
+ slists.Append("b", "df");
+ db->CompactRange(nullptr, nullptr);
+ slists.Get("a", &a);
+ slists.Get("b", &b);
+ slists.Get("c", &c);
+ ASSERT_EQ(a, "x\nt\nr\nsa");
+ ASSERT_EQ(b, "y\n2\nmonkey\ndf");
+ ASSERT_EQ(c, "asdasd\nasdasd\nbbnagnagsx");
// Append, Get
- slists.Append("a","gh");
- slists.Append("a","jk");
- slists.Append("b","l;");
- slists.Append("c","rogosh");
- slists.Get("a",&a);
- slists.Get("b",&b);
- slists.Get("c",&c);
- ASSERT_EQ(a,"x\nt\nr\nsa\ngh\njk");
- ASSERT_EQ(b,"y\n2\nmonkey\ndf\nl;");
- ASSERT_EQ(c,"asdasd\nasdasd\nbbnagnagsx\nrogosh");
+ slists.Append("a", "gh");
+ slists.Append("a", "jk");
+ slists.Append("b", "l;");
+ slists.Append("c", "rogosh");
+ slists.Get("a", &a);
+ slists.Get("b", &b);
+ slists.Get("c", &c);
+ ASSERT_EQ(a, "x\nt\nr\nsa\ngh\njk");
+ ASSERT_EQ(b, "y\n2\nmonkey\ndf\nl;");
+ ASSERT_EQ(c, "asdasd\nasdasd\nbbnagnagsx\nrogosh");
// Compact, Get
- db->CompactRange(nullptr,nullptr);
- ASSERT_EQ(a,"x\nt\nr\nsa\ngh\njk");
- ASSERT_EQ(b,"y\n2\nmonkey\ndf\nl;");
- ASSERT_EQ(c,"asdasd\nasdasd\nbbnagnagsx\nrogosh");
+ db->CompactRange(nullptr, nullptr);
+ ASSERT_EQ(a, "x\nt\nr\nsa\ngh\njk");
+ ASSERT_EQ(b, "y\n2\nmonkey\ndf\nl;");
+ ASSERT_EQ(c, "asdasd\nasdasd\nbbnagnagsx\nrogosh");
// Append, Flush, Compact, Get
- slists.Append("b","afcg");
+ slists.Append("b", "afcg");
db->Flush(leveldb::FlushOptions());
- db->CompactRange(nullptr,nullptr);
- slists.Get("b",&b);
- ASSERT_EQ(b,"y\n2\nmonkey\ndf\nl;\nafcg");
+ db->CompactRange(nullptr, nullptr);
+ slists.Get("b", &b);
+ ASSERT_EQ(b, "y\n2\nmonkey\ndf\nl;\nafcg");
}
}
-TEST(StringAppendOperatorTest,SimpleTestNullDelimiter) {
- DestroyDB(kDbName, Options()); // Start this test with a fresh DB
-
- StringAppendOperator append_op('\0');
- auto db = OpenDb(&append_op);
+TEST(StringAppendOperatorTest, SimpleTestNullDelimiter) {
+ auto db = OpenDb('\0');
StringLists slists(db);
- slists.Append("k1","v1");
- slists.Append("k1","v2");
- slists.Append("k1","v3");
+ slists.Append("k1", "v1");
+ slists.Append("k1", "v2");
+ slists.Append("k1", "v3");
std::string res;
- bool status = slists.Get("k1",&res);
+ bool status = slists.Get("k1", &res);
ASSERT_TRUE(status);
// Construct the desired string. Default constructor doesn't like '\0' chars.
// Check that the leveldb result string matches the desired string
assert(res.size() == checker.size());
- ASSERT_EQ(res,checker);
+ ASSERT_EQ(res, checker);
}
-
} // namespace leveldb
int main(int arc, char** argv) {
- leveldb::test::RunAllTests();
+ // Run with regular database
+ {
+ fprintf(stderr, "Running tests with regular db and operator.\n");
+ StringAppendOperatorTest::SetOpenDbFunction(&OpenNormalDb);
+ leveldb::test::RunAllTests();
+ }
+
+ // Run with TTL
+ {
+ fprintf(stderr, "Running tests with ttl db and generic operator.\n");
+ StringAppendOperatorTest::SetOpenDbFunction(&OpenTtlDb);
+ leveldb::test::RunAllTests();
+ }
+
return 0;
}