BlockBasedTableIterator to keep BlockIter after out of upper bound (#4004)

Summary:
b555ed30a4 makes the BlockBasedTableIterator to be invalidated if the current position if over the upper bound. However, this can bring performance regression to the case of multiple Seek()s hitting the same data block but all out of upper bound.

For example, if an SST file has a data block containing following keys : {a, z}

The user sets the upper bound to be "x", and it executed following queries:
Seek("b")
Seek("c")
Seek("d")

Before the upper bound optimization, these queries always come to this same current data block of the iterator, but now inside each Seek() the data block is read from the block cache but is returned again.

To prevent this regression case, we keep the current data block iterator if it is upper bound.
Closes https://github.com/facebook/rocksdb/pull/4004

Differential Revision: D8463192

Pulled By: siying

fbshipit-source-id: 8710628b30acde7063a097c3184d6c4333a8ef81
This commit is contained in:
Siying Dong 2018-06-19 09:42:40 -07:00 committed by Facebook Github Bot
parent 7f3a634e06
commit 92ee3350e0
3 changed files with 56 additions and 2 deletions

View file

@ -1035,6 +1035,60 @@ TEST_P(DBIteratorTest, DBIteratorBoundTest) {
ASSERT_EQ(static_cast<int>(get_perf_context()->internal_delete_skipped_count), 0); ASSERT_EQ(static_cast<int>(get_perf_context()->internal_delete_skipped_count), 0);
} }
} }
TEST_P(DBIteratorTest, DBIteratorBoundMultiSeek) {
Options options = CurrentOptions();
options.env = env_;
options.create_if_missing = true;
options.statistics = rocksdb::CreateDBStatistics();
options.prefix_extractor = nullptr;
DestroyAndReopen(options);
ASSERT_OK(Put("a", "0"));
ASSERT_OK(Put("z", "0"));
ASSERT_OK(Flush());
ASSERT_OK(Put("foo1", "bar1"));
ASSERT_OK(Put("foo2", "bar2"));
ASSERT_OK(Put("foo3", "bar3"));
ASSERT_OK(Put("foo4", "bar4"));
{
std::string up_str = "foo5";
Slice up(up_str);
ReadOptions ro;
ro.iterate_upper_bound = &up;
std::unique_ptr<Iterator> iter(NewIterator(ro));
iter->Seek("foo1");
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->key().compare(Slice("foo1")), 0);
uint64_t prev_block_cache_hit =
TestGetTickerCount(options, BLOCK_CACHE_HIT);
uint64_t prev_block_cache_miss =
TestGetTickerCount(options, BLOCK_CACHE_MISS);
ASSERT_GT(prev_block_cache_hit + prev_block_cache_miss, 0);
iter->Seek("foo4");
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->key().compare(Slice("foo4")), 0);
ASSERT_EQ(prev_block_cache_hit,
TestGetTickerCount(options, BLOCK_CACHE_HIT));
ASSERT_EQ(prev_block_cache_miss,
TestGetTickerCount(options, BLOCK_CACHE_MISS));
iter->Seek("foo2");
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->key().compare(Slice("foo2")), 0);
iter->Next();
ASSERT_TRUE(iter->Valid());
ASSERT_EQ(iter->key().compare(Slice("foo3")), 0);
ASSERT_EQ(prev_block_cache_hit,
TestGetTickerCount(options, BLOCK_CACHE_HIT));
ASSERT_EQ(prev_block_cache_miss,
TestGetTickerCount(options, BLOCK_CACHE_MISS));
}
}
#endif #endif
TEST_P(DBIteratorTest, DBIteratorBoundOptimizationTest) { TEST_P(DBIteratorTest, DBIteratorBoundOptimizationTest) {

View file

@ -2039,7 +2039,6 @@ void BlockBasedTableIterator::FindKeyForward() {
&reached_upper_bound); &reached_upper_bound);
if (reached_upper_bound) { if (reached_upper_bound) {
is_out_of_bound_ = true; is_out_of_bound_ = true;
ResetDataIter();
return; return;
} }
} }

View file

@ -535,7 +535,8 @@ class BlockBasedTableIterator : public InternalIterator {
void Next() override; void Next() override;
void Prev() override; void Prev() override;
bool Valid() const override { bool Valid() const override {
return block_iter_points_to_real_block_ && data_block_iter_.Valid(); return !is_out_of_bound_ && block_iter_points_to_real_block_ &&
data_block_iter_.Valid();
} }
Slice key() const override { Slice key() const override {
assert(Valid()); assert(Valid());