diff --git a/db/db_iter_test.cc b/db/db_iter_test.cc index e1f93db8500..686945293f1 100644 --- a/db/db_iter_test.cc +++ b/db/db_iter_test.cc @@ -36,7 +36,9 @@ class TestIterator : public InternalIterator { valid_(false), sequence_number_(0), iter_(0), - cmp(comparator) {} + cmp(comparator) { + data_.reserve(16); + } void AddPut(std::string argkey, std::string argvalue) { Add(argkey, kTypeValue, argvalue); @@ -2549,7 +2551,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIteratorDataRace1) { // MergeIterator::Prev() realized the mem table iterator is at its end // and before an SeekToLast() is called. rocksdb::SyncPoint::GetInstance()->SetCallBack( - "MergeIterator::Prev:BeforeSeekToLast", + "MergeIterator::Prev:BeforePrev", [&](void* arg) { internal_iter2_->Add("z", kTypeValue, "7", 12u); }); rocksdb::SyncPoint::GetInstance()->EnableProcessing(); @@ -2585,7 +2587,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIteratorDataRace2) { // mem table after MergeIterator::Prev() realized the mem tableiterator is at // its end and before an SeekToLast() is called. rocksdb::SyncPoint::GetInstance()->SetCallBack( - "MergeIterator::Prev:BeforeSeekToLast", [&](void* arg) { + "MergeIterator::Prev:BeforePrev", [&](void* arg) { internal_iter2_->Add("z", kTypeValue, "7", 12u); internal_iter2_->Add("z", kTypeValue, "7", 11u); }); @@ -2623,7 +2625,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIteratorDataRace3) { // mem table after MergeIterator::Prev() realized the mem table iterator is at // its end and before an SeekToLast() is called. rocksdb::SyncPoint::GetInstance()->SetCallBack( - "MergeIterator::Prev:BeforeSeekToLast", [&](void* arg) { + "MergeIterator::Prev:BeforePrev", [&](void* arg) { internal_iter2_->Add("z", kTypeValue, "7", 16u, true); internal_iter2_->Add("z", kTypeValue, "7", 15u, true); internal_iter2_->Add("z", kTypeValue, "7", 14u, true); diff --git a/db/db_iterator_test.cc b/db/db_iterator_test.cc index a0ecbb68160..9697361d41c 100644 --- a/db/db_iterator_test.cc +++ b/db/db_iterator_test.cc @@ -2043,6 +2043,43 @@ TEST_P(DBIteratorTest, CreationFailure) { delete iter; } +TEST_P(DBIteratorTest, UpperBoundWithChangeDirection) { + Options options = CurrentOptions(); + options.max_sequential_skip_in_iterations = 3; + DestroyAndReopen(options); + + // write a bunch of kvs to the database. + ASSERT_OK(Put("a", "1")); + ASSERT_OK(Put("y", "1")); + ASSERT_OK(Put("y1", "1")); + ASSERT_OK(Put("y2", "1")); + ASSERT_OK(Put("y3", "1")); + ASSERT_OK(Put("z", "1")); + ASSERT_OK(Flush()); + ASSERT_OK(Put("a", "1")); + ASSERT_OK(Put("z", "1")); + ASSERT_OK(Put("bar", "1")); + ASSERT_OK(Put("foo", "1")); + + std::string upper_bound = "x"; + Slice ub_slice(upper_bound); + ReadOptions ro; + ro.iterate_upper_bound = &ub_slice; + ro.max_skippable_internal_keys = 1000; + + Iterator* iter = NewIterator(ro); + iter->Seek("foo"); + ASSERT_TRUE(iter->Valid()); + ASSERT_EQ("foo", iter->key().ToString()); + + iter->Prev(); + ASSERT_TRUE(iter->Valid()); + ASSERT_OK(iter->status()); + ASSERT_EQ("bar", iter->key().ToString()); + + delete iter; +} + TEST_P(DBIteratorTest, TableFilter) { ASSERT_OK(Put("a", "1")); dbfull()->Flush(FlushOptions()); @@ -2109,6 +2146,47 @@ TEST_P(DBIteratorTest, TableFilter) { } } +TEST_P(DBIteratorTest, UpperBoundWithPrevReseek) { + Options options = CurrentOptions(); + options.max_sequential_skip_in_iterations = 3; + DestroyAndReopen(options); + + // write a bunch of kvs to the database. + ASSERT_OK(Put("a", "1")); + ASSERT_OK(Put("y", "1")); + ASSERT_OK(Put("z", "1")); + ASSERT_OK(Flush()); + ASSERT_OK(Put("a", "1")); + ASSERT_OK(Put("z", "1")); + ASSERT_OK(Put("bar", "1")); + ASSERT_OK(Put("foo", "1")); + ASSERT_OK(Put("foo", "2")); + + ASSERT_OK(Put("foo", "3")); + ASSERT_OK(Put("foo", "4")); + ASSERT_OK(Put("foo", "5")); + const Snapshot* snapshot = db_->GetSnapshot(); + ASSERT_OK(Put("foo", "6")); + + std::string upper_bound = "x"; + Slice ub_slice(upper_bound); + ReadOptions ro; + ro.snapshot = snapshot; + ro.iterate_upper_bound = &ub_slice; + + Iterator* iter = NewIterator(ro); + iter->SeekForPrev("goo"); + ASSERT_TRUE(iter->Valid()); + ASSERT_EQ("foo", iter->key().ToString()); + iter->Prev(); + + ASSERT_TRUE(iter->Valid()); + ASSERT_EQ("bar", iter->key().ToString()); + + delete iter; + db_->ReleaseSnapshot(snapshot); +} + TEST_P(DBIteratorTest, SkipStatistics) { Options options = CurrentOptions(); options.statistics = rocksdb::CreateDBStatistics(); diff --git a/table/merging_iterator.cc b/table/merging_iterator.cc index 99a867fe723..e4355475dbf 100644 --- a/table/merging_iterator.cc +++ b/table/merging_iterator.cc @@ -193,23 +193,11 @@ class MergingIterator : public InternalIterator { InitMaxHeap(); for (auto& child : children_) { if (&child != current_) { - if (!prefix_seek_mode_) { - child.Seek(key()); - if (child.Valid()) { - // Child is at first entry >= key(). Step back one to be < key() - TEST_SYNC_POINT_CALLBACK("MergeIterator::Prev:BeforePrev", - &child); - child.Prev(); - } else { - // Child has no entries >= key(). Position at last entry. - TEST_SYNC_POINT("MergeIterator::Prev:BeforeSeekToLast"); - child.SeekToLast(); - } - } else { - child.SeekForPrev(key()); - if (child.Valid() && comparator_->Equal(key(), child.key())) { - child.Prev(); - } + child.SeekForPrev(key()); + // Child is at first entry >= key(). Step back one to be < key() + TEST_SYNC_POINT_CALLBACK("MergeIterator::Prev:BeforePrev", &child); + if (child.Valid() && comparator_->Equal(key(), child.key())) { + child.Prev(); } } if (child.Valid()) {