Repair DBs with trailing slash in name

Summary:
Problem:

- `DB::SanitizeOptions` strips trailing slash from `wal_dir` but not `dbname`
- We check whether `wal_dir` and `dbname` refer to the same directory using string equality: https://github.com/facebook/rocksdb/blob/master/db/repair.cc#L258
- Providing `dbname` with trailing slash causes default `wal_dir` to be misidentified as a separate directory.
- Then the repair tries to add all SST files to the `VersionEdit` twice (once for `dbname` dir, once for `wal_dir`) and fails with coredump.

Solution:

- Add a new `Env` function, `AreFilesSame`, which uses device and inode number to check whether files are the same. It's currently only implemented in `PosixEnv`.
- Migrate repair to use `AreFilesSame` to check whether `dbname` and `wal_dir` are same. If unsupported, falls back to string comparison.
Closes https://github.com/facebook/rocksdb/pull/2827

Differential Revision: D5761349

Pulled By: ajkr

fbshipit-source-id: c839d548678b742af1166d60b09abd94e5476238
This commit is contained in:
Andrew Kryczka 2017-09-22 12:37:59 -07:00 committed by Facebook Github Bot
parent fc7476bec1
commit 4708a6875c
4 changed files with 62 additions and 5 deletions

View File

@ -254,14 +254,21 @@ class Repairer {
} }
// search wal_dir if user uses a customize wal_dir // search wal_dir if user uses a customize wal_dir
if (!db_options_.wal_dir.empty() && bool same = false;
db_options_.wal_dir != dbname_) { Status status = env_->AreFilesSame(db_options_.wal_dir, dbname_, &same);
if (status.IsNotSupported()) {
same = db_options_.wal_dir == dbname_;
status = Status::OK();
} else if (!status.ok()) {
return status;
}
if (!same) {
to_search_paths.push_back(db_options_.wal_dir); to_search_paths.push_back(db_options_.wal_dir);
} }
for (size_t path_id = 0; path_id < to_search_paths.size(); path_id++) { for (size_t path_id = 0; path_id < to_search_paths.size(); path_id++) {
Status status = status = env_->GetChildren(to_search_paths[path_id], &filenames);
env_->GetChildren(to_search_paths[path_id], &filenames);
if (!status.ok()) { if (!status.ok()) {
return status; return status;
} }

View File

@ -309,6 +309,25 @@ TEST_F(RepairTest, RepairColumnFamilyOptions) {
} }
} }
TEST_F(RepairTest, DbNameContainsTrailingSlash) {
{
bool tmp;
if (env_->AreFilesSame("", "", &tmp).IsNotSupported()) {
fprintf(stderr,
"skipping RepairTest.DbNameContainsTrailingSlash due to "
"unsupported Env::AreFilesSame\n");
return;
}
}
Put("key", "val");
Flush();
Close();
ASSERT_OK(RepairDB(dbname_ + "/", CurrentOptions()));
Reopen(CurrentOptions());
ASSERT_EQ(Get("key"), "val");
}
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE
} // namespace rocksdb } // namespace rocksdb

21
env/env_posix.cc vendored
View File

@ -23,6 +23,7 @@
#ifdef OS_LINUX #ifdef OS_LINUX
#include <sys/statfs.h> #include <sys/statfs.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/sysmacros.h>
#endif #endif
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
@ -592,6 +593,26 @@ class PosixEnv : public Env {
return result; return result;
} }
virtual Status AreFilesSame(const std::string& first,
const std::string& second, bool* res) override {
struct stat statbuf[2];
if (stat(first.c_str(), &statbuf[0]) != 0) {
return IOError("stat file", first, errno);
}
if (stat(second.c_str(), &statbuf[1]) != 0) {
return IOError("stat file", second, errno);
}
if (major(statbuf[0].st_dev) != major(statbuf[1].st_dev) ||
minor(statbuf[0].st_dev) != minor(statbuf[1].st_dev) ||
statbuf[0].st_ino != statbuf[1].st_ino) {
*res = false;
} else {
*res = true;
}
return Status::OK();
}
virtual Status LockFile(const std::string& fname, FileLock** lock) override { virtual Status LockFile(const std::string& fname, FileLock** lock) override {
*lock = nullptr; *lock = nullptr;
Status result; Status result;

View File

@ -261,6 +261,11 @@ class Env {
return Status::NotSupported("LinkFile is not supported for this Env"); return Status::NotSupported("LinkFile is not supported for this Env");
} }
virtual Status AreFilesSame(const std::string& first,
const std::string& second, bool* res) {
return Status::NotSupported("AreFilesSame is not supported for this Env");
}
// Lock the specified file. Used to prevent concurrent access to // Lock the specified file. Used to prevent concurrent access to
// the same db by multiple processes. On failure, stores nullptr in // the same db by multiple processes. On failure, stores nullptr in
// *lock and returns non-OK. // *lock and returns non-OK.
@ -980,6 +985,11 @@ class EnvWrapper : public Env {
return target_->LinkFile(s, t); return target_->LinkFile(s, t);
} }
Status AreFilesSame(const std::string& first, const std::string& second,
bool* res) override {
return target_->AreFilesSame(first, second, res);
}
Status LockFile(const std::string& f, FileLock** l) override { Status LockFile(const std::string& f, FileLock** l) override {
return target_->LockFile(f, l); return target_->LockFile(f, l);
} }