From 19a97dd13977f6c1cc602b66aa786e87f11f474d Mon Sep 17 00:00:00 2001 From: anand76 Date: Thu, 3 Oct 2019 20:52:17 -0700 Subject: [PATCH] Fix data block upper bound checking for iterator reseek case (#5883) Summary: When an iterator reseek happens with the user specifying a new iterate_upper_bound in ReadOptions, and the new seek position is at the end of the same data block, the Seek() ends up using a stale value of data_block_within_upper_bound_ and may return incorrect results. Pull Request resolved: https://github.com/facebook/rocksdb/pull/5883 Test Plan: Added a new test case DBIteratorTest.IterReseekNewUpperBound. Verified that it failed due to the assertion failure without the fix, and passes with the fix. Differential Revision: D17752740 Pulled By: anand1976 fbshipit-source-id: f9b635ff5d6aeb0e1bef102cf8b2f900efd378e3 --- db/db_iterator_test.cc | 27 +++++++++++++++++++ table/block_based/block_based_table_reader.cc | 11 +++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/db/db_iterator_test.cc b/db/db_iterator_test.cc index 00207461e..6abe40b27 100644 --- a/db/db_iterator_test.cc +++ b/db/db_iterator_test.cc @@ -183,6 +183,33 @@ TEST_P(DBIteratorTest, IterSeekBeforePrev) { delete iter; } +TEST_P(DBIteratorTest, IterReseekNewUpperBound) { + Random rnd(301); + Options options = CurrentOptions(); + BlockBasedTableOptions table_options; + table_options.block_size = 1024; + table_options.block_size_deviation = 50; + options.table_factory.reset(NewBlockBasedTableFactory(table_options)); + options.compression = kNoCompression; + Reopen(options); + + ASSERT_OK(Put("a", RandomString(&rnd, 400))); + ASSERT_OK(Put("aabb", RandomString(&rnd, 400))); + ASSERT_OK(Put("aaef", RandomString(&rnd, 400))); + ASSERT_OK(Put("b", RandomString(&rnd, 400))); + dbfull()->Flush(FlushOptions()); + ReadOptions opts; + Slice ub = Slice("aa"); + opts.iterate_upper_bound = &ub; + auto iter = NewIterator(opts); + iter->Seek(Slice("a")); + ub = Slice("b"); + iter->Seek(Slice("aabc")); + ASSERT_TRUE(iter->Valid()); + ASSERT_EQ(iter->key().ToString(), "aaef"); + delete iter; +} + TEST_P(DBIteratorTest, IterSeekForPrevBeforeNext) { ASSERT_OK(Put("a", "b")); ASSERT_OK(Put("c", "d")); diff --git a/table/block_based/block_based_table_reader.cc b/table/block_based/block_based_table_reader.cc index 4c58c23ec..acab50aaf 100644 --- a/table/block_based/block_based_table_reader.cc +++ b/table/block_based/block_based_table_reader.cc @@ -2707,11 +2707,21 @@ void BlockBasedTableIterator::SeekImpl( // Index contains the first key of the block, and it's >= target. // We can defer reading the block. is_at_first_key_from_index_ = true; + // ResetDataIter() will invalidate block_iter_. Thus, there is no need to + // call CheckDataBlockWithinUpperBound() to check for iterate_upper_bound + // as that will be done later when the data block is actually read. ResetDataIter(); } else { // Need to use the data block. if (!same_block) { InitDataBlock(); + } else { + // When the user does a reseek, the iterate_upper_bound might have + // changed. CheckDataBlockWithinUpperBound() needs to be called + // explicitly if the reseek ends up in the same data block. + // If the reseek ends up in a different block, InitDataBlock() will do + // the iterator upper bound check. + CheckDataBlockWithinUpperBound(); } if (target) { @@ -2722,7 +2732,6 @@ void BlockBasedTableIterator::SeekImpl( FindKeyForward(); } - CheckDataBlockWithinUpperBound(); CheckOutOfBound(); if (target) {