Fix a missing CV signal in FindObsoleteFiles() (#14069)

Summary:
Fixed a missing CV signal when `FindObsoleteFiles()` decides there is nothing to purge and then decrements `pending_purge_obsolete_files_` to zero.  This bug could cause `DB::GetSortedWalFiles()` to hang, at least.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/14069

Test Plan: unit test repro

Reviewed By: hx235

Differential Revision: D85453534

Pulled By: cbi42

fbshipit-source-id: cf5cfe7f5087459ca1f1f28ce81ea6afc84178f0
This commit is contained in:
Andrew Kryczka
2025-10-24 13:11:26 -07:00
committed by meta-codesync[bot]
parent 2edc660e28
commit e687ca79b4
4 changed files with 46 additions and 0 deletions
+1
View File
@@ -109,6 +109,7 @@ Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
{
InstrumentedMutexLock l(&mutex_);
while (pending_purge_obsolete_files_ > 0 || bg_purge_scheduled_ > 0) {
TEST_SYNC_POINT("DBImpl::GetSortedWalFilesImpl:WaitPurge");
bg_cv_.Wait();
}
+3
View File
@@ -267,6 +267,9 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
if (!job_context->HaveSomethingToDelete()) {
mutex_.AssertHeld();
--pending_purge_obsolete_files_;
if (pending_purge_obsolete_files_ == 0) {
bg_cv_.SignalAll();
}
}
});
+41
View File
@@ -303,6 +303,47 @@ TEST_F(ObsoleteFilesTest, BlobFiles) {
ASSERT_EQ(deleted_files, expected_deleted_files);
}
TEST_F(ObsoleteFilesTest, GetSortedWalFilesHangsAfterNoopPurge) {
// This test used to trigger a hang in `DB::GetSortedWalFiles()`, where it
// would wait for a no-op purge that did not signal the CV upon completion.
// Grab an iterator and flush to switch the super version. That way, when the
// iterator is destroyed, it will go through the purge path.
DB* db = db_; // Only using `db` makes it clear we only use DB-level APIs.
ASSERT_OK(db->Put(WriteOptions(), "key", "value"));
std::unique_ptr<Iterator> iter(db->NewIterator(ReadOptions()));
ASSERT_OK(db->Flush(FlushOptions()));
// Sync points ensure `GetSortedWalFiles()` waits for a purge after
// `FindObsoleteFiles()` releases the mutex but before its corresponding purge
// completes.
SyncPoint::GetInstance()->SetCallBack(
"FindObsoleteFiles::PostMutexUnlock", [&](void* /* arg */) {
TEST_SYNC_POINT(
"ObsoleteFilesTest::GetSortedWalFilesHangsAfterNoopPurge:"
"InCallback:1");
TEST_SYNC_POINT(
"ObsoleteFilesTest::GetSortedWalFilesHangsAfterNoopPurge:"
"InCallback:2");
});
SyncPoint::GetInstance()->LoadDependency({
{"ObsoleteFilesTest::GetSortedWalFilesHangsAfterNoopPurge:InCallback:1",
"ObsoleteFilesTest::GetSortedWalFilesHangsAfterNoopPurge:Thread:Begin"},
{"DBImpl::GetSortedWalFilesImpl:WaitPurge",
"ObsoleteFilesTest::GetSortedWalFilesHangsAfterNoopPurge:InCallback:2"},
});
SyncPoint::GetInstance()->EnableProcessing();
port::Thread get_sorted_wal_files_thread([db]() {
TEST_SYNC_POINT(
"ObsoleteFilesTest::GetSortedWalFilesHangsAfterNoopPurge:Thread:Begin");
VectorWalPtr files;
ASSERT_OK(db->GetSortedWalFiles(files));
});
iter.reset();
get_sorted_wal_files_thread.join();
}
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
@@ -0,0 +1 @@
Fixed a bug where `DB::GetSortedWalFiles()` could hang when waiting for a purge operation that found nothing to do (potentially triggered by iterator release, flush, compaction, etc.).