1f32dc7d2b
Summary: Rebased and resubmitting #1831 on behalf of stevelittle. The problem is when a single process attempts to open the same DB twice, the second attempt fails due to LOCK file held. If the second attempt had opened the LOCK file, it'll now need to close it, and closing causes the file to be unlocked. Then, any subsequent attempt to open the DB will succeed, which is the wrong behavior. The solution was to track which files a process has locked in PosixEnv, and check those before opening a LOCK file. Fixes #1780. Closes https://github.com/facebook/rocksdb/pull/3993 Differential Revision: D8398984 Pulled By: ajkr fbshipit-source-id: 2755fe66950a0c9de63075f932f9e15768041918
142 lines
3.3 KiB
C++
142 lines
3.3 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 <vector>
|
|
#include <fcntl.h>
|
|
#include "util/coding.h"
|
|
#include "util/testharness.h"
|
|
|
|
namespace rocksdb {
|
|
|
|
class LockTest : public testing::Test {
|
|
public:
|
|
static LockTest* current_;
|
|
std::string file_;
|
|
rocksdb::Env* env_;
|
|
|
|
LockTest() : file_(test::TmpDir() + "/db_testlock_file"),
|
|
env_(rocksdb::Env::Default()) {
|
|
current_ = this;
|
|
}
|
|
|
|
~LockTest() {
|
|
}
|
|
|
|
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.
|
|
ASSERT_TRUE(LockFile(&lock2).IsIOError());
|
|
|
|
// 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
|
|
|
|
int main(int argc, char** argv) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|