mirror of https://github.com/facebook/rocksdb.git
Summary: Tests a scenario where range tombstone reseek used to cause MergingIterator to discard non-ok status. Ran on main without https://github.com/facebook/rocksdb/issues/11786: ``` ./db_range_del_test --gtest_filter="*RangeDelReseekAfterFileReadError*" Note: Google Test filter = *RangeDelReseekAfterFileReadError* [==========] Running 1 test from 1 test case. [----------] Global test environment set-up. [----------] 1 test from DBRangeDelTest [ RUN ] DBRangeDelTest.RangeDelReseekAfterFileReadError db/db_range_del_test.cc:3577: Failure Value of: iter->Valid() Actual: true Expected: false [ FAILED ] DBRangeDelTest.RangeDelReseekAfterFileReadError (64 ms) ``` Pull Request resolved: https://github.com/facebook/rocksdb/pull/11790 Reviewed By: ajkr Differential Revision: D48972869 Pulled By: cbi42 fbshipit-source-id: b1a71867533b0fb60af86f8ce8a9e391ba84dd57
This commit is contained in:
parent
137cd4bb75
commit
195f35c08b
|
@ -3511,6 +3511,138 @@ TEST_F(DBRangeDelTest, MemtableMaxRangeDeletions) {
|
|||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_EQ(3, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, RangeDelReseekAfterFileReadError) {
|
||||
// This is to test a bug that is fixed in
|
||||
// https://github.com/facebook/rocksdb/pull/11786.
|
||||
Options opts = CurrentOptions();
|
||||
opts.num_levels = 7;
|
||||
|
||||
// Set up LSM
|
||||
//
|
||||
// L4: F1: [key1] F2: [key2]
|
||||
// L5: F3:[DeleteRange(key3, key6)]
|
||||
// L6: F4:[key3, key6]
|
||||
// Will inject error when reading from F2.
|
||||
// SeekToFirst() should land on key1.
|
||||
// Next() should encounter error when reading from F2,
|
||||
// and range del reseek should not reset this status.
|
||||
Random rnd(301);
|
||||
// L6
|
||||
ASSERT_OK(Put(Key(3), rnd.RandomString(100)));
|
||||
ASSERT_OK(Put(Key(6), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(6);
|
||||
// L5
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(3),
|
||||
Key(6)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(5);
|
||||
// L4
|
||||
ASSERT_OK(Put(Key(2), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(4);
|
||||
std::string fname;
|
||||
std::vector<LiveFileMetaData> live_files;
|
||||
db_->GetLiveFilesMetaData(&live_files);
|
||||
for (auto& meta : live_files) {
|
||||
if (meta.level == 4) {
|
||||
fname = meta.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(!fname.empty());
|
||||
ASSERT_OK(Put(Key(1), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(4);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::BeforeReturn", [&fname](void* pair_ptr) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
if (p->first->find(fname) != std::string::npos) {
|
||||
*p->second = IOStatus::IOError();
|
||||
p->second->SetRetryable(true);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
std::unique_ptr<Iterator> iter{db_->NewIterator(ReadOptions())};
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(iter->key(), Key(1));
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_NOK(iter->status());
|
||||
ASSERT_TRUE(iter->status().IsIOError());
|
||||
iter.reset();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// Reverse scan
|
||||
// LSM setup
|
||||
// L4: F1: [key2] F2: [key7, key8]
|
||||
// L5: F3:[[key3, key6)]
|
||||
// L6: F4:[key1, key5]
|
||||
// Ingest error when read from F1.
|
||||
// SeekToLast() should land on key8.
|
||||
// During Prev(), MergingIterator will encounter error when reading from F1
|
||||
// and do a range del reseek (it sees key5 covered by a range tombstone).
|
||||
DestroyAndReopen(opts);
|
||||
// L6
|
||||
ASSERT_OK(Put(Key(1), rnd.RandomString(100)));
|
||||
ASSERT_OK(Put(Key(5), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(6);
|
||||
// L5
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(3),
|
||||
Key(6)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(5);
|
||||
// L4
|
||||
ASSERT_OK(Put(Key(2), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(4);
|
||||
live_files.clear();
|
||||
db_->GetLiveFilesMetaData(&live_files);
|
||||
for (auto& meta : live_files) {
|
||||
if (meta.level == 4) {
|
||||
fname = meta.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(!fname.empty());
|
||||
ASSERT_OK(Put(Key(7), rnd.RandomString(100)));
|
||||
ASSERT_OK(Put(Key(8), rnd.RandomString(100)));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(4);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"RandomAccessFileReader::Read::AnyOffset", [&fname](void* pair_ptr) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
|
||||
if (p->first->find(fname) != std::string::npos) {
|
||||
*p->second = IOStatus::IOError();
|
||||
p->second->SetRetryable(true);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
iter.reset(db_->NewIterator(ReadOptions()));
|
||||
iter->SeekToLast();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(iter->key(), Key(8));
|
||||
// Note that for reverse scan, DBIter will need to ensure
|
||||
// the key it returns is the one with the highest sequence number.
|
||||
// To return key7, it internally calls MergingIterator::Prev()
|
||||
// until it reaches a previous user key.
|
||||
iter->Prev();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_NOK(iter->status());
|
||||
ASSERT_TRUE(iter->status().IsIOError());
|
||||
|
||||
iter.reset();
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
|
Loading…
Reference in New Issue