diff --git a/db/db_iter.cc b/db/db_iter.cc index 8ef9b3ce31..4e3c52c6ef 100644 --- a/db/db_iter.cc +++ b/db/db_iter.cc @@ -373,6 +373,7 @@ void DBIter::FindPrevUserEntry() { if (iter_->Valid()) { do { ParsedInternalKey ikey; + bool saved_key_cleared = false; if (ParseKey(&ikey) && ikey.sequence <= sequence_) { if ((value_type != kTypeDeletion) && user_comparator_->Compare(ikey.user_key, saved_key_) < 0) { @@ -383,6 +384,7 @@ void DBIter::FindPrevUserEntry() { if (value_type == kTypeDeletion) { saved_key_.clear(); ClearSavedValue(); + saved_key_cleared = true; } else { Slice raw_value = iter_->value(); if (saved_value_.capacity() > raw_value.size() + 1048576) { @@ -398,7 +400,7 @@ void DBIter::FindPrevUserEntry() { // found the prev user-key, then it is better to seek so that we can // avoid too many key comparisons. We seek to the first occurence of // our current key by looking for max sequence number. - if (num_skipped > max_skip_) { + if (!saved_key_cleared && num_skipped > max_skip_) { num_skipped = 0; std::string last_key; AppendInternalKey(&last_key, diff --git a/db/db_test.cc b/db/db_test.cc index 6695f5f3c5..074a902e9d 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -254,7 +254,8 @@ class DBTest { enum OptionSkip { kNoSkip = 0, kSkipDeletesFilterFirst = 1, - kSkipUniversalCompaction = 2 + kSkipUniversalCompaction = 2, + kSkipMergePut = 4 }; DBTest() : option_config_(kDefault), @@ -287,7 +288,9 @@ class DBTest { option_config_ == kUniversalCompaction) { option_config_++; } - + if (skip_mask & kSkipMergePut && option_config_ == kMergePut) { + option_config_++; + } if (option_config_ >= kEnd) { Destroy(&last_options_); return false; @@ -639,6 +642,13 @@ class DBTest { std::string DummyString(size_t len, char c = 'a') { return std::string(len, c); } + + void VerifyIterLast(std::string expected_key) { + Iterator* iter = db_->NewIterator(ReadOptions()); + iter->SeekToLast(); + ASSERT_EQ(IterStatus(iter), expected_key); + delete iter; + } }; TEST(DBTest, Empty) { @@ -1314,6 +1324,35 @@ TEST(DBTest, IterMultiWithDelete) { } while (ChangeOptions()); } +TEST(DBTest, IterPrevMaxSkip) { + do { + for (int i = 0; i < 2; i++) { + db_->Put(WriteOptions(), "key1", "v1"); + db_->Put(WriteOptions(), "key2", "v2"); + db_->Put(WriteOptions(), "key3", "v3"); + db_->Put(WriteOptions(), "key4", "v4"); + db_->Put(WriteOptions(), "key5", "v5"); + } + + VerifyIterLast("key5->v5"); + + ASSERT_OK(db_->Delete(WriteOptions(), "key5")); + VerifyIterLast("key4->v4"); + + ASSERT_OK(db_->Delete(WriteOptions(), "key4")); + VerifyIterLast("key3->v3"); + + ASSERT_OK(db_->Delete(WriteOptions(), "key3")); + VerifyIterLast("key2->v2"); + + ASSERT_OK(db_->Delete(WriteOptions(), "key2")); + VerifyIterLast("key1->v1"); + + ASSERT_OK(db_->Delete(WriteOptions(), "key1")); + VerifyIterLast("(invalid)"); + } while (ChangeOptions(kSkipMergePut)); +} + TEST(DBTest, IterWithSnapshot) { do { ASSERT_OK(Put("key1", "val1"));