Skip to content

Commit 8eb72c4

Browse files
xavierdfacebook-github-bot
authored andcommitted
store: allow batch of aux data to be fetched
Summary: Nothing is being enqueued just yet, but this teaches EdenFS how to fetch batches of aux data. In the case where the aux data cannot be fetched, a nullptr auxdata is returned to the higher level which will then trigger a blob fetch to compute aux data. Reviewed By: chadaustin Differential Revision: D44110104 fbshipit-source-id: 53df1496addf3a9dae521ffcdba5b060b8fce16a
1 parent ecc972b commit 8eb72c4

File tree

7 files changed

+215
-1
lines changed

7 files changed

+215
-1
lines changed

eden/fs/config/EdenConfig.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,14 @@ class EdenConfig : private ConfigSettingManager {
686686
*/
687687
ConfigSetting<bool> hgBlobFetchFallback{"hg:blob-fetch-fallback", true, this};
688688

689+
/**
690+
* Whether fetching blob metadata should fall back to fetching blobs.
691+
*/
692+
ConfigSetting<bool> hgBlobMetaFetchFallback{
693+
"hg:blobmeta-fetch-fallback",
694+
true,
695+
this};
696+
689697
// [backingstore]
690698

691699
/**

eden/fs/store/BackingStore.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,21 @@ class BackingStore : public RootIdCodec, public ObjectIdCodec {
120120
const ObjectId& id,
121121
const ObjectFetchContextPtr& context) = 0;
122122

123+
/**
124+
* Return value of the getBlobMetadata method.
125+
*/
126+
struct GetBlobMetaResult {
127+
/**
128+
* The retrieved blob metadata.
129+
*
130+
* Setting this to a nullptr will make the ObjectStore fallback to fetching
131+
* the blob and computing the blob metadata from it.
132+
*/
133+
std::unique_ptr<BlobMetadata> blobMeta;
134+
/** The fetch origin of the blob metadata. */
135+
ObjectFetchContext::Origin origin;
136+
};
137+
123138
/**
124139
* Fetch blob metadata if available in a local cache. Returns nullptr if not
125140
* locally available.

eden/fs/store/hg/HgDatapackStore.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,55 @@ std::unique_ptr<BlobMetadata> HgDatapackStore::getLocalBlobMetadata(
258258
return nullptr;
259259
}
260260

261+
void HgDatapackStore::getBlobMetadataBatch(
262+
const std::vector<std::shared_ptr<HgImportRequest>>& importRequests) {
263+
size_t count = importRequests.size();
264+
265+
std::vector<sapling::NodeId> requests;
266+
requests.reserve(count);
267+
for (const auto& importRequest : importRequests) {
268+
requests.emplace_back(
269+
importRequest->getRequest<HgImportRequest::BlobMetaImport>()
270+
->proxyHash.byteHash());
271+
}
272+
273+
std::vector<RequestMetricsScope> requestsWatches;
274+
requestsWatches.reserve(count);
275+
for (auto i = 0ul; i < count; i++) {
276+
requestsWatches.emplace_back(&liveBatchedBlobMetaWatches_);
277+
}
278+
279+
store_.getBlobMetadataBatch(
280+
folly::range(requests),
281+
false,
282+
[&](size_t index,
283+
folly::Try<std::shared_ptr<sapling::FileAuxData>> auxTry) {
284+
if (auxTry.hasException() &&
285+
config_->getEdenConfig()->hgBlobMetaFetchFallback.getValue()) {
286+
// The caller will fallback to fetching the blob.
287+
// TODO: Remove this.
288+
return;
289+
}
290+
291+
XLOGF(DBG9, "Imported aux={}", folly::hexlify(requests[index]));
292+
auto& importRequest = importRequests[index];
293+
importRequest->getPromise<std::unique_ptr<BlobMetadata>>()->setWith(
294+
[&]() -> folly::Try<std::unique_ptr<BlobMetadata>> {
295+
if (auxTry.hasException()) {
296+
return folly::Try<std::unique_ptr<BlobMetadata>>{
297+
std::move(auxTry).exception()};
298+
}
299+
300+
auto& aux = auxTry.value();
301+
return folly::Try{std::make_unique<BlobMetadata>(
302+
Hash20{aux->content_sha1}, aux->total_size)};
303+
});
304+
305+
// Make sure that we're stopping this watch.
306+
requestsWatches[index].reset();
307+
});
308+
}
309+
261310
void HgDatapackStore::flush() {
262311
store_.flush();
263312
}

eden/fs/store/hg/HgDatapackStore.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ class HgDatapackStore {
7272
*/
7373
std::unique_ptr<BlobMetadata> getLocalBlobMetadata(const HgProxyHash& id);
7474

75+
/**
76+
* Fetch multiple aux data at once.
77+
*
78+
* This function returns when all the aux data have been fetched.
79+
*/
80+
void getBlobMetadataBatch(
81+
const std::vector<std::shared_ptr<HgImportRequest>>& requests);
82+
7583
/**
7684
* Flush any pending writes to disk.
7785
*

eden/fs/store/hg/HgQueuedBackingStore.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,44 @@ void HgQueuedBackingStore::processTreeImportRequests(
242242
}
243243
}
244244

245+
void HgQueuedBackingStore::processBlobMetaImportRequests(
246+
std::vector<std::shared_ptr<HgImportRequest>>&& requests) {
247+
folly::stop_watch<std::chrono::milliseconds> watch;
248+
249+
for (auto& request : requests) {
250+
auto* blobMetaImport =
251+
request->getRequest<HgImportRequest::BlobMetaImport>();
252+
253+
traceBus_->publish(HgImportTraceEvent::start(
254+
request->getUnique(),
255+
HgImportTraceEvent::BLOBMETA,
256+
blobMetaImport->proxyHash,
257+
request->getPriority().getClass(),
258+
request->getCause()));
259+
260+
XLOGF(DBG4, "Processing blob meta request for {}", blobMetaImport->hash);
261+
}
262+
263+
backingStore_->getDatapackStore().getBlobMetadataBatch(requests);
264+
265+
{
266+
for (auto& request : requests) {
267+
auto* promise = request->getPromise<std::unique_ptr<BlobMetadata>>();
268+
if (promise->isFulfilled()) {
269+
stats_->addDuration(
270+
&HgBackingStoreStats::fetchBlobMetadata, watch.elapsed());
271+
continue;
272+
}
273+
274+
// The code waiting on the promise will fallback to fetching the Blob to
275+
// compute the blob metadata. We can't trigger a blob fetch here without
276+
// the risk of running into a deadlock: if all import thread are in this
277+
// code path, there are no free importer to fetch blobs.
278+
promise->setValue(nullptr);
279+
}
280+
}
281+
}
282+
245283
void HgQueuedBackingStore::processRequest() {
246284
folly::setThreadName("hgqueue");
247285
for (;;) {
@@ -257,6 +295,8 @@ void HgQueuedBackingStore::processRequest() {
257295
processBlobImportRequests(std::move(requests));
258296
} else if (first->isType<HgImportRequest::TreeImport>()) {
259297
processTreeImportRequests(std::move(requests));
298+
} else if (first->isType<HgImportRequest::BlobMetaImport>()) {
299+
processBlobMetaImportRequests(std::move(requests));
260300
}
261301
}
262302
}
@@ -523,6 +563,84 @@ HgQueuedBackingStore::getBlobImpl(
523563
});
524564
}
525565

566+
folly::SemiFuture<BackingStore::GetBlobMetaResult>
567+
HgQueuedBackingStore::getBlobMetadata(
568+
const ObjectId& id,
569+
const ObjectFetchContextPtr& context) {
570+
DurationScope scope{stats_, &HgBackingStoreStats::getBlobMetadata};
571+
572+
HgProxyHash proxyHash;
573+
try {
574+
proxyHash =
575+
HgProxyHash::load(localStore_.get(), id, "getBlobMetadata", *stats_);
576+
} catch (const std::exception&) {
577+
logMissingProxyHash();
578+
throw;
579+
}
580+
581+
logBackingStoreFetch(
582+
*context,
583+
folly::Range{&proxyHash, 1},
584+
ObjectFetchContext::ObjectType::BlobMetadata);
585+
586+
if (auto metadata =
587+
backingStore_->getDatapackStore().getLocalBlobMetadata(proxyHash)) {
588+
stats_->increment(&ObjectStoreStats::getLocalBlobMetadataFromBackingStore);
589+
return folly::makeSemiFuture(GetBlobMetaResult{
590+
std::move(metadata), ObjectFetchContext::Origin::FromDiskCache});
591+
}
592+
593+
return getBlobMetadataImpl(id, proxyHash, context)
594+
.deferEnsure([scope = std::move(scope)] {});
595+
}
596+
597+
folly::SemiFuture<BackingStore::GetBlobMetaResult>
598+
HgQueuedBackingStore::getBlobMetadataImpl(
599+
const ObjectId& id,
600+
const HgProxyHash& proxyHash,
601+
const ObjectFetchContextPtr& context) {
602+
auto getBlobMetaFuture = folly::makeFutureWith([&] {
603+
XLOG(DBG4) << "make blob meta import request for " << proxyHash.path()
604+
<< ", hash is:" << id;
605+
606+
auto request = HgImportRequest::makeBlobMetaImportRequest(
607+
id, proxyHash, context->getPriority(), context->getCause());
608+
auto unique = request->getUnique();
609+
610+
auto importTracker =
611+
std::make_unique<RequestMetricsScope>(&pendingImportBlobMetaWatches_);
612+
traceBus_->publish(HgImportTraceEvent::queue(
613+
unique,
614+
HgImportTraceEvent::BLOBMETA,
615+
proxyHash,
616+
context->getPriority().getClass(),
617+
context->getCause()));
618+
619+
return queue_.enqueueBlobMeta(std::move(request))
620+
.ensure([this,
621+
unique,
622+
proxyHash,
623+
context = context.copy(),
624+
importTracker = std::move(importTracker)]() {
625+
traceBus_->publish(HgImportTraceEvent::finish(
626+
unique,
627+
HgImportTraceEvent::BLOBMETA,
628+
proxyHash,
629+
context->getPriority().getClass(),
630+
context->getCause()));
631+
});
632+
});
633+
634+
return std::move(getBlobMetaFuture)
635+
.thenTry([this, id](folly::Try<std::unique_ptr<BlobMetadata>>&& result) {
636+
this->queue_.markImportAsFinished<BlobMetadata>(id, result);
637+
auto blobMeta = std::move(result).value();
638+
stats_->increment(&ObjectStoreStats::getBlobMetadataFromBackingStore);
639+
return GetBlobMetaResult{
640+
std::move(blobMeta), ObjectFetchContext::Origin::FromNetworkFetch};
641+
});
642+
}
643+
526644
ImmediateFuture<std::unique_ptr<Tree>> HgQueuedBackingStore::getRootTree(
527645
const RootId& rootId,
528646
const ObjectFetchContextPtr& /*context*/) {

eden/fs/store/hg/HgQueuedBackingStore.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ class HgQueuedBackingStore final : public BackingStore {
171171
folly::SemiFuture<GetBlobResult> getBlob(
172172
const ObjectId& id,
173173
const ObjectFetchContextPtr& context) override;
174+
folly::SemiFuture<GetBlobMetaResult> getBlobMetadata(
175+
const ObjectId& id,
176+
const ObjectFetchContextPtr& context);
174177

175178
FOLLY_NODISCARD virtual folly::SemiFuture<folly::Unit> prefetchBlobs(
176179
ObjectIdRange ids,
@@ -218,7 +221,7 @@ class HgQueuedBackingStore final : public BackingStore {
218221
std::vector<std::shared_ptr<HgImportRequest>>&& requests);
219222
void processTreeImportRequests(
220223
std::vector<std::shared_ptr<HgImportRequest>>&& requests);
221-
void processPrefetchRequests(
224+
void processBlobMetaImportRequests(
222225
std::vector<std::shared_ptr<HgImportRequest>>&& requests);
223226

224227
/**
@@ -244,6 +247,18 @@ class HgQueuedBackingStore final : public BackingStore {
244247
const ObjectId& id,
245248
const ObjectFetchContextPtr& context) override;
246249

250+
/**
251+
* Fetch the blob metadata from Mercurial.
252+
*
253+
* For latency sensitive context, the caller is responsible for checking if
254+
* the blob metadata is present locally, as this function will always push
255+
* the request at the end of the queue.
256+
*/
257+
folly::SemiFuture<GetBlobMetaResult> getBlobMetadataImpl(
258+
const ObjectId& id,
259+
const HgProxyHash& proxyHash,
260+
const ObjectFetchContextPtr& context);
261+
247262
/**
248263
* Fetch a tree from Mercurial.
249264
*

eden/fs/telemetry/EdenStats.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ struct HgBackingStoreStats : StatsGroup<HgBackingStoreStats> {
312312
Duration fetchBlob{"store.hg.fetch_blob_us"};
313313
Duration importBlob{"store.hg.import_blob_us"};
314314
Duration getBlobMetadata{"store.hg.get_blob_metadata_us"};
315+
Duration fetchBlobMetadata{"store.hg.get_blob_metadata_us"};
315316
Counter loadProxyHash{"store.hg.load_proxy_hash"};
316317
Counter auxMetadataMiss{"store.hg.aux_metadata_miss"};
317318
};

0 commit comments

Comments
 (0)