sapling/eden/fs/telemetry/EdenStats.h
Xavier Deguillard 8b82dc96cb prjfs: make readdir asynchronous
Summary:
As of right now, opendir is the expensive callbacks due to fetching the sizes
for all the files in a directory. This strategy however breaks down when
timeouts are added as very large directories would trigger the timeout, not
because EdenFS is having a hard time reaching Mononoke, but because of
bandwidth limitation.

To avoid this issue, we need to have a per-file timeout and thus makes opendir
just trigger the futures, but not wait on them. The waiting bit will happen
at readdir time which will also enable having a timeout per file.

The one drawback to this is that the ObjectFetchContext that is passed in by
opendir cannot live long enough to be used in the size future. For now, we can
use a null context, but a proper context will need to be passed in, in the
future.

Reviewed By: wez

Differential Revision: D24895089

fbshipit-source-id: e10ceae2f7c49b4a006b15a34f85d06a2863ae3a
2020-11-13 14:27:26 -08:00

243 lines
8.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.
*/
#pragma once
#include <fb303/ThreadLocalStats.h>
#include <folly/ThreadLocal.h>
#include <memory>
#include "eden/fs/eden-config.h"
namespace facebook {
namespace eden {
class ChannelThreadStats;
class ObjectStoreThreadStats;
class HgBackingStoreThreadStats;
class HgImporterThreadStats;
class JournalThreadStats;
class EdenStats {
public:
/**
* This function can be called on any thread.
*
* The returned object can be used only on the current thread.
*/
ChannelThreadStats& getChannelStatsForCurrentThread();
/**
* This function can be called on any thread.
*
* The returned object can be used only on the current thread.
*/
ObjectStoreThreadStats& getObjectStoreStatsForCurrentThread();
/**
* This function can be called on any thread.
*
* The returned object can be used only on the current thread.
*/
HgBackingStoreThreadStats& getHgBackingStoreStatsForCurrentThread();
/**
* This function can be called on any thread.
*
* The returned object can be used only on the current thread.
*/
HgImporterThreadStats& getHgImporterStatsForCurrentThread();
/**
* This function can be called on any thread.
*
* The returned object can be used only on the current thread.
*/
JournalThreadStats& getJournalStatsForCurrentThread();
/**
* This function can be called on any thread.
*/
void aggregate();
private:
class ThreadLocalTag {};
folly::ThreadLocal<ChannelThreadStats, ThreadLocalTag, void>
threadLocalChannelStats_;
folly::ThreadLocal<ObjectStoreThreadStats, ThreadLocalTag, void>
threadLocalObjectStoreStats_;
folly::ThreadLocal<HgBackingStoreThreadStats, ThreadLocalTag, void>
threadLocalHgBackingStoreStats_;
folly::ThreadLocal<HgImporterThreadStats, ThreadLocalTag, void>
threadLocalHgImporterStats_;
folly::ThreadLocal<JournalThreadStats, ThreadLocalTag, void>
threadLocalJournalStats_;
};
std::shared_ptr<HgImporterThreadStats> getSharedHgImporterStatsForCurrentThread(
std::shared_ptr<EdenStats>);
/**
* EdenThreadStatsBase is a base class for a group of thread-local stats
* structures.
*
* Each EdenThreadStatsBase object should only be used from a single thread. The
* EdenStats object should be used to maintain one EdenThreadStatsBase object
* for each thread that needs to access/update the stats.
*/
class EdenThreadStatsBase
: public fb303::ThreadLocalStatsT<fb303::TLStatsThreadSafe> {
public:
using Histogram = TLHistogram;
using Timeseries = TLTimeseries;
explicit EdenThreadStatsBase();
protected:
Histogram createHistogram(const std::string& name);
Timeseries createTimeseries(const std::string& name);
};
class ChannelThreadStats : public EdenThreadStatsBase {
public:
// We track latency in units of microseconds, hence the _us suffix
// in the histogram names below.
#ifndef _WIN32
Histogram lookup{createHistogram("fuse.lookup_us")};
Histogram forget{createHistogram("fuse.forget_us")};
Histogram getattr{createHistogram("fuse.getattr_us")};
Histogram setattr{createHistogram("fuse.setattr_us")};
Histogram readlink{createHistogram("fuse.readlink_us")};
Histogram mknod{createHistogram("fuse.mknod_us")};
Histogram mkdir{createHistogram("fuse.mkdir_us")};
Histogram unlink{createHistogram("fuse.unlink_us")};
Histogram rmdir{createHistogram("fuse.rmdir_us")};
Histogram symlink{createHistogram("fuse.symlink_us")};
Histogram rename{createHistogram("fuse.rename_us")};
Histogram link{createHistogram("fuse.link_us")};
Histogram open{createHistogram("fuse.open_us")};
Histogram read{createHistogram("fuse.read_us")};
Histogram write{createHistogram("fuse.write_us")};
Histogram flush{createHistogram("fuse.flush_us")};
Histogram release{createHistogram("fuse.release_us")};
Histogram fsync{createHistogram("fuse.fsync_us")};
Histogram opendir{createHistogram("fuse.opendir_us")};
Histogram readdir{createHistogram("fuse.readdir_us")};
Histogram releasedir{createHistogram("fuse.releasedir_us")};
Histogram fsyncdir{createHistogram("fuse.fsyncdir_us")};
Histogram statfs{createHistogram("fuse.statfs_us")};
Histogram setxattr{createHistogram("fuse.setxattr_us")};
Histogram getxattr{createHistogram("fuse.getxattr_us")};
Histogram listxattr{createHistogram("fuse.listxattr_us")};
Histogram removexattr{createHistogram("fuse.removexattr_us")};
Histogram access{createHistogram("fuse.access_us")};
Histogram create{createHistogram("fuse.create_us")};
Histogram bmap{createHistogram("fuse.bmap_us")};
Histogram ioctl{createHistogram("fuse.ioctl_us")};
Histogram poll{createHistogram("fuse.poll_us")};
Histogram forgetmulti{createHistogram("fuse.forgetmulti_us")};
#else
Timeseries outOfOrderCreate{createTimeseries("prjfs.out_of_order_create")};
Histogram newFileCreated{createHistogram("prjfs.newFileCreated_us")};
Histogram fileOverwritten{createHistogram("prjfs.fileOverwritten_us")};
Histogram fileHandleClosedFileModified{
createHistogram("prjfs.fileHandleClosedFileModified_us")};
Histogram fileRenamed{createHistogram("prjfs.fileRenamed_us")};
Histogram preRenamed{createHistogram("prjfs.preRenamed_us")};
Histogram fileHandleClosedFileDeleted{
createHistogram("prjfs.fileHandleClosedFileDeleted_us")};
Histogram preSetHardlink{createHistogram("prjfs.preSetHardlink_us")};
Histogram openDir{createHistogram("prjfs.opendir_us")};
Histogram readDir{createHistogram("prjfs.readdir_us")};
Histogram lookup{createHistogram("prjfs.lookup_us")};
Histogram access{createHistogram("prjfs.access_us")};
Histogram read{createHistogram("prjfs.read_us")};
#endif
// Since we can potentially finish a request in a different
// thread from the one used to initiate it, we use HistogramPtr
// as a helper for referencing the pointer-to-member that we
// want to update at the end of the request.
using HistogramPtr = Histogram ChannelThreadStats::*;
/** Record a the latency for an operation.
* item is the pointer-to-member for one of the histograms defined
* above.
* elapsed is the duration of the operation, measured in microseconds.
*/
void recordLatency(HistogramPtr item, std::chrono::microseconds elapsed);
};
/**
* @see ObjectStore
*/
class ObjectStoreThreadStats : public EdenThreadStatsBase {
public:
Timeseries getBlobFromLocalStore{
createTimeseries("object_store.get_blob.local_store")};
Timeseries getBlobFromBackingStore{
createTimeseries("object_store.get_blob.backing_store")};
Timeseries getBlobMetadataFromMemory{
createTimeseries("object_store.get_blob_metadata.memory")};
Timeseries getBlobMetadataFromLocalStore{
createTimeseries("object_store.get_blob_metadata.local_store")};
Timeseries getBlobMetadataFromBackingStore{
createTimeseries("object_store.get_blob_metadata.backing_store")};
Timeseries getBlobSizeFromLocalStore{
createTimeseries("object_store.get_blob_size.local_store")};
Timeseries getBlobSizeFromBackingStore{
createTimeseries("object_store.get_blob_size.backing_store")};
};
/**
* @see HgBackingStore
*/
class HgBackingStoreThreadStats : public EdenThreadStatsBase {
public:
Histogram hgBackingStoreGetBlob{createHistogram("store.hg.get_blob")};
Histogram hgBackingStoreImportBlob{createHistogram("store.hg.import_blob")};
Histogram hgBackingStoreGetTree{createHistogram("store.hg.get_tree")};
Histogram hgBackingStoreImportTree{createHistogram("store.hg.import_tree")};
Histogram mononokeBackingStoreGetTree{
createHistogram("store.mononoke.get_tree")};
Histogram mononokeBackingStoreGetBlob{
createHistogram("store.mononoke.get_blob")};
};
/**
* @see HgImporter
* @see HgBackingStore
*/
class HgImporterThreadStats : public EdenThreadStatsBase {
public:
Timeseries catFile{createTimeseries("hg_importer.cat_file")};
Timeseries fetchTree{createTimeseries("hg_importer.fetch_tree")};
Timeseries manifest{createTimeseries("hg_importer.manifest")};
Timeseries manifestNodeForCommit{
createTimeseries("hg_importer.manifest_node_for_commit")};
Timeseries prefetchFiles{createTimeseries("hg_importer.prefetch_files")};
Timeseries catTree{createTimeseries("hg_importer.cat_tree")};
};
class JournalThreadStats : public EdenThreadStatsBase {
public:
Timeseries truncatedReads{this, "journal.truncated_reads", fb303::SUM};
Timeseries filesAccumulated{this,
"journal.files_accumulated",
fb303::COUNT,
fb303::SUM};
};
} // namespace eden
} // namespace facebook