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.
|
||||
ColumnFamilyHandle* DBImpl::GetColumnFamilyHandleUnlocked(
|
||||
std::unique_ptr<ColumnFamilyHandle> DBImpl::GetColumnFamilyHandleUnlocked(
|
||||
uint32_t column_family_id) {
|
||||
ColumnFamilyMemTables* cf_memtables = column_family_memtables_.get();
|
||||
|
||||
@ -2164,7 +2164,8 @@ ColumnFamilyHandle* DBImpl::GetColumnFamilyHandleUnlocked(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return cf_memtables->GetColumnFamilyHandle();
|
||||
return std::unique_ptr<ColumnFamilyHandleImpl>(
|
||||
new ColumnFamilyHandleImpl(cf_memtables->current(), this, &mutex_));
|
||||
}
|
||||
|
||||
void DBImpl::GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
|
@ -545,7 +545,8 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* GetColumnFamilyHandle(uint32_t column_family_id);
|
||||
|
||||
// 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.
|
||||
// REQUIREMENT: mutex_ must be held when calling this function.
|
||||
|
@ -2835,6 +2835,44 @@ TEST_F(DBTest2, TestBBTTailPrefetch) {
|
||||
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
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
@ -1459,8 +1459,7 @@ Status BlobDBImpl::GCFileAndUpdateLSM(const std::shared_ptr<BlobFile>& bfptr,
|
||||
return s;
|
||||
}
|
||||
|
||||
auto* cfh =
|
||||
db_impl_->GetColumnFamilyHandleUnlocked(bfptr->column_family_id());
|
||||
auto cfh = db_impl_->DefaultColumnFamily();
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
auto column_family_id = cfd->GetID();
|
||||
bool has_ttl = header.has_ttl;
|
||||
|
Loading…
Reference in New Issue
Block a user