From 38ad3c9f8aa36bec451eb03f8271ad9e6dff54cf Mon Sep 17 00:00:00 2001 From: Yi Wu Date: Mon, 27 Aug 2018 16:23:48 -0700 Subject: [PATCH] BlobDB: Avoid returning garbage value on key not found (#4321) Summary: When reading an expired key using `Get(..., std::string* value)` API, BlobDB first read the index entry and decode expiration from it. In this case, although BlobDB reset the PinnableSlice, the index entry is stored in user provided string `value`. The value will be returned as a garbage value, despite status being NotFound. Fixing it by use a different PinnableSlice to read the index entry. Pull Request resolved: https://github.com/facebook/rocksdb/pull/4321 Differential Revision: D9519042 Pulled By: yiwu-arbug fbshipit-source-id: f054c951a1fa98265228be94f931904ed7056677 --- utilities/blob_db/blob_db_impl.cc | 18 +++++++++++------- utilities/blob_db/blob_db_test.cc | 5 +++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/utilities/blob_db/blob_db_impl.cc b/utilities/blob_db/blob_db_impl.cc index 6c6fbc3b3..f614f3bac 100644 --- a/utilities/blob_db/blob_db_impl.cc +++ b/utilities/blob_db/blob_db_impl.cc @@ -1115,9 +1115,10 @@ Status BlobDBImpl::GetImpl(const ReadOptions& read_options, ReadOptions ro(read_options); bool snapshot_created = SetSnapshotIfNeeded(&ro); + PinnableSlice index_entry; Status s; bool is_blob_index = false; - s = db_impl_->GetImpl(ro, column_family, key, value, + s = db_impl_->GetImpl(ro, column_family, key, &index_entry, nullptr /*value_found*/, nullptr /*read_callback*/, &is_blob_index); TEST_SYNC_POINT("BlobDBImpl::Get:AfterIndexEntryGet:1"); @@ -1125,16 +1126,19 @@ Status BlobDBImpl::GetImpl(const ReadOptions& read_options, if (expiration != nullptr) { *expiration = kNoExpiration; } - if (s.ok() && is_blob_index) { - std::string index_entry = value->ToString(); - value->Reset(); - s = GetBlobValue(key, index_entry, value, expiration); + RecordTick(statistics_, BLOB_DB_NUM_KEYS_READ); + if (s.ok()) { + if (is_blob_index) { + s = GetBlobValue(key, index_entry, value, expiration); + } else { + // The index entry is the value itself in this case. + value->PinSelf(index_entry); + } + RecordTick(statistics_, BLOB_DB_BYTES_READ, value->size()); } if (snapshot_created) { db_->ReleaseSnapshot(ro.snapshot); } - RecordTick(statistics_, BLOB_DB_NUM_KEYS_READ); - RecordTick(statistics_, BLOB_DB_BYTES_READ, value->size()); return s; } diff --git a/utilities/blob_db/blob_db_test.cc b/utilities/blob_db/blob_db_test.cc index 4e9873e4b..ce6216c24 100644 --- a/utilities/blob_db/blob_db_test.cc +++ b/utilities/blob_db/blob_db_test.cc @@ -1413,6 +1413,11 @@ TEST_F(BlobDBTest, EvictExpiredFile) { blob_db_impl()->TEST_DeleteObsoleteFiles(); ASSERT_EQ(0, blob_db_impl()->TEST_GetBlobFiles().size()); ASSERT_EQ(0, blob_db_impl()->TEST_GetObsoleteFiles().size()); + // Make sure we don't return garbage value after blob file being evicted, + // but the blob index still exists in the LSM tree. + std::string val = ""; + ASSERT_TRUE(blob_db_->Get(ReadOptions(), "foo", &val).IsNotFound()); + ASSERT_EQ("", val); } TEST_F(BlobDBTest, DisableFileDeletions) {