mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 14:47:40 +08:00
Fix leak or crash on failure in automatic atomic flush (#12176)
Summary: Through code inspection in debugging an apparent leak of ColumnFamilyData in the crash test, I found a case where too few UnrefAndTryDelete() could be called on a cfd. This fixes that case, which would fail like this in the new unit test: ``` db_flush_test: db/column_family.cc:1648: rocksdb::ColumnFamilySet::~ColumnFamilySet(): Assertion `last_ref' failed. ``` Pull Request resolved: https://github.com/facebook/rocksdb/pull/12176 Test Plan: unit test added Reviewed By: cbi42 Differential Revision: D52417071 Pulled By: pdillinger fbshipit-source-id: 4ee33c918409cf9c1968f138e273d3347a6cc8e5
This commit is contained in:
committed by
Facebook GitHub Bot
parent
106058c076
commit
a771a47a1b
@@ -3035,6 +3035,39 @@ TEST_P(DBAtomicFlushTest, RollbackAfterFailToInstallResults) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, FailureInMultiCfAutomaticFlush) {
|
||||
bool atomic_flush = GetParam();
|
||||
auto fault_injection_env = std::make_shared<FaultInjectionTestEnv>(env_);
|
||||
Options options = CurrentOptions();
|
||||
options.env = fault_injection_env.get();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = atomic_flush;
|
||||
const int kNumKeysTriggerFlush = 4;
|
||||
options.memtable_factory.reset(
|
||||
test::NewSpecialSkipListFactory(kNumKeysTriggerFlush));
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
for (size_t cf = 0; cf < handles_.size(); ++cf) {
|
||||
ASSERT_OK(Put(static_cast<int>(cf), "a", "value"));
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::ScheduleFlushes:PreSwitchMemtable",
|
||||
[&](void* /*arg*/) { fault_injection_env->SetFilesystemActive(false); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
for (int i = 1; i < kNumKeysTriggerFlush; ++i) {
|
||||
ASSERT_OK(Put(0, "key" + std::to_string(i), "value" + std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
// Next write after failed flush should fail.
|
||||
ASSERT_NOK(Put(0, "x", "y"));
|
||||
fault_injection_env->SetFilesystemActive(true);
|
||||
Close();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
// In atomic flush, concurrent bg flush threads commit to the MANIFEST in
|
||||
// serial, in the order of their picked memtables for each column family.
|
||||
// Only when a bg flush thread finds out that its memtables are the earliest
|
||||
|
||||
@@ -1563,6 +1563,8 @@ Status DBImpl::WriteRecoverableState() {
|
||||
if (status.ok()) {
|
||||
cached_recoverable_state_.Clear();
|
||||
cached_recoverable_state_empty_ = true;
|
||||
} else {
|
||||
// FIXME: !ok status is untested
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -2060,16 +2062,15 @@ Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::ScheduleFlushes:PreSwitchMemtable",
|
||||
nullptr);
|
||||
for (auto& cfd : cfds) {
|
||||
if (!cfd->mem()->IsEmpty()) {
|
||||
if (status.ok() && !cfd->mem()->IsEmpty()) {
|
||||
status = SwitchMemtable(cfd, context);
|
||||
}
|
||||
if (cfd->UnrefAndTryDelete()) {
|
||||
cfd = nullptr;
|
||||
}
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (two_write_queues_) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Fixed a possible memory leak or crash on a failure (such as I/O error) in automatic atomic flush of multiple column families.
|
||||
Reference in New Issue
Block a user