From b27a1448b6370784e91b17bb3d07fb2afe07a098 Mon Sep 17 00:00:00 2001 From: Peter Dillinger Date: Thu, 7 May 2020 15:39:49 -0700 Subject: [PATCH] Fix false NotFound from batched MultiGet with kHashSearch (#6821) Summary: The error is assigning KeyContext::s to NotFound status in a table reader for a "not found in this table" case, which skips searching in later tables, like only a delete should. (The hash search index iterator is the only one that can return status NotFound even if Valid() == false.) This was detected by intermittent failure in MultiThreadedDBTest.MultiThreaded/5, a kHashSearch configuration. Pull Request resolved: https://github.com/facebook/rocksdb/pull/6821 Test Plan: modified existing unit test to reproduce problem Reviewed By: anand1976 Differential Revision: D21450469 Pulled By: pdillinger fbshipit-source-id: 7478003684d637dbd491cdac81468041a791be2c --- HISTORY.md | 1 + db/db_basic_test.cc | 8 ++++++-- table/block_based/block_based_table_reader.cc | 7 +++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 6eb8563ca..e95bb5858 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,7 @@ ## Unreleased ### Bug Fixes * Fix consistency checking error swallowing in some cases when options.force_consistency_checks = true. +* Fix possible false NotFound status from batched MultiGet using index type kHashSearch. ### Public API Change * Flush(..., column_family) may return Status::ColumnFamilyDropped() instead of Status::InvalidArgument() if column_family is dropped while processing the flush request. diff --git a/db/db_basic_test.cc b/db/db_basic_test.cc index 3bfcff0f2..a09474c6c 100644 --- a/db/db_basic_test.cc +++ b/db/db_basic_test.cc @@ -1295,14 +1295,18 @@ TEST_F(DBBasicTest, MultiGetBatchedSimpleUnsorted) { } while (ChangeCompactOptions()); } -TEST_F(DBBasicTest, MultiGetBatchedSimpleSorted) { +TEST_F(DBBasicTest, MultiGetBatchedSortedMultiFile) { do { CreateAndReopenWithCF({"pikachu"}, CurrentOptions()); SetPerfLevel(kEnableCount); + // To expand the power of this test, generate > 1 table file and + // mix with memtable ASSERT_OK(Put(1, "k1", "v1")); ASSERT_OK(Put(1, "k2", "v2")); + Flush(1); ASSERT_OK(Put(1, "k3", "v3")); ASSERT_OK(Put(1, "k4", "v4")); + Flush(1); ASSERT_OK(Delete(1, "k4")); ASSERT_OK(Put(1, "k5", "v5")); ASSERT_OK(Delete(1, "no_key")); @@ -1333,7 +1337,7 @@ TEST_F(DBBasicTest, MultiGetBatchedSimpleSorted) { ASSERT_TRUE(s[5].IsNotFound()); SetPerfLevel(kDisable); - } while (ChangeCompactOptions()); + } while (ChangeOptions()); } TEST_F(DBBasicTest, MultiGetBatchedMultiLevel) { diff --git a/table/block_based/block_based_table_reader.cc b/table/block_based/block_based_table_reader.cc index 4e852ebc2..303484e4a 100644 --- a/table/block_based/block_based_table_reader.cc +++ b/table/block_based/block_based_table_reader.cc @@ -2461,13 +2461,16 @@ void BlockBasedTable::MultiGet(const ReadOptions& read_options, ExtractUserKey(v.first_internal_key)) < 0)) { // The requested key falls between highest key in previous block and // lowest key in current block. - *(miter->s) = iiter->status(); + if (!iiter->status().IsNotFound()) { + *(miter->s) = iiter->status(); + } data_block_range.SkipKey(miter); sst_file_range.SkipKey(miter); continue; } if (!uncompression_dict_status.ok()) { + assert(!uncompression_dict_status.IsNotFound()); *(miter->s) = uncompression_dict_status; data_block_range.SkipKey(miter); sst_file_range.SkipKey(miter); @@ -2680,7 +2683,7 @@ void BlockBasedTable::MultiGet(const ReadOptions& read_options, PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_full_true_positive, 1, rep_->level); } - if (s.ok()) { + if (s.ok() && !iiter->status().IsNotFound()) { s = iiter->status(); } *(miter->s) = s;