Skip to content

Commit

Permalink
Fix race between flush error recovery VS db destruction
Browse files Browse the repository at this point in the history
  • Loading branch information
hx235 committed Oct 24, 2023
1 parent 543191f commit 50fc35a
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 0 deletions.
8 changes: 8 additions & 0 deletions db/error_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,13 @@ const Status& ErrorHandler::StartRecoverFromRetryableBGIOError(
} else if (db_options_.max_bgerror_resume_count <= 0 || recovery_in_prog_) {
// Auto resume BG error is not enabled, directly return bg_error_.
return bg_error_;
} else if (end_recovery_) {
// Can temporarily release db mutex
EventHelpers::NotifyOnErrorRecoveryEnd(db_options_.listeners, bg_error_,
Status::ShutdownInProgress(),
db_mutex_);
db_mutex_->AssertHeld();
return bg_error_;
}
if (bg_error_stats_ != nullptr) {
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_AUTORESUME_COUNT);
Expand Down Expand Up @@ -819,6 +826,7 @@ void ErrorHandler::EndAutoRecovery() {
old_recovery_thread->join();
db_mutex_->Lock();
}
TEST_SYNC_POINT("PostEndAutoRecovery");
return;
}

Expand Down
47 changes: 47 additions & 0 deletions db/error_handler_fs_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "rocksdb/io_status.h"
#include "rocksdb/sst_file_manager.h"
#include "test_util/sync_point.h"
#include "test_util/testharness.h"
#include "util/random.h"
#include "utilities/fault_injection_env.h"
#include "utilities/fault_injection_fs.h"
Expand Down Expand Up @@ -2473,6 +2474,52 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableErrorAbortRecovery) {
Destroy(options);
}

TEST_F(DBErrorHandlingFSTest, FlushErrorRecoveryRaceWithDBDestruction) {
Options options = GetDefaultOptions();
options.env = fault_env_.get();
options.create_if_missing = true;
DestroyAndReopen(options);
ASSERT_OK(Put("k1", "val"));

// Inject retryable flush error
bool error_set = false;
SyncPoint::GetInstance()->SetCallBack(
"BuildTable:BeforeOutputValidation", [&](void*) {
if (error_set) {
return;
}
IOStatus st = IOStatus::IOError("Injected");
st.SetRetryable(true);
fault_fs_->SetFilesystemActive(false, st);
error_set = true;
});

port::Thread db_close_thread;
SyncPoint::GetInstance()->SetCallBack(
"BuildTable:BeforeDeleteFile", [&](void*) {
// Clear retryable flush error injection
fault_fs_->SetFilesystemActive(true);

// Coerce race between ending auto recovery in db destruction and flush
// error recovery
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"PostEndAutoRecovery",
"StartRecoverFromRetryableBGIOError::in_progress"}});
db_close_thread = port::Thread([&]() { Close(); });
});
SyncPoint::GetInstance()->EnableProcessing();

Status s = Flush();
ASSERT_NOK(s);
// Prior to the fix, the db close will crash due to the recovery thread for
// flush error is not joined by the time of destruction.
db_close_thread.join();

SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
Destroy(options);
}

TEST_F(DBErrorHandlingFSTest, FlushReadError) {
std::shared_ptr<ErrorHandlerFSListener> listener =
std::make_shared<ErrorHandlerFSListener>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a race between flush error recovery and db destruction that can lead to db crashing.

0 comments on commit 50fc35a

Please sign in to comment.