sapling/eden/fs/service/EdenStateDir.cpp
Xavier Deguillard f2e7099799 nfs: allow mountd and nfsd to bind to a unix socket on macOS
Summary:
Due to NFS being designed as a network filesystem, it default to binding on a
TCP socket. For EdenFS, since we're not expecting to mount an actual remote
filesystem, we bind these sockets to localhost. Unfortunately, TCP sockets have
some inherent overhead due to being designed to be reliable over a non-reliable
medium.

On macOS, Apple provides a way to mount an NFS server that is listening on a
unix domain socket. Thanks for unix socket being reliable, the TCP overhead
goes away leading to some higher throughput and lower latency for the NFS
server. For EdenFS, timing `rg foobar` over a directory containing 27k files gives:

NFS over TCP:
rg foobar > /dev/null  0.80s user 5.44s system 567% cpu 1.100 total

NFS over UDS:
rg foobar > /dev/null  0.77s user 5.27s system 679% cpu 0.888 total

osxfuse:
rg foobar > /dev/null  0.87s user 5.59s system 662% cpu 0.975 total

The main drawback of going through a unix socket is that D27717945 (bcf6aa465c) appears to
no longer be effective due to
8f02f2a044/bsd/nfs/nfs_vfsops.c (L3739)

Reviewed By: kmancini

Differential Revision: D28261422

fbshipit-source-id: 25dc1dc78cdb50d6c6550a86ef01ea2c894c110f
2021-05-12 13:06:57 -07:00

126 lines
3.7 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#include "eden/fs/service/EdenStateDir.h"
#include <folly/FileUtil.h>
#include <folly/logging/xlog.h>
using folly::StringPiece;
namespace facebook {
namespace eden {
namespace {
constexpr StringPiece kLockFileName{"lock"};
constexpr StringPiece kPidFileName{"pid"};
constexpr StringPiece kTakeoverSocketName{"takeover"};
constexpr StringPiece kThriftSocketName{"socket"};
constexpr PathComponentPiece kMountdSocketName{"mountd.socket"_pc};
} // namespace
EdenStateDir::EdenStateDir(AbsolutePathPiece path)
: path_(path), lockPath_(path + PathComponentPiece(kLockFileName)) {}
EdenStateDir::~EdenStateDir() {}
bool EdenStateDir::acquireLock() {
auto lockFile =
folly::File(lockPath_.value(), O_WRONLY | O_CREAT | O_CLOEXEC);
if (!lockFile.try_lock()) {
return false;
}
takeoverLock(std::move(lockFile));
return true;
}
void EdenStateDir::takeoverLock(folly::File lockFile) {
writePidToLockFile(lockFile);
int rc = fstat(lockFile.fd(), &lockFileStat_);
folly::checkUnixError(rc, "error getting lock file attributes");
lockFile_ = std::move(lockFile);
// On Windows, also write the pid to a separate file.
// Other processes cannot read the lock file while we are holding the lock,
// so we store it in a separate file too.
auto pidFilePath = path_ + PathComponentPiece(kPidFileName);
folly::File pidFile(pidFilePath.c_str(), O_WRONLY | O_CREAT, 0644);
writePidToLockFile(pidFile);
}
folly::File EdenStateDir::extractLock() {
return std::move(lockFile_);
}
void EdenStateDir::writePidToLockFile(folly::File& lockFile) {
// Write the PID (with a newline) to the lockfile.
folly::ftruncateNoInt(lockFile.fd(), /* len */ 0);
const auto pidContents = folly::to<std::string>(getpid(), "\n");
folly::pwriteNoInt(lockFile.fd(), pidContents.data(), pidContents.size(), 0);
}
bool EdenStateDir::isLocked() const {
// We only set lockFile_ once we have locked it,
// so as long as this is set we have the lock.
return bool(lockFile_);
}
bool EdenStateDir::isLockValid() const {
if (!lockFile_) {
return false;
}
// The st_dev and st_ino fields aren't valid on Windows, so skip the check
// to see if the lock file is still valid. Assume that if we acquired it
// initially it is still valid.
//
// Windows generally makes it harder for users to delete or rename the
// directory out from under an existing process while we have file handles
// open, so this check isn't really as necessary.
#ifndef _WIN32
struct stat st;
int rc = stat(lockPath_.c_str(), &st);
if (rc != 0) {
int errnum = errno;
XLOG(ERR) << "EdenFS lock file no longer appears valid: "
"failed to stat lock file: "
<< folly::errnoStr(errnum);
return false;
}
bool isSameFile =
(st.st_dev == lockFileStat_.st_dev && st.st_ino == lockFileStat_.st_ino);
if (!isSameFile) {
XLOG(ERR) << "EdenFS lock file no longer appears valid: "
"file has been replaced";
return false;
}
#endif
return true;
}
AbsolutePath EdenStateDir::getThriftSocketPath() const {
return path_ + PathComponentPiece{kThriftSocketName};
}
AbsolutePath EdenStateDir::getTakeoverSocketPath() const {
return path_ + PathComponentPiece{kTakeoverSocketName};
}
AbsolutePath EdenStateDir::getMountdSocketPath() const {
return path_ + kMountdSocketName;
}
AbsolutePath EdenStateDir::getCheckoutStateDir(StringPiece checkoutID) const {
return path_ + PathComponent("clients") + PathComponent(checkoutID);
}
} // namespace eden
} // namespace facebook