Summary:
Failure to create the lock file (e.g. out of space) could
prevent future LockFile attempts in the same process on the same file
from succeeding.
Also added DEBUG code to fail assertion if PosixFileLock is destroyed
without using UnlockFile (which is a risk because FileLock is in the
public API with virtual destructor).
Pull Request resolved: https://github.com/facebook/rocksdb/pull/8747
Test Plan: test added
Reviewed By: ajkr
Differential Revision:
D30732543
Pulled By: pdillinger
fbshipit-source-id:
4c30a959566d91f778d6fad3fbbd5f3941b097c1
* Fix a race in DumpStats() with column family destruction due to not taking a Ref on each entry while iterating the ColumnFamilySet.
* Fix a race in item ref counting in LRUCache when promoting an item from the SecondaryCache.
* Fix a race in BackupEngine if RateLimiter is reconfigured during concurrent Restore operations.
+* Fix a bug on POSIX in which failure to create a lock file (e.g. out of space) can prevent future LockFile attempts in the same process on the same file from succeeding.
### New Features
* RemoteCompaction's interface now includes `db_name`, `db_id`, `session_id`, which could help the user uniquely identify compaction job between db instances and sessions.
#include "rocksdb/convenience.h"
#include "rocksdb/env.h"
#include "rocksdb/env_encryption.h"
+#include "rocksdb/file_system.h"
#include "rocksdb/system_clock.h"
#include "test_util/sync_point.h"
#include "test_util/testharness.h"
t.Run();
}
+TEST_F(EnvTest, FailureToCreateLockFile) {
+ auto env = Env::Default();
+ auto fs = env->GetFileSystem();
+ std::string dir = test::PerThreadDBPath(env, "lockdir");
+ std::string file = dir + "/lockfile";
+
+ // Ensure directory doesn't exist
+ ASSERT_OK(DestroyDir(env, dir));
+
+ // Make sure that we can acquire a file lock after the first attempt fails
+ FileLock* lock = nullptr;
+ ASSERT_NOK(fs->LockFile(file, IOOptions(), &lock, /*dbg*/ nullptr));
+ ASSERT_FALSE(lock);
+
+ ASSERT_OK(fs->CreateDir(dir, IOOptions(), /*dbg*/ nullptr));
+ ASSERT_OK(fs->LockFile(file, IOOptions(), &lock, /*dbg*/ nullptr));
+ ASSERT_OK(fs->UnlockFile(lock, IOOptions(), /*dbg*/ nullptr));
+
+ // Clean up
+ ASSERT_OK(DestroyDir(env, dir));
+}
+
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {
class PosixFileLock : public FileLock {
public:
- int fd_;
+ int fd_ = /*invalid*/ -1;
std::string filename;
+
+ void Clear() {
+ fd_ = -1;
+ filename.clear();
+ }
+
+ virtual ~PosixFileLock() override {
+ // Check for destruction without UnlockFile
+ assert(fd_ == -1);
+ }
};
int cloexec_flags(int flags, const EnvOptions* options) {
if (fd < 0) {
result = IOError("while open a file for lock", fname, errno);
} else if (LockOrUnlock(fd, true) == -1) {
- // if there is an error in locking, then remove the pathname from
- // lockedfiles
- locked_files.erase(fname);
result = IOError("While lock file", fname, errno);
close(fd);
} else {
my_lock->filename = fname;
*lock = my_lock;
}
+ if (!result.ok()) {
+ // If there is an error in locking, then remove the pathname from
+ // locked_files. (If we got this far, it did not exist in locked_files
+ // before this call.)
+ locked_files.erase(fname);
+ }
mutex_locked_files.Unlock();
return result;
result = IOError("unlock", my_lock->filename, errno);
}
close(my_lock->fd_);
+ my_lock->Clear();
delete my_lock;
mutex_locked_files.Unlock();
return result;
InfoLogLevel log_level_;
};
-// Identifies a locked file.
+// Identifies a locked file. Except in custom Env/Filesystem implementations,
+// the lifetime of a FileLock object should be managed only by LockFile() and
+// UnlockFile().
class FileLock {
public:
FileLock() {}