deprioiritize when fetch count exceeds threshold

Summary:
check fetch count before `getPriority()` is used. If fetch count has exceeded `fetchThreshold_`, lower the priority by 1.

Note: this diff only guarantees that the `getPriority()` function is returning the lowered priority. How the returned value is used for scheduling is handled by `HgQueuedBackingStore`

Reviewed By: kmancini

Differential Revision: D22550640

fbshipit-source-id: c032f8f72ca658618ac118dfb3ad3dcae61e9735
This commit is contained in:
Ailin Zhang 2020-07-24 08:21:52 -07:00 committed by Facebook GitHub Bot
parent 102d8586cc
commit ce28ec8caa
5 changed files with 73 additions and 0 deletions

View File

@ -116,6 +116,16 @@ class RequestData : public folly::RequestData, public ObjectFetchContext {
return priority_;
}
// Override of `deprioritize`
virtual void deprioritize(uint64_t delta) override {
ImportPriority prev = priority_.load();
priority_.compare_exchange_strong(prev, prev.getDeprioritized(delta));
if (getClientPid().has_value()) {
XLOG(DBG7) << "priority for " << getClientPid().value()
<< " has changed to: " << priority_.load().value();
}
}
// Override of `ObjectFetchContext`
Cause getCause() const override {
return ObjectFetchContext::Cause::Fuse;

View File

@ -44,6 +44,16 @@ struct ImportPriority {
offset;
}
/**
* Deprioritize ImportPriority by decreasing offset by delta.
* Note: this function maintains ImportPriorityKind, as jobs
* with higher priority kind are usually designed to be scheduled
* ealier and should not lower their kind even when deprioritized.
*/
constexpr ImportPriority getDeprioritized(uint64_t delta) const noexcept {
return ImportPriority{kind, offset > delta ? offset - delta : 0};
}
friend bool operator<(
const ImportPriority& lhs,
const ImportPriority& rhs) noexcept {

View File

@ -66,6 +66,18 @@ class ObjectFetchContext {
return ImportPriority::kNormal();
}
/**
* Support deprioritizing in sub-classes.
* Note: Normally, each ObjectFetchContext is designed to be used for only one
* import (with NullObjectFetchContext being the only exception currenly).
* Therefore, this method should only be called once on each
* ObjectFetchContext object (when it is related to a process doing too much
* fetches). However, implementations of this method should write the priority
* change to log as debug information and watch out for unexpected uses of
* ObjectFetchContext that cause it to be used for more than one import.
*/
virtual void deprioritize(uint64_t) {}
/**
* Return a no-op fetch context suitable when no tracking is desired.
*/

View File

@ -80,6 +80,17 @@ void ObjectStore::sendFetchHeavyEvent(pid_t pid, uint64_t fetch_count) const {
#endif
}
void ObjectStore::deprioritizeWhenFetchHeavy(
ObjectFetchContext& context) const {
auto pid = context.getClientPid();
if (pid.has_value()) {
auto fetch_count = pidFetchCounts_->getCountByPid(pid.value());
if (fetch_count >= fetchThreshold_) {
context.deprioritize(importPriorityDeprioritizeAmount);
}
}
}
Future<shared_ptr<const Tree>> ObjectStore::getTree(
const Hash& id,
ObjectFetchContext& fetchContext) const {
@ -104,6 +115,8 @@ Future<shared_ptr<const Tree>> ObjectStore::getTree(
return makeFuture(std::move(tree));
}
self->deprioritizeWhenFetchHeavy(fetchContext);
// Note: We don't currently have logic here to avoid duplicate work if
// multiple callers request the same tree at once. We could store a map
// of pending lookups as (Hash --> std::list<Promise<unique_ptr<Tree>>),
@ -231,6 +244,8 @@ Future<shared_ptr<const Blob>> ObjectStore::getBlob(
return makeFuture(shared_ptr<const Blob>(std::move(blob)));
}
self->deprioritizeWhenFetchHeavy(fetchContext);
// Look in the BackingStore
return self->backingStore_
->getBlob(id, fetchContext, fetchContext.getPriority())
@ -319,6 +334,8 @@ Future<BlobMetadata> ObjectStore::getBlobMetadata(
return makeFuture(*metadata);
}
self->deprioritizeWhenFetchHeavy(context);
// Check backing store
//
// TODO: It would be nice to add a smarter API to the BackingStore so

View File

@ -47,8 +47,20 @@ struct PidFetchCounts {
void clear() {
map_.wlock()->clear();
}
uint64_t getCountByPid(pid_t pid) {
auto rl = map_.rlock();
auto fetch_count = rl->find(pid);
if (fetch_count != rl->end()) {
return fetch_count->second;
} else {
return 0;
}
}
};
constexpr uint64_t importPriorityDeprioritizeAmount{1};
/**
* ObjectStore is a content-addressed store for eden object data.
*
@ -78,6 +90,18 @@ class ObjectStore : public IObjectStore,
*/
void sendFetchHeavyEvent(pid_t pid, uint64_t fetch_count) const;
/**
* Check fetch count of the process using this fetchContext before using
* the fetchContext in BackingStore. if fetchThreshold_ is exceeded,
* deprioritize the fetchContext by 1.
*
* Note: Normally, one fetchContext is created for only one fetch request,
* so deprioritize() should only be called once by one thread, but that is
* not strictly guaranteed. See comments before deprioritize() for more
* information
*/
void deprioritizeWhenFetchHeavy(ObjectFetchContext& context) const;
/**
* Get a Tree by ID.
*