mirror of https://github.com/facebook/rocksdb.git
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:
parent
7f3a634e06
commit
92ee3350e0
|
@ -1035,6 +1035,60 @@ TEST_P(DBIteratorTest, DBIteratorBoundTest) {
|
|||
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
|
||||
|
||||
TEST_P(DBIteratorTest, DBIteratorBoundOptimizationTest) {
|
||||
|
|
|
@ -2039,7 +2039,6 @@ void BlockBasedTableIterator::FindKeyForward() {
|
|||
&reached_upper_bound);
|
||||
if (reached_upper_bound) {
|
||||
is_out_of_bound_ = true;
|
||||
ResetDataIter();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -535,7 +535,8 @@ class BlockBasedTableIterator : public InternalIterator {
|
|||
void Next() override;
|
||||
void Prev() 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 {
|
||||
assert(Valid());
|
||||
|
|
Loading…
Reference in New Issue