edenapi: log unfetchable blobs and trees with EdenAPI

Summary:
Add EdenAPI fallover scuba logging to `HgBackingStore`.

The reason that this is being added to `HgBackingStore` instead of `HgQueuedBackingStore` is that we don't have good access to the actual point where trees and blobs are imported from HgImporter in there. As a result we have to get StructuredLogger in there and send events directly from there.

Reviewed By: xavierd

Differential Revision: D34217906

fbshipit-source-id: 795b61ef6a2ea52e8b67cd08633ec017e5bdbb06
This commit is contained in:
Zeyi (Rice) Fan 2022-02-28 18:07:38 -08:00 committed by Facebook GitHub Bot
parent 5bc49f102d
commit 3c6f866635
4 changed files with 63 additions and 9 deletions

View File

@ -1830,7 +1830,8 @@ shared_ptr<BackingStore> EdenServer::createBackingStore(
serverState_->getThreadPool().get(),
reloadableConfig,
getSharedStats(),
metadataImporterFactory_);
metadataImporterFactory_,
serverState_->getStructuredLogger());
return std::make_shared<LocalStoreCachedBackingStore>(
make_shared<HgQueuedBackingStore>(
localStore_,

View File

@ -39,7 +39,9 @@
#include "eden/fs/store/hg/HgProxyHash.h"
#include "eden/fs/store/hg/MetadataImporter.h"
#include "eden/fs/telemetry/EdenStats.h"
#include "eden/fs/telemetry/LogEvent.h"
#include "eden/fs/telemetry/RequestMetricsScope.h"
#include "eden/fs/telemetry/StructuredLogger.h"
#include "eden/fs/utils/Bug.h"
#include "eden/fs/utils/EnumValue.h"
#include "eden/fs/utils/UnboundedQueueExecutor.h"
@ -152,7 +154,8 @@ HgBackingStore::HgBackingStore(
UnboundedQueueExecutor* serverThreadPool,
std::shared_ptr<ReloadableConfig> config,
std::shared_ptr<EdenStats> stats,
MetadataImporterFactory metadataImporterFactory)
MetadataImporterFactory metadataImporterFactory,
std::shared_ptr<StructuredLogger> logger)
: localStore_(std::move(localStore)),
stats_(stats),
importThreadPool_(make_unique<folly::CPUThreadPoolExecutor>(
@ -174,11 +177,13 @@ HgBackingStore::HgBackingStore(
std::make_shared<HgImporterThreadFactory>(repository, stats))),
config_(config),
serverThreadPool_(serverThreadPool),
useEdenApi_(config->getEdenConfig()->useEdenApi.getValue()),
datapackStore_(
repository,
config->getEdenConfig()->useEdenApi.getValue(),
useEdenApi_,
config->getEdenConfig()->useAuxMetadata.getValue(),
config) {
config),
logger_(logger) {
HgImporter importer(repository, stats);
const auto& options = importer.getOptions();
repoName_ = options.repoName;
@ -217,7 +222,9 @@ HgBackingStore::HgBackingStore(
importThreadPool_{std::make_unique<HgImporterTestExecutor>(importer)},
config_(std::move(config)),
serverThreadPool_{importThreadPool_.get()},
datapackStore_(repository, false, false, config_) {
useEdenApi_{false},
datapackStore_(repository, false, false, config_),
logger_(nullptr) {
const auto& options = importer->getOptions();
repoName_ = options.repoName;
metadataImporter_ = metadataImporterFactory(config_, repoName_, localStore_);
@ -431,14 +438,22 @@ folly::Future<std::unique_ptr<Tree>> HgBackingStore::fetchTreeFromImporter(
auto fut =
folly::via(
importThreadPool_.get(),
[path,
[this,
path,
manifestNode,
stats = stats_,
&liveImportTreeWatches = liveImportTreeWatches_] {
Importer& importer = getThreadLocalImporter();
folly::stop_watch<std::chrono::milliseconds> watch;
RequestMetricsScope queueTracker{&liveImportTreeWatches};
if (useEdenApi_ && logger_) {
logger_->logEvent(EdenApiMiss{
repoName_,
EdenApiMiss::Tree,
path.stringPiece().toString(),
manifestNode.toString(),
});
}
auto serializedTree = importer.fetchTree(path, manifestNode);
stats->getHgBackingStoreStatsForCurrentThread()
.hgBackingStoreImportTree.addValue(watch.elapsed().count());
@ -699,12 +714,21 @@ SemiFuture<std::unique_ptr<Blob>> HgBackingStore::fetchBlobFromHgImporter(
HgProxyHash hgInfo) {
return folly::via(
importThreadPool_.get(),
[stats = stats_,
[this,
stats = stats_,
hgInfo = std::move(hgInfo),
&liveImportBlobWatches = liveImportBlobWatches_] {
Importer& importer = getThreadLocalImporter();
folly::stop_watch<std::chrono::milliseconds> watch;
RequestMetricsScope queueTracker{&liveImportBlobWatches};
if (useEdenApi_ && logger_) {
logger_->logEvent(EdenApiMiss{
repoName_,
EdenApiMiss::Blob,
hgInfo.path().stringPiece().toString(),
hgInfo.revHash().toString(),
});
}
auto blob =
importer.importFileContents(hgInfo.path(), hgInfo.revHash());
stats->getHgBackingStoreStatsForCurrentThread()

View File

@ -32,6 +32,7 @@ class LocalStore;
class UnboundedQueueExecutor;
class ReloadableConfig;
class HgProxyHash;
class StructuredLogger;
/**
* An implementation class for HgQueuedBackingStore that loads data out of a
@ -48,7 +49,8 @@ class HgBackingStore {
UnboundedQueueExecutor* serverThreadPool,
std::shared_ptr<ReloadableConfig> config,
std::shared_ptr<EdenStats> edenStats,
MetadataImporterFactory metadataImporter);
MetadataImporterFactory metadataImporter,
std::shared_ptr<StructuredLogger> logger);
/**
* Create an HgBackingStore suitable for use in unit tests. It uses an inline
@ -201,9 +203,11 @@ class HgBackingStore {
folly::Executor* serverThreadPool_;
std::string repoName_;
const bool useEdenApi_;
HgDatapackStore datapackStore_;
std::unique_ptr<MetadataImporter> metadataImporter_;
std::shared_ptr<StructuredLogger> logger_;
// Track metrics for imports currently fetching data from hg
mutable RequestMetricsScope::LockedRequestWatchList liveImportBlobWatches_;

View File

@ -244,5 +244,30 @@ struct ServerDataFetch {
}
};
struct EdenApiMiss {
enum MissType : bool {
Blob = 0,
Tree = 1,
};
static constexpr const char* type = "edenapi_miss";
std::string repo_name;
MissType miss_type;
std::string path;
std::string hash;
void populate(DynamicEvent& event) const {
event.addString("repo_source", repo_name);
if (miss_type == Blob) {
event.addString("edenapi_miss_type", "blob");
} else {
event.addString("edenapi_miss_type", "tree");
}
event.addString("path", path);
event.addString("hash", hash);
}
};
} // namespace eden
} // namespace facebook