Summary: Add some helper functions to make sure DB works well for non-default comparators. Add a test for SimpleSuffixReverseComparator. Test Plan: Run the new test Reviewers: ljin, rven, yhchiang, igor Reviewed By: igor Subscribers: leveldb, dhruba Differential Revision: https://reviews.facebook.net/D27831main
parent
17be187ff9
commit
86de2007b8
@ -0,0 +1,260 @@ |
|||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||||
|
// 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 <map> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "rocksdb/db.h" |
||||||
|
#include "rocksdb/env.h" |
||||||
|
#include "util/testharness.h" |
||||||
|
#include "util/testutil.h" |
||||||
|
#include "utilities/merge_operators.h" |
||||||
|
|
||||||
|
using std::unique_ptr; |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
namespace { |
||||||
|
|
||||||
|
static const Comparator* comparator; |
||||||
|
|
||||||
|
// A comparator for std::map, using comparator
|
||||||
|
struct MapComparator { |
||||||
|
bool operator()(const std::string& a, const std::string& b) const { |
||||||
|
return comparator->Compare(a, b) < 0; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
typedef std::map<std::string, std::string, MapComparator> KVMap; |
||||||
|
|
||||||
|
class KVIter : public Iterator { |
||||||
|
public: |
||||||
|
explicit KVIter(const KVMap* map) : map_(map), iter_(map_->end()) {} |
||||||
|
virtual bool Valid() const { return iter_ != map_->end(); } |
||||||
|
virtual void SeekToFirst() { iter_ = map_->begin(); } |
||||||
|
virtual void SeekToLast() { |
||||||
|
if (map_->empty()) { |
||||||
|
iter_ = map_->end(); |
||||||
|
} else { |
||||||
|
iter_ = map_->find(map_->rbegin()->first); |
||||||
|
} |
||||||
|
} |
||||||
|
virtual void Seek(const Slice& k) { iter_ = map_->lower_bound(k.ToString()); } |
||||||
|
virtual void Next() { ++iter_; } |
||||||
|
virtual void Prev() { |
||||||
|
if (iter_ == map_->begin()) { |
||||||
|
iter_ = map_->end(); |
||||||
|
return; |
||||||
|
} |
||||||
|
--iter_; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Slice key() const { return iter_->first; } |
||||||
|
virtual Slice value() const { return iter_->second; } |
||||||
|
virtual Status status() const { return Status::OK(); } |
||||||
|
|
||||||
|
private: |
||||||
|
const KVMap* const map_; |
||||||
|
KVMap::const_iterator iter_; |
||||||
|
}; |
||||||
|
|
||||||
|
void AssertItersEqual(Iterator* iter1, Iterator* iter2) { |
||||||
|
ASSERT_EQ(iter1->Valid(), iter2->Valid()); |
||||||
|
if (iter1->Valid()) { |
||||||
|
ASSERT_EQ(iter1->key().ToString(), iter2->key().ToString()); |
||||||
|
ASSERT_EQ(iter1->value().ToString(), iter2->value().ToString()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Measuring operations on DB (expect to be empty).
|
||||||
|
// source_strings are candidate keys
|
||||||
|
void DoRandomIteraratorTest(DB* db, std::vector<std::string> source_strings, |
||||||
|
Random* rnd, int num_writes, int num_iter_ops, |
||||||
|
int num_trigger_flush) { |
||||||
|
KVMap map; |
||||||
|
|
||||||
|
for (int i = 0; i < num_writes; i++) { |
||||||
|
if (num_trigger_flush > 0 && i != 0 && i % num_trigger_flush == 0) { |
||||||
|
db->Flush(FlushOptions()); |
||||||
|
} |
||||||
|
|
||||||
|
int type = rnd->Uniform(2); |
||||||
|
int index = rnd->Uniform(source_strings.size()); |
||||||
|
auto& key = source_strings[index]; |
||||||
|
switch (type) { |
||||||
|
case 0: |
||||||
|
// put
|
||||||
|
map[key] = key; |
||||||
|
ASSERT_OK(db->Put(WriteOptions(), key, key)); |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
// delete
|
||||||
|
if (map.find(key) != map.end()) { |
||||||
|
map.erase(key); |
||||||
|
} |
||||||
|
ASSERT_OK(db->Delete(WriteOptions(), key)); |
||||||
|
break; |
||||||
|
default: |
||||||
|
assert(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::unique_ptr<Iterator> iter(db->NewIterator(ReadOptions())); |
||||||
|
std::unique_ptr<Iterator> result_iter(new KVIter(&map)); |
||||||
|
|
||||||
|
bool is_valid = false; |
||||||
|
for (int i = 0; i < num_iter_ops; i++) { |
||||||
|
// Random walk and make sure iter and result_iter returns the
|
||||||
|
// same key and value
|
||||||
|
int type = rnd->Uniform(6); |
||||||
|
ASSERT_OK(iter->status()); |
||||||
|
switch (type) { |
||||||
|
case 0: |
||||||
|
// Seek to First
|
||||||
|
iter->SeekToFirst(); |
||||||
|
result_iter->SeekToFirst(); |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
// Seek to last
|
||||||
|
iter->SeekToLast(); |
||||||
|
result_iter->SeekToLast(); |
||||||
|
break; |
||||||
|
case 2: { |
||||||
|
// Seek to random key
|
||||||
|
auto key_idx = rnd->Uniform(source_strings.size()); |
||||||
|
auto key = source_strings[key_idx]; |
||||||
|
iter->Seek(key); |
||||||
|
result_iter->Seek(key); |
||||||
|
break; |
||||||
|
} |
||||||
|
case 3: |
||||||
|
// Next
|
||||||
|
if (is_valid) { |
||||||
|
iter->Next(); |
||||||
|
result_iter->Next(); |
||||||
|
} else { |
||||||
|
continue; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
// Prev
|
||||||
|
if (is_valid) { |
||||||
|
iter->Prev(); |
||||||
|
result_iter->Prev(); |
||||||
|
} else { |
||||||
|
continue; |
||||||
|
} |
||||||
|
break; |
||||||
|
default: { |
||||||
|
assert(type == 5); |
||||||
|
auto key_idx = rnd->Uniform(source_strings.size()); |
||||||
|
auto key = source_strings[key_idx]; |
||||||
|
std::string result; |
||||||
|
auto status = db->Get(ReadOptions(), key, &result); |
||||||
|
if (map.find(key) == map.end()) { |
||||||
|
ASSERT_TRUE(status.IsNotFound()); |
||||||
|
} else { |
||||||
|
ASSERT_EQ(map[key], result); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
AssertItersEqual(iter.get(), result_iter.get()); |
||||||
|
is_valid = iter->Valid(); |
||||||
|
} |
||||||
|
} |
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class ComparatorDBTest { |
||||||
|
private: |
||||||
|
std::string dbname_; |
||||||
|
Env* env_; |
||||||
|
DB* db_; |
||||||
|
Options last_options_; |
||||||
|
std::unique_ptr<const Comparator> comparator_guard; |
||||||
|
|
||||||
|
public: |
||||||
|
ComparatorDBTest() : env_(Env::Default()), db_(nullptr) { |
||||||
|
comparator = BytewiseComparator(); |
||||||
|
dbname_ = test::TmpDir() + "/comparator_db_test"; |
||||||
|
ASSERT_OK(DestroyDB(dbname_, last_options_)); |
||||||
|
} |
||||||
|
|
||||||
|
~ComparatorDBTest() { |
||||||
|
delete db_; |
||||||
|
ASSERT_OK(DestroyDB(dbname_, last_options_)); |
||||||
|
comparator = BytewiseComparator(); |
||||||
|
} |
||||||
|
|
||||||
|
DB* GetDB() { return db_; } |
||||||
|
|
||||||
|
void SetOwnedComparator(const Comparator* cmp) { |
||||||
|
comparator_guard.reset(cmp); |
||||||
|
comparator = cmp; |
||||||
|
last_options_.comparator = cmp; |
||||||
|
} |
||||||
|
|
||||||
|
// Return the current option configuration.
|
||||||
|
Options* GetOptions() { return &last_options_; } |
||||||
|
|
||||||
|
void DestroyAndReopen() { |
||||||
|
// Destroy using last options
|
||||||
|
Destroy(); |
||||||
|
ASSERT_OK(TryReopen()); |
||||||
|
} |
||||||
|
|
||||||
|
void Destroy() { |
||||||
|
delete db_; |
||||||
|
db_ = nullptr; |
||||||
|
ASSERT_OK(DestroyDB(dbname_, last_options_)); |
||||||
|
} |
||||||
|
|
||||||
|
Status TryReopen() { |
||||||
|
delete db_; |
||||||
|
db_ = nullptr; |
||||||
|
last_options_.create_if_missing = true; |
||||||
|
|
||||||
|
return DB::Open(last_options_, dbname_, &db_); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
TEST(ComparatorDBTest, Bytewise) { |
||||||
|
for (int rand_seed = 301; rand_seed < 306; rand_seed++) { |
||||||
|
DestroyAndReopen(); |
||||||
|
Random rnd(rand_seed); |
||||||
|
DoRandomIteraratorTest(GetDB(), |
||||||
|
{"a", "b", "c", "d", "e", "f", "g", "h", "i"}, &rnd, |
||||||
|
8, 100, 3); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST(ComparatorDBTest, SimpleSuffixReverseComparator) { |
||||||
|
SetOwnedComparator(new test::SimpleSuffixReverseComparator()); |
||||||
|
|
||||||
|
for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) { |
||||||
|
Options* opt = GetOptions(); |
||||||
|
opt->comparator = comparator; |
||||||
|
DestroyAndReopen(); |
||||||
|
Random rnd(rnd_seed); |
||||||
|
|
||||||
|
std::vector<std::string> source_strings; |
||||||
|
std::vector<std::string> source_prefixes; |
||||||
|
// Randomly generate 5 prefixes
|
||||||
|
for (int i = 0; i < 5; i++) { |
||||||
|
source_prefixes.push_back(test::RandomHumanReadableString(&rnd, 8)); |
||||||
|
} |
||||||
|
for (int j = 0; j < 20; j++) { |
||||||
|
int prefix_index = rnd.Uniform(source_prefixes.size()); |
||||||
|
std::string key = source_prefixes[prefix_index] + |
||||||
|
test::RandomHumanReadableString(&rnd, rnd.Uniform(8)); |
||||||
|
source_strings.push_back(key); |
||||||
|
} |
||||||
|
|
||||||
|
DoRandomIteraratorTest(GetDB(), source_strings, &rnd, 30, 600, 66); |
||||||
|
} |
||||||
|
} |
||||||
|
} // namespace rocksdb
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); } |
Loading…
Reference in new issue