sapling/eden/fs/fuse/RequestData.cpp
Katie Mancini 37c264f457 make FUSE metrics more efficient
Summary:
Tl;DR Reduces costs of fuse request mertics by reducing lock contention.

D20922194 adds tracking for FUSE request metrics, this makes tracking those
metrics more efficient.  Since every user request goes through the FUSE channel,
we want to reduce the cost of these metrics as much  as possible (originally
mentioned in a comment D20922194).

The synchronization used to track the metrics is costly especially
when the lock is contended.

To reduce the cost, each FuseChannel will have a thread local copy of metrics.
Each will still be synchronized to allow for reading the metrics and for
Requests moved to other threads that will need to access the metrics. However,
the lock should be contended less often since only requests from a single fuse
channel thread will access it.

Reviewed By: chadaustin

Differential Revision: D21043792

fbshipit-source-id: ce58a0cbce334095976233bfac7578d39c81bb55
2020-04-23 10:46:16 -07:00

166 lines
4.6 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/fuse/RequestData.h"
#include <folly/Subprocess.h>
#include <folly/logging/xlog.h>
#include "eden/fs/fuse/Dispatcher.h"
#include "eden/fs/notifications/Notifications.h"
#include "eden/fs/telemetry/RequestMetricsScope.h"
#include "eden/fs/utils/SystemError.h"
using namespace folly;
using namespace std::chrono;
namespace facebook {
namespace eden {
const std::string RequestData::kKey("fuse");
RequestData::RequestData(
FuseChannel* channel,
const fuse_in_header& fuseHeader,
Dispatcher* dispatcher)
: channel_(channel), fuseHeader_(fuseHeader), dispatcher_(dispatcher) {}
bool RequestData::isFuseRequest() {
return folly::RequestContext::get()->getContextData(kKey) != nullptr;
}
RequestData& RequestData::get() {
const auto data = folly::RequestContext::get()->getContextData(kKey);
if (UNLIKELY(!data)) {
XLOG(FATAL) << "boom for missing RequestData";
throw std::runtime_error("no fuse request data set in this context!");
}
return *dynamic_cast<RequestData*>(data);
}
RequestData& RequestData::create(
FuseChannel* channel,
const fuse_in_header& fuseHeader,
Dispatcher* dispatcher) {
folly::RequestContext::get()->setContextData(
RequestData::kKey,
std::make_unique<RequestData>(channel, fuseHeader, dispatcher));
return get();
}
void RequestData::startRequest(
EdenStats* stats,
FuseThreadStats::HistogramPtr histogram,
std::shared_ptr<RequestMetricsScope::LockedRequestWatchList>&
requestWatches) {
startTime_ = steady_clock::now();
DCHECK(latencyHistogram_ == nullptr);
latencyHistogram_ = histogram;
stats_ = stats;
channelThreadLocalStats_ = requestWatches;
requestMetricsScope_ = RequestMetricsScope(channelThreadLocalStats_.get());
}
void RequestData::finishRequest() {
const auto now = steady_clock::now();
const auto now_since_epoch = duration_cast<seconds>(now.time_since_epoch());
const auto diff = now - startTime_;
const auto diff_us = duration_cast<microseconds>(diff);
const auto diff_ns = duration_cast<nanoseconds>(diff);
stats_->getFuseStatsForCurrentThread().recordLatency(
latencyHistogram_, diff_us, now_since_epoch);
latencyHistogram_ = nullptr;
stats_ = nullptr;
{ auto temp = std::move(requestMetricsScope_); }
channelThreadLocalStats_.reset();
auto& pal = channel_->getProcessAccessLog();
if (getEdenTopStats().didImportFromBackingStore()) {
auto type = ProcessAccessLog::AccessType::FuseBackingStoreImport;
pal.recordAccess(examineReq().pid, type);
}
pal.recordDuration(examineReq().pid, diff_ns);
}
fuse_in_header RequestData::stealReq() {
if (fuseHeader_.opcode == 0) {
throw std::runtime_error("req_ has been released");
}
fuse_in_header res = fuseHeader_;
fuseHeader_.opcode = 0;
return res;
}
const fuse_in_header& RequestData::getReq() const {
if (fuseHeader_.opcode == 0) {
throw std::runtime_error("req_ has been released");
}
return fuseHeader_;
}
const fuse_in_header& RequestData::examineReq() const {
// Will just return the fuseHeader_ and not throw(unlike getReq)
// The caller is responsible to check the opcode and ignore if zero
return fuseHeader_;
}
Dispatcher* RequestData::getDispatcher() const {
return dispatcher_;
}
RequestData::EdenTopStats& RequestData::getEdenTopStats() {
return edenTopStats_;
}
void RequestData::replyError(int err) {
channel_->replyError(stealReq(), err);
}
void RequestData::replyNone() {
stealReq();
}
void RequestData::systemErrorHandler(
const std::system_error& err,
Notifications* FOLLY_NULLABLE notifications) {
int errnum = EIO;
if (isErrnoError(err)) {
errnum = err.code().value();
}
XLOG(DBG5) << folly::exceptionStr(err);
RequestData::get().replyError(errnum);
if (notifications) {
notifications->showGenericErrorNotification(err);
}
}
void RequestData::genericErrorHandler(
const std::exception& err,
Notifications* FOLLY_NULLABLE notifications) {
XLOG(DBG5) << folly::exceptionStr(err);
RequestData::get().replyError(EIO);
if (notifications) {
notifications->showGenericErrorNotification(err);
}
}
void RequestData::timeoutErrorHandler(
const folly::FutureTimeout& err,
Notifications* FOLLY_NULLABLE notifications) {
XLOG_EVERY_MS(WARN, 1000)
<< "FUSE request timed out: " << folly::exceptionStr(err);
RequestData::get().replyError(ETIMEDOUT);
if (notifications) {
notifications->showGenericErrorNotification(err);
}
}
} // namespace eden
} // namespace facebook