From 4584a99a3cfef05bdcf8e3be5a903fc0881b4769 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 17e13f3c8..9bd1de0a6 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -10,6 +10,7 @@ * Fix a bug caused by using wrong compare function when sorting the input keys of MultiGet with timestamps. * Upgraded version of bzip library (1.0.6 -> 1.0.8) used with RocksJava to address potential vulnerabilities if an attacker can manipulate compressed data saved and loaded by RocksDB (not normal). See issue #6703. * 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 * Add a ConfigOptions argument to the APIs dealing with converting options to and from strings and files. The ConfigOptions is meant to replace some of the options (such as input_strings_escaped and ignore_unknown_options) and allow for more parameters to be passed in the future without changing the function signature. diff --git a/db/db_basic_test.cc b/db/db_basic_test.cc index 1fa9a1a9f..60a84194c 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;