Upstream changes
git-svn-id: https://leveldb.googlecode.com/svn/trunk@15 62dab493-f737-651d-591e-8d6aee1b9529
This commit is contained in:
parent
8303bb1b33
commit
e11bdf1935
1
TODO
1
TODO
@ -8,7 +8,6 @@ Maybe afterwards
|
||||
|
||||
ss
|
||||
- Stats
|
||||
- Speed up backwards scan (avoid three passes over data)
|
||||
|
||||
db
|
||||
- Maybe implement DB::BulkDeleteForRange(start_key, end_key)
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include "include/db.h"
|
||||
#include "include/env.h"
|
||||
#include "include/write_batch.h"
|
||||
#include "port/port.h"
|
||||
#include "util/crc32c.h"
|
||||
#include "util/histogram.h"
|
||||
#include "util/random.h"
|
||||
#include "util/testutil.h"
|
||||
@ -25,6 +27,8 @@
|
||||
// readseq -- read N values sequentially
|
||||
// readreverse -- read N values in reverse order
|
||||
// readrandom -- read N values in random order
|
||||
// crc32c -- repeated crc32c of 4K of data
|
||||
// sha1 -- repeated SHA1 computation over 4K of data
|
||||
// Meta operations:
|
||||
// compact -- Compact the entire DB
|
||||
// heapprofile -- Dump a heap profile (if supported by this port)
|
||||
@ -34,17 +38,21 @@
|
||||
// normal -- reset N back to its normal value (1000000)
|
||||
static const char* FLAGS_benchmarks =
|
||||
"fillseq,"
|
||||
"fillsync,"
|
||||
"fillrandom,"
|
||||
"overwrite,"
|
||||
"fillsync,"
|
||||
"readrandom,"
|
||||
"readrandom," // Extra run to allow previous compactions to quiesce
|
||||
"readseq,"
|
||||
"readreverse,"
|
||||
"readrandom,"
|
||||
"compact,"
|
||||
"readrandom,"
|
||||
"readseq,"
|
||||
"readreverse,"
|
||||
"readrandom,"
|
||||
"fill100K";
|
||||
"fill100K,"
|
||||
"crc32c,"
|
||||
"sha1"
|
||||
;
|
||||
|
||||
// Number of key/values to place in database
|
||||
static int FLAGS_num = 1000000;
|
||||
@ -330,6 +338,10 @@ class Benchmark {
|
||||
ReadRandom();
|
||||
} else if (name == Slice("compact")) {
|
||||
Compact();
|
||||
} else if (name == Slice("crc32c")) {
|
||||
Crc32c(4096, "(4K per op)");
|
||||
} else if (name == Slice("sha1")) {
|
||||
SHA1(4096, "(4K per op)");
|
||||
} else if (name == Slice("heapprofile")) {
|
||||
HeapProfile();
|
||||
} else {
|
||||
@ -340,6 +352,41 @@ class Benchmark {
|
||||
}
|
||||
|
||||
private:
|
||||
void Crc32c(int size, const char* label) {
|
||||
// Checksum about 500MB of data total
|
||||
string data(size, 'x');
|
||||
int64_t bytes = 0;
|
||||
uint32_t crc = 0;
|
||||
while (bytes < 500 * 1048576) {
|
||||
crc = crc32c::Value(data.data(), size);
|
||||
FinishedSingleOp();
|
||||
bytes += size;
|
||||
}
|
||||
// Print so result is not dead
|
||||
fprintf(stderr, "... crc=0x%x\r", static_cast<unsigned int>(crc));
|
||||
|
||||
bytes_ = bytes;
|
||||
message_ = label;
|
||||
}
|
||||
|
||||
void SHA1(int size, const char* label) {
|
||||
// SHA1 about 100MB of data total
|
||||
string data(size, 'x');
|
||||
int64_t bytes = 0;
|
||||
char sha1[20];
|
||||
while (bytes < 100 * 1048576) {
|
||||
port::SHA1_Hash(data.data(), size, sha1);
|
||||
FinishedSingleOp();
|
||||
bytes += size;
|
||||
}
|
||||
|
||||
// Print so result is not dead
|
||||
fprintf(stderr, "... sha1=%02x...\r", static_cast<unsigned int>(sha1[0]));
|
||||
|
||||
bytes_ = bytes;
|
||||
message_ = label;
|
||||
}
|
||||
|
||||
void Open() {
|
||||
assert(db_ == NULL);
|
||||
Options options;
|
||||
|
440
db/db_iter.cc
440
db/db_iter.cc
@ -36,6 +36,16 @@ namespace {
|
||||
// numbers, deletion markers, overwrites, etc.
|
||||
class DBIter: public Iterator {
|
||||
public:
|
||||
// Which direction is the iterator currently moving?
|
||||
// (1) When moving forward, the internal iterator is positioned at
|
||||
// the exact entry that yields this->key(), this->value()
|
||||
// (2) When moving backwards, the internal iterator is positioned
|
||||
// just before all entries whose user key == this->key().
|
||||
enum Direction {
|
||||
kForward,
|
||||
kReverse
|
||||
};
|
||||
|
||||
DBIter(const std::string* dbname, Env* env,
|
||||
const Comparator* cmp, Iterator* iter, SequenceNumber s)
|
||||
: dbname_(dbname),
|
||||
@ -44,6 +54,7 @@ class DBIter: public Iterator {
|
||||
iter_(iter),
|
||||
sequence_(s),
|
||||
large_(NULL),
|
||||
direction_(kForward),
|
||||
valid_(false) {
|
||||
}
|
||||
virtual ~DBIter() {
|
||||
@ -53,48 +64,21 @@ class DBIter: public Iterator {
|
||||
virtual bool Valid() const { return valid_; }
|
||||
virtual Slice key() const {
|
||||
assert(valid_);
|
||||
return key_;
|
||||
return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_;
|
||||
}
|
||||
virtual Slice value() const {
|
||||
assert(valid_);
|
||||
Slice raw_value = (direction_ == kForward) ? iter_->value() : saved_value_;
|
||||
if (large_ == NULL) {
|
||||
return value_;
|
||||
return raw_value;
|
||||
} else {
|
||||
MutexLock l(&large_->mutex);
|
||||
if (!large_->produced) {
|
||||
ReadIndirectValue();
|
||||
ReadIndirectValue(raw_value);
|
||||
}
|
||||
return large_->value;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Next() {
|
||||
assert(valid_);
|
||||
// iter_ is already positioned past DBIter::key()
|
||||
FindNextUserEntry();
|
||||
}
|
||||
|
||||
virtual void Prev() {
|
||||
assert(valid_);
|
||||
bool ignored;
|
||||
ScanUntilBeforeCurrentKey(&ignored);
|
||||
FindPrevUserEntry();
|
||||
}
|
||||
|
||||
virtual void Seek(const Slice& target) {
|
||||
ParsedInternalKey ikey(target, sequence_, kValueTypeForSeek);
|
||||
std::string tmp;
|
||||
AppendInternalKey(&tmp, ikey);
|
||||
iter_->Seek(tmp);
|
||||
FindNextUserEntry();
|
||||
}
|
||||
virtual void SeekToFirst() {
|
||||
iter_->SeekToFirst();
|
||||
FindNextUserEntry();
|
||||
}
|
||||
|
||||
virtual void SeekToLast();
|
||||
|
||||
virtual Status status() const {
|
||||
if (status_.ok()) {
|
||||
if (large_ != NULL && !large_->status.ok()) return large_->status;
|
||||
@ -104,23 +88,13 @@ class DBIter: public Iterator {
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Next();
|
||||
virtual void Prev();
|
||||
virtual void Seek(const Slice& target);
|
||||
virtual void SeekToFirst();
|
||||
virtual void SeekToLast();
|
||||
|
||||
private:
|
||||
void FindNextUserEntry();
|
||||
void FindPrevUserEntry();
|
||||
void SaveKey(const Slice& k) { key_.assign(k.data(), k.size()); }
|
||||
void SaveValue(const Slice& v) {
|
||||
if (value_.capacity() > v.size() + 1048576) {
|
||||
std::string empty;
|
||||
swap(empty, value_);
|
||||
}
|
||||
value_.assign(v.data(), v.size());
|
||||
}
|
||||
bool ParseKey(ParsedInternalKey* key);
|
||||
void SkipPast(const Slice& k);
|
||||
void ScanUntilBeforeCurrentKey(bool* found_live);
|
||||
|
||||
void ReadIndirectValue() const;
|
||||
|
||||
struct Large {
|
||||
port::Mutex mutex;
|
||||
std::string value;
|
||||
@ -128,19 +102,42 @@ class DBIter: public Iterator {
|
||||
Status status;
|
||||
};
|
||||
|
||||
void FindNextUserEntry(bool skipping, std::string* skip);
|
||||
void FindPrevUserEntry();
|
||||
bool ParseKey(ParsedInternalKey* key);
|
||||
void ReadIndirectValue(Slice ref) const;
|
||||
|
||||
inline void SaveKey(const Slice& k, std::string* dst) {
|
||||
dst->assign(k.data(), k.size());
|
||||
}
|
||||
|
||||
inline void ForgetLargeValue() {
|
||||
if (large_ != NULL) {
|
||||
delete large_;
|
||||
large_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline void ClearSavedValue() {
|
||||
if (saved_value_.capacity() > 1048576) {
|
||||
std::string empty;
|
||||
swap(empty, saved_value_);
|
||||
} else {
|
||||
saved_value_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
const std::string* const dbname_;
|
||||
Env* const env_;
|
||||
|
||||
const Comparator* const user_comparator_;
|
||||
|
||||
// iter_ is positioned just past current entry for DBIter if valid_
|
||||
Iterator* const iter_;
|
||||
|
||||
SequenceNumber const sequence_;
|
||||
|
||||
Status status_;
|
||||
std::string key_; // Always a user key
|
||||
std::string value_;
|
||||
Large* large_; // Non-NULL if value is an indirect reference
|
||||
std::string saved_key_; // == current key when direction_==kReverse
|
||||
std::string saved_value_; // == current raw value when direction_==kReverse
|
||||
Large* large_; // Non-NULL if value is an indirect reference
|
||||
Direction direction_;
|
||||
bool valid_;
|
||||
|
||||
// No copying allowed
|
||||
@ -157,204 +154,189 @@ inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::FindNextUserEntry() {
|
||||
if (large_ != NULL) {
|
||||
if (status_.ok() && !large_->status.ok()) {
|
||||
status_ = large_->status;
|
||||
}
|
||||
delete large_;
|
||||
large_ = NULL;
|
||||
}
|
||||
while (iter_->Valid()) {
|
||||
ParsedInternalKey ikey;
|
||||
if (!ParseKey(&ikey)) {
|
||||
// Skip past corrupted entry
|
||||
void DBIter::Next() {
|
||||
assert(valid_);
|
||||
ForgetLargeValue();
|
||||
|
||||
if (direction_ == kReverse) { // Switch directions?
|
||||
direction_ = kForward;
|
||||
// iter_ is pointing just before the entries for this->key(),
|
||||
// so advance into the range of entries for this->key() and then
|
||||
// use the normal skipping code below.
|
||||
if (!iter_->Valid()) {
|
||||
iter_->SeekToFirst();
|
||||
} else {
|
||||
iter_->Next();
|
||||
continue;
|
||||
}
|
||||
if (ikey.sequence > sequence_) {
|
||||
// Ignore entries newer than the snapshot
|
||||
iter_->Next();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (ikey.type) {
|
||||
case kTypeDeletion:
|
||||
SaveKey(ikey.user_key); // Make local copy for use by SkipPast()
|
||||
iter_->Next();
|
||||
SkipPast(key_);
|
||||
// Do not return deleted entries. Instead keep looping.
|
||||
break;
|
||||
|
||||
case kTypeValue:
|
||||
SaveKey(ikey.user_key);
|
||||
SaveValue(iter_->value());
|
||||
iter_->Next();
|
||||
SkipPast(key_);
|
||||
// Yield the value we just found.
|
||||
valid_ = true;
|
||||
return;
|
||||
|
||||
case kTypeLargeValueRef:
|
||||
SaveKey(ikey.user_key);
|
||||
// Save the large value ref as value_, and read it lazily on a call
|
||||
// to value()
|
||||
SaveValue(iter_->value());
|
||||
large_ = new Large;
|
||||
large_->produced = false;
|
||||
iter_->Next();
|
||||
SkipPast(key_);
|
||||
// Yield the value we just found.
|
||||
valid_ = true;
|
||||
return;
|
||||
if (!iter_->Valid()) {
|
||||
valid_ = false;
|
||||
saved_key_.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
valid_ = false;
|
||||
key_.clear();
|
||||
value_.clear();
|
||||
assert(large_ == NULL);
|
||||
|
||||
// Temporarily use saved_key_ as storage for key to skip.
|
||||
std::string* skip = &saved_key_;
|
||||
SaveKey(ExtractUserKey(iter_->key()), skip);
|
||||
FindNextUserEntry(true, skip);
|
||||
}
|
||||
|
||||
void DBIter::SkipPast(const Slice& k) {
|
||||
while (iter_->Valid()) {
|
||||
void DBIter::FindNextUserEntry(bool skipping, std::string* skip) {
|
||||
// Loop until we hit an acceptable entry to yield
|
||||
assert(iter_->Valid());
|
||||
assert(direction_ == kForward);
|
||||
assert(large_ == NULL);
|
||||
do {
|
||||
ParsedInternalKey ikey;
|
||||
// Note that if we cannot parse an internal key, we keep looping
|
||||
// so that if we have a run like the following:
|
||||
// <x,100,v> => value100
|
||||
// <corrupted entry for user key x>
|
||||
// <x,50,v> => value50
|
||||
// we will skip over the corrupted entry as well as value50.
|
||||
if (ParseKey(&ikey) && user_comparator_->Compare(ikey.user_key, k) != 0) {
|
||||
break;
|
||||
if (ParseKey(&ikey) && ikey.sequence <= sequence_) {
|
||||
switch (ikey.type) {
|
||||
case kTypeDeletion:
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
SaveKey(ikey.user_key, skip);
|
||||
skipping = true;
|
||||
break;
|
||||
case kTypeValue:
|
||||
case kTypeLargeValueRef:
|
||||
if (skipping &&
|
||||
user_comparator_->Compare(ikey.user_key, *skip) <= 0) {
|
||||
// Entry hidden
|
||||
} else {
|
||||
valid_ = true;
|
||||
saved_key_.clear();
|
||||
if (ikey.type == kTypeLargeValueRef) {
|
||||
large_ = new Large;
|
||||
large_->produced = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
iter_->Next();
|
||||
} while (iter_->Valid());
|
||||
saved_key_.clear();
|
||||
valid_ = false;
|
||||
}
|
||||
|
||||
void DBIter::Prev() {
|
||||
assert(valid_);
|
||||
ForgetLargeValue();
|
||||
|
||||
if (direction_ == kForward) { // Switch directions?
|
||||
// iter_ is pointing at the current entry. Scan backwards until
|
||||
// the key changes so we can use the normal reverse scanning code.
|
||||
assert(iter_->Valid()); // Otherwise valid_ would have been false
|
||||
SaveKey(ExtractUserKey(iter_->key()), &saved_key_);
|
||||
while (true) {
|
||||
iter_->Prev();
|
||||
if (!iter_->Valid()) {
|
||||
valid_ = false;
|
||||
saved_key_.clear();
|
||||
ClearSavedValue();
|
||||
return;
|
||||
}
|
||||
if (user_comparator_->Compare(ExtractUserKey(iter_->key()),
|
||||
saved_key_) < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
direction_ = kReverse;
|
||||
}
|
||||
|
||||
FindPrevUserEntry();
|
||||
}
|
||||
|
||||
void DBIter::FindPrevUserEntry() {
|
||||
assert(direction_ == kReverse);
|
||||
assert(large_ == NULL);
|
||||
|
||||
ValueType value_type = kTypeDeletion;
|
||||
if (iter_->Valid()) {
|
||||
SaveKey(ExtractUserKey(iter_->key()), &saved_key_);
|
||||
do {
|
||||
ParsedInternalKey ikey;
|
||||
if (ParseKey(&ikey) && ikey.sequence <= sequence_) {
|
||||
if ((value_type != kTypeDeletion) &&
|
||||
user_comparator_->Compare(ikey.user_key, saved_key_) < 0) {
|
||||
// We encountered a non-deleted value in entries for previous keys,
|
||||
break;
|
||||
}
|
||||
value_type = ikey.type;
|
||||
if (value_type == kTypeDeletion) {
|
||||
ClearSavedValue();
|
||||
} else {
|
||||
Slice raw_value = iter_->value();
|
||||
if (saved_value_.capacity() > raw_value.size() + 1048576) {
|
||||
std::string empty;
|
||||
swap(empty, saved_value_);
|
||||
}
|
||||
saved_value_.assign(raw_value.data(), raw_value.size());
|
||||
}
|
||||
}
|
||||
iter_->Prev();
|
||||
} while (iter_->Valid());
|
||||
}
|
||||
|
||||
if (value_type == kTypeDeletion) {
|
||||
// End
|
||||
valid_ = false;
|
||||
saved_key_.clear();
|
||||
ClearSavedValue();
|
||||
direction_ = kForward;
|
||||
} else {
|
||||
valid_ = true;
|
||||
if (value_type == kTypeLargeValueRef) {
|
||||
large_ = new Large;
|
||||
large_->produced = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::Seek(const Slice& target) {
|
||||
direction_ = kForward;
|
||||
ForgetLargeValue();
|
||||
ClearSavedValue();
|
||||
saved_key_.clear();
|
||||
AppendInternalKey(
|
||||
&saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek));
|
||||
iter_->Seek(saved_key_);
|
||||
if (iter_->Valid()) {
|
||||
FindNextUserEntry(false, &saved_key_ /* temporary storage */);
|
||||
} else {
|
||||
valid_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::SeekToFirst() {
|
||||
direction_ = kForward;
|
||||
ForgetLargeValue();
|
||||
ClearSavedValue();
|
||||
iter_->SeekToFirst();
|
||||
if (iter_->Valid()) {
|
||||
FindNextUserEntry(false, &saved_key_ /* temporary storage */);
|
||||
} else {
|
||||
valid_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::SeekToLast() {
|
||||
// Position iter_ at the last uncorrupted user key and then
|
||||
// let FindPrevUserEntry() do the heavy lifting to find
|
||||
// a user key that is live.
|
||||
direction_ = kReverse;
|
||||
ForgetLargeValue();
|
||||
ClearSavedValue();
|
||||
iter_->SeekToLast();
|
||||
ParsedInternalKey current;
|
||||
while (iter_->Valid() && !ParseKey(¤t)) {
|
||||
iter_->Prev();
|
||||
}
|
||||
if (iter_->Valid()) {
|
||||
SaveKey(current.user_key);
|
||||
}
|
||||
FindPrevUserEntry();
|
||||
}
|
||||
|
||||
// Let X be the user key at which iter_ is currently positioned.
|
||||
// Adjust DBIter to point at the last entry with a key <= X that
|
||||
// has a live value.
|
||||
void DBIter::FindPrevUserEntry() {
|
||||
// Consider the following example:
|
||||
//
|
||||
// A@540
|
||||
// A@400
|
||||
//
|
||||
// B@300
|
||||
// B@200
|
||||
// B@100 <- iter_
|
||||
//
|
||||
// C@301
|
||||
// C@201
|
||||
//
|
||||
// The comments marked "(first iteration)" below relate what happens
|
||||
// for the preceding example in the first iteration of the while loop
|
||||
// below. There may be more than one iteration either if there are
|
||||
// no live values for B, or if there is a corruption.
|
||||
while (iter_->Valid()) {
|
||||
std::string saved = key_;
|
||||
bool found_live;
|
||||
ScanUntilBeforeCurrentKey(&found_live);
|
||||
// (first iteration) iter_ at A@400
|
||||
if (found_live) {
|
||||
// Step forward into range of entries with user key >= saved
|
||||
if (!iter_->Valid()) {
|
||||
iter_->SeekToFirst();
|
||||
} else {
|
||||
iter_->Next();
|
||||
}
|
||||
// (first iteration) iter_ at B@300
|
||||
|
||||
FindNextUserEntry(); // Sets key_ to the key of the next value it found
|
||||
if (valid_ && user_comparator_->Compare(key_, saved) == 0) {
|
||||
// (first iteration) iter_ at C@301
|
||||
return;
|
||||
}
|
||||
|
||||
// FindNextUserEntry() could not find any entries under the
|
||||
// user key "saved". This is probably a corruption since
|
||||
// ScanUntilBefore(saved) found a live value. So we skip
|
||||
// backwards to an earlier key and ignore the corrupted
|
||||
// entries for "saved".
|
||||
//
|
||||
// (first iteration) iter_ at C@301 and saved == "B"
|
||||
key_ = saved;
|
||||
bool ignored;
|
||||
ScanUntilBeforeCurrentKey(&ignored);
|
||||
// (first iteration) iter_ at A@400
|
||||
}
|
||||
}
|
||||
valid_ = false;
|
||||
key_.clear();
|
||||
value_.clear();
|
||||
}
|
||||
|
||||
void DBIter::ScanUntilBeforeCurrentKey(bool* found_live) {
|
||||
*found_live = false;
|
||||
if (!iter_->Valid()) {
|
||||
iter_->SeekToLast();
|
||||
}
|
||||
|
||||
while (iter_->Valid()) {
|
||||
ParsedInternalKey current;
|
||||
if (!ParseKey(¤t)) {
|
||||
iter_->Prev();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current.sequence > sequence_) {
|
||||
// Ignore entries that are serialized after this read
|
||||
iter_->Prev();
|
||||
continue;
|
||||
}
|
||||
|
||||
const int cmp = user_comparator_->Compare(current.user_key, key_);
|
||||
if (cmp < 0) {
|
||||
SaveKey(current.user_key);
|
||||
return;
|
||||
} else if (cmp == 0) {
|
||||
switch (current.type) {
|
||||
case kTypeDeletion:
|
||||
*found_live = false;
|
||||
break;
|
||||
|
||||
case kTypeValue:
|
||||
case kTypeLargeValueRef:
|
||||
*found_live = true;
|
||||
break;
|
||||
}
|
||||
} else { // cmp > 0
|
||||
*found_live = false;
|
||||
}
|
||||
|
||||
iter_->Prev();
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::ReadIndirectValue() const {
|
||||
void DBIter::ReadIndirectValue(Slice ref) const {
|
||||
assert(!large_->produced);
|
||||
large_->produced = true;
|
||||
LargeValueRef large_ref;
|
||||
if (value_.size() != LargeValueRef::ByteSize()) {
|
||||
if (ref.size() != LargeValueRef::ByteSize()) {
|
||||
large_->status = Status::Corruption("malformed large value reference");
|
||||
return;
|
||||
}
|
||||
memcpy(large_ref.data, value_.data(), LargeValueRef::ByteSize());
|
||||
memcpy(large_ref.data, ref.data(), LargeValueRef::ByteSize());
|
||||
std::string fname = LargeValueFileName(*dbname_, large_ref);
|
||||
RandomAccessFile* file;
|
||||
Status s = env_->NewRandomAccessFile(fname, &file);
|
||||
|
184
db/db_test.cc
184
db/db_test.cc
@ -209,6 +209,16 @@ class DBTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string IterStatus(Iterator* iter) {
|
||||
std::string result;
|
||||
if (iter->Valid()) {
|
||||
result = iter->key().ToString() + "->" + iter->value().ToString();
|
||||
} else {
|
||||
result = "(invalid)";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(DBTest, Empty) {
|
||||
@ -234,6 +244,180 @@ TEST(DBTest, PutDeleteGet) {
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
}
|
||||
|
||||
TEST(DBTest, IterEmpty) {
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->Seek("foo");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST(DBTest, IterSingle) {
|
||||
ASSERT_OK(Put("a", "va"));
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->Seek("");
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->Seek("a");
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->Seek("b");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST(DBTest, IterMulti) {
|
||||
ASSERT_OK(Put("a", "va"));
|
||||
ASSERT_OK(Put("b", "vb"));
|
||||
ASSERT_OK(Put("c", "vc"));
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->Seek("");
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Seek("a");
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Seek("ax");
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
iter->Seek("b");
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
iter->Seek("z");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
// Switch from reverse to forward
|
||||
iter->SeekToLast();
|
||||
iter->Prev();
|
||||
iter->Prev();
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
|
||||
// Switch from forward to reverse
|
||||
iter->SeekToFirst();
|
||||
iter->Next();
|
||||
iter->Next();
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
|
||||
// Make sure iter stays at snapshot
|
||||
ASSERT_OK(Put("a", "va2"));
|
||||
ASSERT_OK(Put("a2", "va3"));
|
||||
ASSERT_OK(Put("b", "vb2"));
|
||||
ASSERT_OK(Put("c", "vc2"));
|
||||
ASSERT_OK(Delete("b"));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST(DBTest, IterSmallAndLargeMix) {
|
||||
ASSERT_OK(Put("a", "va"));
|
||||
ASSERT_OK(Put("b", std::string(100000, 'b')));
|
||||
ASSERT_OK(Put("c", "vc"));
|
||||
ASSERT_OK(Put("d", std::string(100000, 'd')));
|
||||
ASSERT_OK(Put("e", std::string(100000, 'e')));
|
||||
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b'));
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd'));
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e'));
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e'));
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd'));
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b'));
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST(DBTest, Recover) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
ASSERT_OK(Put("baz", "v5"));
|
||||
|
@ -132,8 +132,7 @@ range <code>[start,limit)</code>:
|
||||
}
|
||||
</pre>
|
||||
You can also process entries in reverse order. (Caveat: reverse
|
||||
iteration is currently a factor of two or three slower than forward
|
||||
iteration.)
|
||||
iteration may be somewhat slower than forward iteration.)
|
||||
<p>
|
||||
<pre>
|
||||
for (it->SeekToLast(); it->Valid(); it->Prev()) {
|
||||
|
@ -31,22 +31,6 @@ TEST(SHA1, Simple) {
|
||||
TestSHA1(x.data(), x.size()));
|
||||
}
|
||||
|
||||
TEST(SHA1, Benchmark) {
|
||||
std::string data(1048576 * 100, 'x');
|
||||
double start = Env::Default()->NowMicros() * 1e-6;
|
||||
static const int kIters = 10;
|
||||
uint32_t sha1 = 0;
|
||||
for (int i = 0; i < kIters; i++) {
|
||||
char hash_val[20];
|
||||
SHA1_Hash(data.data(), data.size(), hash_val);
|
||||
sha1 |= hash_val[0];
|
||||
}
|
||||
double finish = Env::Default()->NowMicros() * 1e-6;
|
||||
double mb = (static_cast<long long int>(data.size()) * kIters) / 1048576.0;
|
||||
fprintf(stderr, "SHA1 %0.0f MB: %.3f secs; %.1f MB/s, dummy=0x%02x\n",
|
||||
mb, (finish - start), mb / (finish - start), sha1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,20 +64,6 @@ TEST(CRC, Mask) {
|
||||
ASSERT_EQ(crc, Unmask(Unmask(Mask(Mask(crc)))));
|
||||
}
|
||||
|
||||
TEST(CRC, Benchmark) {
|
||||
std::string data(1048576 * 100, 'x');
|
||||
double start = Env::Default()->NowMicros() * 1e-6;
|
||||
static const int kIters = 10;
|
||||
uint32_t crc = 0;
|
||||
for (int i = 0; i < kIters; i++) {
|
||||
crc |= Value(data.data(), data.size());
|
||||
}
|
||||
double finish = Env::Default()->NowMicros() * 1e-6;
|
||||
double mb = (static_cast<long long int>(data.size()) * kIters) / 1048576.0;
|
||||
fprintf(stderr, "CRC %0.0f MB: %.3f secs; %.1f MB/s, crc=0x%08x\n",
|
||||
mb, (finish - start), mb / (finish - start), crc);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user