[core] Introduce Resource::minimumUpdateInterval

Introduce `Resource::minimumUpdateInterval` and consider it in the online file source.

The `minimumUpdateInterval` is used to throttle the requests, which were initiated due to resource expiration.
This commit is contained in:
Mikhail Pozdnyakov
2020-04-17 14:27:46 +03:00
parent ee1d700a9b
commit 64cbc9911d
3 changed files with 35 additions and 29 deletions

View File

@@ -95,6 +95,7 @@ public:
optional<Timestamp> priorExpires = {};
optional<std::string> priorEtag = {};
std::shared_ptr<const std::string> priorData;
Duration minimumUpdateInterval{Duration::zero()};
};
inline bool Resource::hasLoadingMethod(Resource::LoadingMethod method) const {

View File

@@ -40,14 +40,15 @@ struct OnlineFileRequest {
~OnlineFileRequest();
void networkIsReachableAgain();
void schedule();
void schedule(optional<Timestamp> expires);
void activate();
void schedule(Duration timeout);
void completed(Response);
void setTransformedURL(const std::string& url);
ActorRef<OnlineFileRequest> actor();
void onCancel(std::function<void()>);
Duration getUpdateInterval(optional<Timestamp> expires) const;
OnlineFileSourceThread& impl;
Resource resource;
std::unique_ptr<AsyncRequest> request;
@@ -100,7 +101,7 @@ public:
ref.invoke(&OnlineFileRequest::setTransformedURL, url);
});
} else {
req->schedule();
req->activate();
}
}
@@ -176,11 +177,11 @@ public:
maximumConcurrentRequests = maximumConcurrentRequests_;
}
void setAPIBaseURL(const std::string& t) { apiBaseURL = t; }
std::string getAPIBaseURL() const { return apiBaseURL; }
void setAPIBaseURL(std::string t) { apiBaseURL = std::move(t); }
const std::string& getAPIBaseURL() const { return apiBaseURL; }
void setAccessToken(const std::string& t) { accessToken = t; }
std::string getAccessToken() const { return accessToken; }
void setAccessToken(std::string t) { accessToken = std::move(t); }
const std::string& getAccessToken() const { return accessToken; }
private:
friend struct OnlineFileRequest;
@@ -383,15 +384,6 @@ OnlineFileRequest::OnlineFileRequest(Resource resource_, Callback callback_, Onl
impl.add(this);
}
void OnlineFileRequest::schedule() {
// Force an immediate first request if we don't have an expiration time.
if (resource.priorExpires) {
schedule(resource.priorExpires);
} else {
schedule(util::now());
}
}
OnlineFileRequest::~OnlineFileRequest() {
if (mailbox) {
mailbox->close();
@@ -431,17 +423,21 @@ Timestamp interpolateExpiration(const Timestamp& current, optional<Timestamp> pr
return now + std::max<Seconds>(delta, util::CLOCK_SKEW_RETRY_TIMEOUT);
}
void OnlineFileRequest::schedule(optional<Timestamp> expires) {
void OnlineFileRequest::activate() {
// Force an immediate first request if we don't have an expiration time.
Duration timeout = Duration::zero();
if (resource.priorExpires) {
timeout = getUpdateInterval(resource.priorExpires);
}
schedule(timeout);
}
void OnlineFileRequest::schedule(Duration timeout) {
if (impl.isPending(this) || impl.isActive(this)) {
// There's already a request in progress; don't start another one.
return;
}
// If we're not being asked for a forced refresh, calculate a timeout that depends on how many
// consecutive errors we've encountered, and on the expiration time, if present.
Duration timeout = std::min(http::errorRetryTimeout(failedRequestReason, failedRequests, retryAfter),
http::expirationTimeout(std::move(expires), expiredRequests));
if (timeout == Duration::max()) {
return;
}
@@ -460,6 +456,15 @@ void OnlineFileRequest::schedule(optional<Timestamp> expires) {
});
}
Duration OnlineFileRequest::getUpdateInterval(optional<Timestamp> expires) const {
// Calculate a timeout that depends on how many
// consecutive errors we've encountered, and on the expiration time, if present.
Duration errorRetryTimeout = http::errorRetryTimeout(failedRequestReason, failedRequests, retryAfter);
Duration expirationTimeout =
std::max(http::expirationTimeout(std::move(expires), expiredRequests), resource.minimumUpdateInterval);
return std::min(errorRetryTimeout, expirationTimeout);
}
namespace {
inline std::string sanitizeURL(std::string& url) {
@@ -519,7 +524,7 @@ void OnlineFileRequest::completed(Response response) {
failedRequestReason = Response::Error::Reason::Success;
}
schedule(response.expires);
schedule(getUpdateInterval(response.expires));
// Calling the callback may result in `this` being deleted. It needs to be done last,
// and needs to make a local copy of the callback to ensure that it remains valid for
@@ -532,13 +537,13 @@ void OnlineFileRequest::networkIsReachableAgain() {
// We need all requests to fail at least once before we are going to start retrying
// them, and we only immediately restart request that failed due to connection issues.
if (failedRequestReason == Response::Error::Reason::Connection) {
schedule(util::now());
schedule(Duration::zero());
}
}
void OnlineFileRequest::setTransformedURL(const std::string& url) {
resource.url = url;
schedule();
activate();
}
ActorRef<OnlineFileRequest> OnlineFileRequest::actor() {

View File

@@ -32,11 +32,11 @@ Duration errorRetryTimeout(Response::Error::Reason failedRequestReason, uint32_t
Duration expirationTimeout(optional<Timestamp> expires, uint32_t expiredRequests) {
if (expiredRequests) {
return Seconds(1u << std::min(expiredRequests - 1, 31u));
} else if (expires) {
return std::max(Seconds::zero(), *expires - util::now());
} else {
return Duration::max();
}
if (expires) {
return std::max(Seconds::zero(), *expires - util::now());
}
return Duration::max();
}
} // namespace http