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
|
ss
|
||||||
- Stats
|
- Stats
|
||||||
- Speed up backwards scan (avoid three passes over data)
|
|
||||||
|
|
||||||
db
|
db
|
||||||
- Maybe implement DB::BulkDeleteForRange(start_key, end_key)
|
- Maybe implement DB::BulkDeleteForRange(start_key, end_key)
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#include "include/db.h"
|
#include "include/db.h"
|
||||||
#include "include/env.h"
|
#include "include/env.h"
|
||||||
#include "include/write_batch.h"
|
#include "include/write_batch.h"
|
||||||
|
#include "port/port.h"
|
||||||
|
#include "util/crc32c.h"
|
||||||
#include "util/histogram.h"
|
#include "util/histogram.h"
|
||||||
#include "util/random.h"
|
#include "util/random.h"
|
||||||
#include "util/testutil.h"
|
#include "util/testutil.h"
|
||||||
@ -25,6 +27,8 @@
|
|||||||
// readseq -- read N values sequentially
|
// readseq -- read N values sequentially
|
||||||
// readreverse -- read N values in reverse order
|
// readreverse -- read N values in reverse order
|
||||||
// readrandom -- read N values in random 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:
|
// Meta operations:
|
||||||
// compact -- Compact the entire DB
|
// compact -- Compact the entire DB
|
||||||
// heapprofile -- Dump a heap profile (if supported by this port)
|
// heapprofile -- Dump a heap profile (if supported by this port)
|
||||||
@ -34,17 +38,21 @@
|
|||||||
// normal -- reset N back to its normal value (1000000)
|
// normal -- reset N back to its normal value (1000000)
|
||||||
static const char* FLAGS_benchmarks =
|
static const char* FLAGS_benchmarks =
|
||||||
"fillseq,"
|
"fillseq,"
|
||||||
|
"fillsync,"
|
||||||
"fillrandom,"
|
"fillrandom,"
|
||||||
"overwrite,"
|
"overwrite,"
|
||||||
"fillsync,"
|
"readrandom,"
|
||||||
|
"readrandom," // Extra run to allow previous compactions to quiesce
|
||||||
"readseq,"
|
"readseq,"
|
||||||
"readreverse,"
|
"readreverse,"
|
||||||
"readrandom,"
|
|
||||||
"compact,"
|
"compact,"
|
||||||
|
"readrandom,"
|
||||||
"readseq,"
|
"readseq,"
|
||||||
"readreverse,"
|
"readreverse,"
|
||||||
"readrandom,"
|
"fill100K,"
|
||||||
"fill100K";
|
"crc32c,"
|
||||||
|
"sha1"
|
||||||
|
;
|
||||||
|
|
||||||
// Number of key/values to place in database
|
// Number of key/values to place in database
|
||||||
static int FLAGS_num = 1000000;
|
static int FLAGS_num = 1000000;
|
||||||
@ -330,6 +338,10 @@ class Benchmark {
|
|||||||
ReadRandom();
|
ReadRandom();
|
||||||
} else if (name == Slice("compact")) {
|
} else if (name == Slice("compact")) {
|
||||||
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")) {
|
} else if (name == Slice("heapprofile")) {
|
||||||
HeapProfile();
|
HeapProfile();
|
||||||
} else {
|
} else {
|
||||||
@ -340,6 +352,41 @@ class Benchmark {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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() {
|
void Open() {
|
||||||
assert(db_ == NULL);
|
assert(db_ == NULL);
|
||||||
Options options;
|
Options options;
|
||||||
|
426
db/db_iter.cc
426
db/db_iter.cc
@ -36,6 +36,16 @@ namespace {
|
|||||||
// numbers, deletion markers, overwrites, etc.
|
// numbers, deletion markers, overwrites, etc.
|
||||||
class DBIter: public Iterator {
|
class DBIter: public Iterator {
|
||||||
public:
|
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,
|
DBIter(const std::string* dbname, Env* env,
|
||||||
const Comparator* cmp, Iterator* iter, SequenceNumber s)
|
const Comparator* cmp, Iterator* iter, SequenceNumber s)
|
||||||
: dbname_(dbname),
|
: dbname_(dbname),
|
||||||
@ -44,6 +54,7 @@ class DBIter: public Iterator {
|
|||||||
iter_(iter),
|
iter_(iter),
|
||||||
sequence_(s),
|
sequence_(s),
|
||||||
large_(NULL),
|
large_(NULL),
|
||||||
|
direction_(kForward),
|
||||||
valid_(false) {
|
valid_(false) {
|
||||||
}
|
}
|
||||||
virtual ~DBIter() {
|
virtual ~DBIter() {
|
||||||
@ -53,48 +64,21 @@ class DBIter: public Iterator {
|
|||||||
virtual bool Valid() const { return valid_; }
|
virtual bool Valid() const { return valid_; }
|
||||||
virtual Slice key() const {
|
virtual Slice key() const {
|
||||||
assert(valid_);
|
assert(valid_);
|
||||||
return key_;
|
return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_;
|
||||||
}
|
}
|
||||||
virtual Slice value() const {
|
virtual Slice value() const {
|
||||||
assert(valid_);
|
assert(valid_);
|
||||||
|
Slice raw_value = (direction_ == kForward) ? iter_->value() : saved_value_;
|
||||||
if (large_ == NULL) {
|
if (large_ == NULL) {
|
||||||
return value_;
|
return raw_value;
|
||||||
} else {
|
} else {
|
||||||
MutexLock l(&large_->mutex);
|
MutexLock l(&large_->mutex);
|
||||||
if (!large_->produced) {
|
if (!large_->produced) {
|
||||||
ReadIndirectValue();
|
ReadIndirectValue(raw_value);
|
||||||
}
|
}
|
||||||
return large_->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 {
|
virtual Status status() const {
|
||||||
if (status_.ok()) {
|
if (status_.ok()) {
|
||||||
if (large_ != NULL && !large_->status.ok()) return large_->status;
|
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:
|
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 {
|
struct Large {
|
||||||
port::Mutex mutex;
|
port::Mutex mutex;
|
||||||
std::string value;
|
std::string value;
|
||||||
@ -128,19 +102,42 @@ class DBIter: public Iterator {
|
|||||||
Status status;
|
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_;
|
const std::string* const dbname_;
|
||||||
Env* const env_;
|
Env* const env_;
|
||||||
|
|
||||||
const Comparator* const user_comparator_;
|
const Comparator* const user_comparator_;
|
||||||
|
|
||||||
// iter_ is positioned just past current entry for DBIter if valid_
|
|
||||||
Iterator* const iter_;
|
Iterator* const iter_;
|
||||||
|
|
||||||
SequenceNumber const sequence_;
|
SequenceNumber const sequence_;
|
||||||
|
|
||||||
Status status_;
|
Status status_;
|
||||||
std::string key_; // Always a user key
|
std::string saved_key_; // == current key when direction_==kReverse
|
||||||
std::string value_;
|
std::string saved_value_; // == current raw value when direction_==kReverse
|
||||||
Large* large_; // Non-NULL if value is an indirect reference
|
Large* large_; // Non-NULL if value is an indirect reference
|
||||||
|
Direction direction_;
|
||||||
bool valid_;
|
bool valid_;
|
||||||
|
|
||||||
// No copying allowed
|
// No copying allowed
|
||||||
@ -157,204 +154,189 @@ inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DBIter::FindNextUserEntry() {
|
void DBIter::Next() {
|
||||||
if (large_ != NULL) {
|
assert(valid_);
|
||||||
if (status_.ok() && !large_->status.ok()) {
|
ForgetLargeValue();
|
||||||
status_ = large_->status;
|
|
||||||
}
|
|
||||||
delete large_;
|
|
||||||
large_ = NULL;
|
|
||||||
}
|
|
||||||
while (iter_->Valid()) {
|
|
||||||
ParsedInternalKey ikey;
|
|
||||||
if (!ParseKey(&ikey)) {
|
|
||||||
// Skip past corrupted entry
|
|
||||||
iter_->Next();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ikey.sequence > sequence_) {
|
|
||||||
// Ignore entries newer than the snapshot
|
|
||||||
iter_->Next();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (ikey.type) {
|
if (direction_ == kReverse) { // Switch directions?
|
||||||
case kTypeDeletion:
|
direction_ = kForward;
|
||||||
SaveKey(ikey.user_key); // Make local copy for use by SkipPast()
|
// iter_ is pointing just before the entries for this->key(),
|
||||||
iter_->Next();
|
// so advance into the range of entries for this->key() and then
|
||||||
SkipPast(key_);
|
// use the normal skipping code below.
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
valid_ = false;
|
|
||||||
key_.clear();
|
|
||||||
value_.clear();
|
|
||||||
assert(large_ == NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DBIter::SkipPast(const Slice& k) {
|
|
||||||
while (iter_->Valid()) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
iter_->Next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
|
||||||
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()) {
|
if (!iter_->Valid()) {
|
||||||
iter_->SeekToFirst();
|
iter_->SeekToFirst();
|
||||||
} else {
|
} else {
|
||||||
iter_->Next();
|
iter_->Next();
|
||||||
}
|
}
|
||||||
// (first iteration) iter_ at B@300
|
if (!iter_->Valid()) {
|
||||||
|
valid_ = false;
|
||||||
FindNextUserEntry(); // Sets key_ to the key of the next value it found
|
saved_key_.clear();
|
||||||
if (valid_ && user_comparator_->Compare(key_, saved) == 0) {
|
|
||||||
// (first iteration) iter_ at C@301
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FindNextUserEntry() could not find any entries under the
|
// Temporarily use saved_key_ as storage for key to skip.
|
||||||
// user key "saved". This is probably a corruption since
|
std::string* skip = &saved_key_;
|
||||||
// ScanUntilBefore(saved) found a live value. So we skip
|
SaveKey(ExtractUserKey(iter_->key()), skip);
|
||||||
// backwards to an earlier key and ignore the corrupted
|
FindNextUserEntry(true, skip);
|
||||||
// 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) {
|
void DBIter::FindNextUserEntry(bool skipping, std::string* skip) {
|
||||||
*found_live = false;
|
// Loop until we hit an acceptable entry to yield
|
||||||
if (!iter_->Valid()) {
|
assert(iter_->Valid());
|
||||||
iter_->SeekToLast();
|
assert(direction_ == kForward);
|
||||||
}
|
assert(large_ == NULL);
|
||||||
|
do {
|
||||||
while (iter_->Valid()) {
|
ParsedInternalKey ikey;
|
||||||
ParsedInternalKey current;
|
if (ParseKey(&ikey) && ikey.sequence <= sequence_) {
|
||||||
if (!ParseKey(¤t)) {
|
switch (ikey.type) {
|
||||||
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:
|
case kTypeDeletion:
|
||||||
*found_live = false;
|
// Arrange to skip all upcoming entries for this key since
|
||||||
|
// they are hidden by this deletion.
|
||||||
|
SaveKey(ikey.user_key, skip);
|
||||||
|
skipping = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case kTypeValue:
|
case kTypeValue:
|
||||||
case kTypeLargeValueRef:
|
case kTypeLargeValueRef:
|
||||||
*found_live = true;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
} else { // cmp > 0
|
}
|
||||||
*found_live = false;
|
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();
|
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::ReadIndirectValue() const {
|
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() {
|
||||||
|
direction_ = kReverse;
|
||||||
|
ForgetLargeValue();
|
||||||
|
ClearSavedValue();
|
||||||
|
iter_->SeekToLast();
|
||||||
|
FindPrevUserEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBIter::ReadIndirectValue(Slice ref) const {
|
||||||
assert(!large_->produced);
|
assert(!large_->produced);
|
||||||
large_->produced = true;
|
large_->produced = true;
|
||||||
LargeValueRef large_ref;
|
LargeValueRef large_ref;
|
||||||
if (value_.size() != LargeValueRef::ByteSize()) {
|
if (ref.size() != LargeValueRef::ByteSize()) {
|
||||||
large_->status = Status::Corruption("malformed large value reference");
|
large_->status = Status::Corruption("malformed large value reference");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memcpy(large_ref.data, value_.data(), LargeValueRef::ByteSize());
|
memcpy(large_ref.data, ref.data(), LargeValueRef::ByteSize());
|
||||||
std::string fname = LargeValueFileName(*dbname_, large_ref);
|
std::string fname = LargeValueFileName(*dbname_, large_ref);
|
||||||
RandomAccessFile* file;
|
RandomAccessFile* file;
|
||||||
Status s = env_->NewRandomAccessFile(fname, &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) {
|
TEST(DBTest, Empty) {
|
||||||
@ -234,6 +244,180 @@ TEST(DBTest, PutDeleteGet) {
|
|||||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
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) {
|
TEST(DBTest, Recover) {
|
||||||
ASSERT_OK(Put("foo", "v1"));
|
ASSERT_OK(Put("foo", "v1"));
|
||||||
ASSERT_OK(Put("baz", "v5"));
|
ASSERT_OK(Put("baz", "v5"));
|
||||||
|
@ -132,8 +132,7 @@ range <code>[start,limit)</code>:
|
|||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
You can also process entries in reverse order. (Caveat: reverse
|
You can also process entries in reverse order. (Caveat: reverse
|
||||||
iteration is currently a factor of two or three slower than forward
|
iteration may be somewhat slower than forward iteration.)
|
||||||
iteration.)
|
|
||||||
<p>
|
<p>
|
||||||
<pre>
|
<pre>
|
||||||
for (it->SeekToLast(); it->Valid(); it->Prev()) {
|
for (it->SeekToLast(); it->Valid(); it->Prev()) {
|
||||||
|
@ -31,22 +31,6 @@ TEST(SHA1, Simple) {
|
|||||||
TestSHA1(x.data(), x.size()));
|
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)))));
|
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