fix DBImpl::NewInternalIterator super-version leak on failure

Summary:
Close #2955
Closes https://github.com/facebook/rocksdb/pull/2960

Differential Revision: D5962872

Pulled By: yiwu-arbug

fbshipit-source-id: a6472d5c015bea3dc476c572ff5a5c90259e6059
This commit is contained in:
Yi Wu 2017-10-11 14:48:28 -07:00 committed by Facebook Github Bot
parent 019aa7074c
commit fb4ae4d810
5 changed files with 51 additions and 18 deletions

View File

@ -894,6 +894,7 @@ InternalIterator* DBImpl::NewInternalIterator(
range_del_agg);
}
}
TEST_SYNC_POINT_CALLBACK("DBImpl::NewInternalIterator:StatusCallback", &s);
if (s.ok()) {
// Collect iterators for files in L0 - Ln
if (read_options.read_tier != kMemtableTier) {
@ -907,8 +908,10 @@ InternalIterator* DBImpl::NewInternalIterator(
internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, nullptr);
return internal_iter;
} else {
CleanupSuperVersion(super_version);
}
return NewErrorInternalIterator(s);
return NewErrorInternalIterator(s, arena);
}
ColumnFamilyHandle* DBImpl::DefaultColumnFamily() const {
@ -1819,11 +1822,7 @@ SuperVersion* DBImpl::GetAndRefSuperVersion(uint32_t column_family_id) {
return GetAndRefSuperVersion(cfd);
}
void DBImpl::ReturnAndCleanupSuperVersion(ColumnFamilyData* cfd,
SuperVersion* sv) {
bool unref_sv = !cfd->ReturnThreadLocalSuperVersion(sv);
if (unref_sv) {
void DBImpl::CleanupSuperVersion(SuperVersion* sv) {
// Release SuperVersion
if (sv->Unref()) {
{
@ -1834,6 +1833,12 @@ void DBImpl::ReturnAndCleanupSuperVersion(ColumnFamilyData* cfd,
RecordTick(stats_, NUMBER_SUPERVERSION_CLEANUPS);
}
RecordTick(stats_, NUMBER_SUPERVERSION_RELEASES);
}
void DBImpl::ReturnAndCleanupSuperVersion(ColumnFamilyData* cfd,
SuperVersion* sv) {
if (!cfd->ReturnThreadLocalSuperVersion(sv)) {
CleanupSuperVersion(sv);
}
}

View File

@ -477,6 +477,9 @@ class DBImpl : public DB {
// mutex is held.
SuperVersion* GetAndRefSuperVersion(uint32_t column_family_id);
// Un-reference the super version and clean it up if it is the last reference.
void CleanupSuperVersion(SuperVersion* sv);
// Un-reference the super version and return it to thread local cache if
// needed. If it is the last reference of the super version. Clean it up
// after un-referencing it.

View File

@ -1997,6 +1997,19 @@ TEST_F(DBIteratorTest, Refresh) {
iter.reset();
}
TEST_F(DBIteratorTest, CreationFailure) {
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::NewInternalIterator:StatusCallback", [](void* arg) {
*(reinterpret_cast<Status*>(arg)) = Status::Corruption("test status");
});
SyncPoint::GetInstance()->EnableProcessing();
Iterator* iter = db_->NewIterator(ReadOptions());
ASSERT_FALSE(iter->Valid());
ASSERT_TRUE(iter->status().IsCorruption());
delete iter;
}
} // namespace rocksdb
int main(int argc, char** argv) {

View File

@ -387,10 +387,20 @@ MergeIteratorBuilder::MergeIteratorBuilder(
new (mem) MergingIterator(comparator, nullptr, 0, true, prefix_seek_mode);
}
MergeIteratorBuilder::~MergeIteratorBuilder() {
if (first_iter != nullptr) {
first_iter->~InternalIterator();
}
if (merge_iter != nullptr) {
merge_iter->~MergingIterator();
}
}
void MergeIteratorBuilder::AddIterator(InternalIterator* iter) {
if (!use_merging_iter && first_iter != nullptr) {
merge_iter->AddIterator(first_iter);
use_merging_iter = true;
first_iter = nullptr;
}
if (use_merging_iter) {
merge_iter->AddIterator(iter);
@ -400,13 +410,15 @@ void MergeIteratorBuilder::AddIterator(InternalIterator* iter) {
}
InternalIterator* MergeIteratorBuilder::Finish() {
InternalIterator* ret = nullptr;
if (!use_merging_iter) {
return first_iter;
ret = first_iter;
first_iter = nullptr;
} else {
auto ret = merge_iter;
ret = merge_iter;
merge_iter = nullptr;
return ret;
}
return ret;
}
} // namespace rocksdb

View File

@ -40,7 +40,7 @@ class MergeIteratorBuilder {
// arena: where the merging iterator needs to be allocated from.
explicit MergeIteratorBuilder(const InternalKeyComparator* comparator,
Arena* arena, bool prefix_seek_mode = false);
~MergeIteratorBuilder() {}
~MergeIteratorBuilder();
// Add iter to the merging iterator.
void AddIterator(InternalIterator* iter);