Fix DBImpl::GetColumnFamilyHandleUnlocked race condition (#4391)
Summary: - Fix DBImpl API race condition The timeline of execution flow is as follow: ``` timeline user_thread1 user_thread2 t1 | cfh = GetColumnFamilyHandleUnlocked(0) t2 | id1 = cfh->GetID() t3 | GetColumnFamilyHandleUnlocked(1) t4 | id2 = cfh->GetID() V ``` The original implementation return a pointer to a stateful variable, so that the return `ColumnFamilyHandle` will be changed when another thread calls `GetColumnFamilyHandleUnlocked` with different `column family id` - Expose ColumnFamily ID to compaction event listener - Fix the return status of `DBImpl::GetLatestSequenceForKey` Pull Request resolved: https://github.com/facebook/rocksdb/pull/4391 Differential Revision: D10221243 Pulled By: yiwu-arbug fbshipit-source-id: dec60ee9ff0c8261a2f2413a8506ec1063991993
This commit is contained in:
parent
e0f05754ba
commit
27090ae8f6
@ -2154,7 +2154,7 @@ ColumnFamilyHandle* DBImpl::GetColumnFamilyHandle(uint32_t column_family_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// REQUIRED: mutex is NOT held.
|
// REQUIRED: mutex is NOT held.
|
||||||
ColumnFamilyHandle* DBImpl::GetColumnFamilyHandleUnlocked(
|
std::unique_ptr<ColumnFamilyHandle> DBImpl::GetColumnFamilyHandleUnlocked(
|
||||||
uint32_t column_family_id) {
|
uint32_t column_family_id) {
|
||||||
ColumnFamilyMemTables* cf_memtables = column_family_memtables_.get();
|
ColumnFamilyMemTables* cf_memtables = column_family_memtables_.get();
|
||||||
|
|
||||||
@ -2164,7 +2164,8 @@ ColumnFamilyHandle* DBImpl::GetColumnFamilyHandleUnlocked(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cf_memtables->GetColumnFamilyHandle();
|
return std::unique_ptr<ColumnFamilyHandleImpl>(
|
||||||
|
new ColumnFamilyHandleImpl(cf_memtables->current(), this, &mutex_));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DBImpl::GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
void DBImpl::GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||||
|
@ -545,7 +545,8 @@ class DBImpl : public DB {
|
|||||||
ColumnFamilyHandle* GetColumnFamilyHandle(uint32_t column_family_id);
|
ColumnFamilyHandle* GetColumnFamilyHandle(uint32_t column_family_id);
|
||||||
|
|
||||||
// Same as above, should called without mutex held and not on write thread.
|
// Same as above, should called without mutex held and not on write thread.
|
||||||
ColumnFamilyHandle* GetColumnFamilyHandleUnlocked(uint32_t column_family_id);
|
std::unique_ptr<ColumnFamilyHandle> GetColumnFamilyHandleUnlocked(
|
||||||
|
uint32_t column_family_id);
|
||||||
|
|
||||||
// Returns the number of currently running flushes.
|
// Returns the number of currently running flushes.
|
||||||
// REQUIREMENT: mutex_ must be held when calling this function.
|
// REQUIREMENT: mutex_ must be held when calling this function.
|
||||||
|
@ -2835,6 +2835,44 @@ TEST_F(DBTest2, TestBBTTailPrefetch) {
|
|||||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DBTest2, TestGetColumnFamilyHandleUnlocked) {
|
||||||
|
// Setup sync point dependency to reproduce the race condition of
|
||||||
|
// DBImpl::GetColumnFamilyHandleUnlocked
|
||||||
|
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||||
|
{ {"TestGetColumnFamilyHandleUnlocked::GetColumnFamilyHandleUnlocked1",
|
||||||
|
"TestGetColumnFamilyHandleUnlocked::PreGetColumnFamilyHandleUnlocked2"},
|
||||||
|
{"TestGetColumnFamilyHandleUnlocked::GetColumnFamilyHandleUnlocked2",
|
||||||
|
"TestGetColumnFamilyHandleUnlocked::ReadColumnFamilyHandle1"},
|
||||||
|
});
|
||||||
|
SyncPoint::GetInstance()->EnableProcessing();
|
||||||
|
|
||||||
|
CreateColumnFamilies({"test1", "test2"}, Options());
|
||||||
|
ASSERT_EQ(handles_.size(), 2);
|
||||||
|
|
||||||
|
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||||
|
port::Thread user_thread1([&]() {
|
||||||
|
auto cfh = dbi->GetColumnFamilyHandleUnlocked(handles_[0]->GetID());
|
||||||
|
ASSERT_EQ(cfh->GetID(), handles_[0]->GetID());
|
||||||
|
TEST_SYNC_POINT("TestGetColumnFamilyHandleUnlocked::GetColumnFamilyHandleUnlocked1");
|
||||||
|
TEST_SYNC_POINT("TestGetColumnFamilyHandleUnlocked::ReadColumnFamilyHandle1");
|
||||||
|
ASSERT_EQ(cfh->GetID(), handles_[0]->GetID());
|
||||||
|
});
|
||||||
|
|
||||||
|
port::Thread user_thread2([&]() {
|
||||||
|
TEST_SYNC_POINT("TestGetColumnFamilyHandleUnlocked::PreGetColumnFamilyHandleUnlocked2");
|
||||||
|
auto cfh = dbi->GetColumnFamilyHandleUnlocked(handles_[1]->GetID());
|
||||||
|
ASSERT_EQ(cfh->GetID(), handles_[1]->GetID());
|
||||||
|
TEST_SYNC_POINT("TestGetColumnFamilyHandleUnlocked::GetColumnFamilyHandleUnlocked2");
|
||||||
|
ASSERT_EQ(cfh->GetID(), handles_[1]->GetID());
|
||||||
|
});
|
||||||
|
|
||||||
|
user_thread1.join();
|
||||||
|
user_thread2.join();
|
||||||
|
|
||||||
|
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||||
|
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace rocksdb
|
} // namespace rocksdb
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
@ -1459,8 +1459,7 @@ Status BlobDBImpl::GCFileAndUpdateLSM(const std::shared_ptr<BlobFile>& bfptr,
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* cfh =
|
auto cfh = db_impl_->DefaultColumnFamily();
|
||||||
db_impl_->GetColumnFamilyHandleUnlocked(bfptr->column_family_id());
|
|
||||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||||
auto column_family_id = cfd->GetID();
|
auto column_family_id = cfd->GetID();
|
||||||
bool has_ttl = header.has_ttl;
|
bool has_ttl = header.has_ttl;
|
||||||
|
Loading…
Reference in New Issue
Block a user