331e6199df
Summary: When users fail to open a DB with file lock failure, it is sometimes hard for users to debug. We now include the time the lock is acquired and the thread ID that acquired the lock, to help users debug problems like this. Default Env's thread ID is used. Since type of lockedFiles is changed, rename it to follow naming convention too. Pull Request resolved: https://github.com/facebook/rocksdb/pull/6507 Test Plan: Add a unit test and improve an existing test to validate the case. Differential Revision: D20378333 fbshipit-source-id: 312fe0e9733fd1d1e9969c321b90ce523cf4708a
147 lines
3.6 KiB
C++
147 lines
3.6 KiB
C++
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
// (found in the LICENSE.Apache file in the root directory).
|
|
//
|
|
#include "rocksdb/status.h"
|
|
#include "rocksdb/env.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <vector>
|
|
#include "test_util/testharness.h"
|
|
#include "util/coding.h"
|
|
#include "util/string_util.h"
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
|
|
class LockTest : public testing::Test {
|
|
public:
|
|
static LockTest* current_;
|
|
std::string file_;
|
|
ROCKSDB_NAMESPACE::Env* env_;
|
|
|
|
LockTest()
|
|
: file_(test::PerThreadDBPath("db_testlock_file")),
|
|
env_(ROCKSDB_NAMESPACE::Env::Default()) {
|
|
current_ = this;
|
|
}
|
|
|
|
~LockTest() override {}
|
|
|
|
Status LockFile(FileLock** db_lock) {
|
|
return env_->LockFile(file_, db_lock);
|
|
}
|
|
|
|
Status UnlockFile(FileLock* db_lock) {
|
|
return env_->UnlockFile(db_lock);
|
|
}
|
|
|
|
bool AssertFileIsLocked(){
|
|
return CheckFileLock( /* lock_expected = */ true);
|
|
}
|
|
|
|
bool AssertFileIsNotLocked(){
|
|
return CheckFileLock( /* lock_expected = */ false);
|
|
}
|
|
|
|
bool CheckFileLock(bool lock_expected){
|
|
// We need to fork to check the fcntl lock as we need
|
|
// to open and close the file from a different process
|
|
// to avoid either releasing the lock on close, or not
|
|
// contending for it when requesting a lock.
|
|
|
|
#ifdef OS_WIN
|
|
|
|
// WaitForSingleObject and GetExitCodeProcess can do what waitpid does.
|
|
// TODO - implement on Windows
|
|
return true;
|
|
|
|
#else
|
|
|
|
pid_t pid = fork();
|
|
if ( 0 == pid ) {
|
|
// child process
|
|
int exit_val = EXIT_FAILURE;
|
|
int fd = open(file_.c_str(), O_RDWR | O_CREAT, 0644);
|
|
if (fd < 0) {
|
|
// could not open file, could not check if it was locked
|
|
fprintf( stderr, "Open on on file %s failed.\n",file_.c_str());
|
|
exit(exit_val);
|
|
}
|
|
|
|
struct flock f;
|
|
memset(&f, 0, sizeof(f));
|
|
f.l_type = (F_WRLCK);
|
|
f.l_whence = SEEK_SET;
|
|
f.l_start = 0;
|
|
f.l_len = 0; // Lock/unlock entire file
|
|
int value = fcntl(fd, F_SETLK, &f);
|
|
if( value == -1 ){
|
|
if( lock_expected ){
|
|
exit_val = EXIT_SUCCESS;
|
|
}
|
|
} else {
|
|
if( ! lock_expected ){
|
|
exit_val = EXIT_SUCCESS;
|
|
}
|
|
}
|
|
close(fd); // lock is released for child process
|
|
exit(exit_val);
|
|
} else if (pid > 0) {
|
|
// parent process
|
|
int status;
|
|
while (-1 == waitpid(pid, &status, 0));
|
|
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
|
// child process exited with non success status
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
} else {
|
|
fprintf( stderr, "Fork failed\n" );
|
|
return false;
|
|
}
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
};
|
|
LockTest* LockTest::current_;
|
|
|
|
TEST_F(LockTest, LockBySameThread) {
|
|
FileLock* lock1;
|
|
FileLock* lock2;
|
|
|
|
// acquire a lock on a file
|
|
ASSERT_OK(LockFile(&lock1));
|
|
|
|
// check the file is locked
|
|
ASSERT_TRUE( AssertFileIsLocked() );
|
|
|
|
// re-acquire the lock on the same file. This should fail.
|
|
Status s = LockFile(&lock2);
|
|
ASSERT_TRUE(s.IsIOError());
|
|
// Validate that error message contains current thread ID.
|
|
ASSERT_TRUE(s.ToString().find(ToString(Env::Default()->GetThreadID())) !=
|
|
std::string::npos);
|
|
|
|
// check the file is locked
|
|
ASSERT_TRUE( AssertFileIsLocked() );
|
|
|
|
// release the lock
|
|
ASSERT_OK(UnlockFile(lock1));
|
|
|
|
// check the file is not locked
|
|
ASSERT_TRUE( AssertFileIsNotLocked() );
|
|
|
|
}
|
|
|
|
} // namespace ROCKSDB_NAMESPACE
|
|
|
|
int main(int argc, char** argv) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|