Skip to content

Commit c61c983

Browse files
committed
Fix a bug that Prev() can hang.
Summary: Prev() now can hang when there is a key with more than max_skipped number of appearance internally but all of them are newer than the sequence ID to seek. Add unit tests to confirm the bug and fix it. Test Plan: make all check Reviewers: igor, haobo Reviewed By: igor CC: ljin, yhchiang, leveldb Differential Revision: https://reviews.facebook.net/D16899
1 parent f9d0530 commit c61c983

File tree

2 files changed

+76
-3
lines changed

2 files changed

+76
-3
lines changed

db/db_iter.cc

+7-3
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,10 @@ void DBIter::FindPrevUserEntry() {
379379
uint64_t num_skipped = 0;
380380

381381
ValueType value_type = kTypeDeletion;
382+
bool saved_key_valid = true;
382383
if (iter_->Valid()) {
383384
do {
384385
ParsedInternalKey ikey;
385-
bool saved_key_cleared = false;
386386
if (ParseKey(&ikey) && ikey.sequence <= sequence_) {
387387
if ((value_type != kTypeDeletion) &&
388388
user_comparator_->Compare(ikey.user_key, saved_key_) < 0) {
@@ -393,7 +393,7 @@ void DBIter::FindPrevUserEntry() {
393393
if (value_type == kTypeDeletion) {
394394
saved_key_.clear();
395395
ClearSavedValue();
396-
saved_key_cleared = true;
396+
saved_key_valid = false;
397397
} else {
398398
Slice raw_value = iter_->value();
399399
if (saved_value_.capacity() > raw_value.size() + 1048576) {
@@ -403,13 +403,17 @@ void DBIter::FindPrevUserEntry() {
403403
SaveKey(ExtractUserKey(iter_->key()), &saved_key_);
404404
saved_value_.assign(raw_value.data(), raw_value.size());
405405
}
406+
} else {
407+
// In the case of ikey.sequence > sequence_, we might have already
408+
// iterated to a different user key.
409+
saved_key_valid = false;
406410
}
407411
num_skipped++;
408412
// If we have sequentially iterated via numerous keys and still not
409413
// found the prev user-key, then it is better to seek so that we can
410414
// avoid too many key comparisons. We seek to the first occurence of
411415
// our current key by looking for max sequence number.
412-
if (!saved_key_cleared && num_skipped > max_skip_) {
416+
if (saved_key_valid && num_skipped > max_skip_) {
413417
num_skipped = 0;
414418
std::string last_key;
415419
AppendInternalKey(&last_key,

db/db_test.cc

+69
Original file line numberDiff line numberDiff line change
@@ -1368,6 +1368,75 @@ TEST(DBTest, IterSeekBeforePrev) {
13681368
delete iter;
13691369
}
13701370

1371+
TEST(DBTest, IterNextWithNewerSeq) {
1372+
ASSERT_OK(Put("0", "0"));
1373+
dbfull()->Flush(FlushOptions());
1374+
ASSERT_OK(Put("a", "b"));
1375+
ASSERT_OK(Put("c", "d"));
1376+
ASSERT_OK(Put("d", "e"));
1377+
auto iter = db_->NewIterator(ReadOptions());
1378+
1379+
// Create a key that needs to be skipped for Seq too new
1380+
for (uint64_t i = 0; i < last_options_.max_sequential_skip_in_iterations + 1;
1381+
i++) {
1382+
ASSERT_OK(Put("b", "f"));
1383+
}
1384+
1385+
iter->Seek(Slice("a"));
1386+
ASSERT_EQ(IterStatus(iter), "a->b");
1387+
iter->Next();
1388+
ASSERT_EQ(IterStatus(iter), "c->d");
1389+
delete iter;
1390+
}
1391+
1392+
TEST(DBTest, IterPrevWithNewerSeq) {
1393+
ASSERT_OK(Put("0", "0"));
1394+
dbfull()->Flush(FlushOptions());
1395+
ASSERT_OK(Put("a", "b"));
1396+
ASSERT_OK(Put("c", "d"));
1397+
ASSERT_OK(Put("d", "e"));
1398+
auto iter = db_->NewIterator(ReadOptions());
1399+
1400+
// Create a key that needs to be skipped for Seq too new
1401+
for (uint64_t i = 0; i < last_options_.max_sequential_skip_in_iterations + 1;
1402+
i++) {
1403+
ASSERT_OK(Put("b", "f"));
1404+
}
1405+
1406+
iter->Seek(Slice("d"));
1407+
ASSERT_EQ(IterStatus(iter), "d->e");
1408+
iter->Prev();
1409+
ASSERT_EQ(IterStatus(iter), "c->d");
1410+
iter->Prev();
1411+
ASSERT_EQ(IterStatus(iter), "a->b");
1412+
1413+
iter->Prev();
1414+
delete iter;
1415+
}
1416+
1417+
TEST(DBTest, IterPrevWithNewerSeq2) {
1418+
ASSERT_OK(Put("0", "0"));
1419+
dbfull()->Flush(FlushOptions());
1420+
ASSERT_OK(Put("a", "b"));
1421+
ASSERT_OK(Put("c", "d"));
1422+
ASSERT_OK(Put("d", "e"));
1423+
auto iter = db_->NewIterator(ReadOptions());
1424+
iter->Seek(Slice("c"));
1425+
ASSERT_EQ(IterStatus(iter), "c->d");
1426+
1427+
// Create a key that needs to be skipped for Seq too new
1428+
for (uint64_t i = 0; i < last_options_.max_sequential_skip_in_iterations + 1;
1429+
i++) {
1430+
ASSERT_OK(Put("b", "f"));
1431+
}
1432+
1433+
iter->Prev();
1434+
ASSERT_EQ(IterStatus(iter), "a->b");
1435+
1436+
iter->Prev();
1437+
delete iter;
1438+
}
1439+
13711440
TEST(DBTest, IterEmpty) {
13721441
do {
13731442
Iterator* iter = db_->NewIterator(ReadOptions());

0 commit comments

Comments
 (0)