Commit 4df20672 authored by Lee Howes's avatar Lee Howes Committed by Facebook Github Bot

Adds SemiFuture support to futures::retrying

Summary: Allows futures::retrying to take a continuation that returns a SemiFuture, and in that case to return a SemiFuture as the result.

Reviewed By: andriigrynenko

Differential Revision: D15876742

fbshipit-source-id: da6f1614dd940b83ca048ecedf3d3645aa69b1f0
parent a0394d84
......@@ -30,14 +30,21 @@ namespace futures {
* policy.
*
* The policy must be moveable - retrying will move it a lot - and callable of
* either of the two forms:
* any of the forms:
* - Future<bool>(size_t, exception_wrapper)
* - SemiFuture<bool>(size_t, exception_wrapper)
* - bool(size_t, exception_wrapper)
* Internally, the latter is transformed into the former in the obvious way.
* The first parameter is the attempt number of the next prospective attempt;
* the second parameter is the most recent exception. The policy returns a
* Future<bool> which, when completed with true, indicates that a retry is
* desired.
* (Semi)Future<bool> which, when completed with true, indicates that a retry
* is desired.
*
* If the callable or policy returns a SemiFuture, then retrying returns a
* SemiFuture. Note that, consistent with other SemiFuture-returning functions
* the implication of this statement is that retrying should be assumed to be
* lazy: it may do nothing until .wait()/.get() is called on the result or
* an executor is attached with .via.
*
* We provide a few generic policies:
* - Basic
......@@ -111,7 +118,11 @@ void retryingImpl(size_t k, Policy&& p, FF&& ff, Prom prom) {
}
template <class Policy, class FF>
invoke_result_t<FF, size_t> retrying(size_t k, Policy&& p, FF&& ff) {
typename std::enable_if<
!(isSemiFuture<invoke_result_t<FF, size_t>>::value ||
isSemiFuture<invoke_result_t<Policy, size_t, exception_wrapper>>::value),
invoke_result_t<FF, size_t>>::type
retrying(size_t k, Policy&& p, FF&& ff) {
using F = invoke_result_t<FF, size_t>;
using T = typename F::value_type;
auto prom = Promise<T>();
......@@ -121,6 +132,26 @@ invoke_result_t<FF, size_t> retrying(size_t k, Policy&& p, FF&& ff) {
return f;
}
template <class Policy, class FF>
typename std::enable_if<
isSemiFuture<invoke_result_t<FF, size_t>>::value ||
isSemiFuture<invoke_result_t<Policy, size_t, exception_wrapper>>::value,
invoke_result_t<FF, size_t>>::type
retrying(size_t k, Policy&& p, FF&& ff) {
auto sf = folly::makeSemiFuture().deferExValue(
[k, p = std::forward<Policy>(p), ff = std::forward<FF>(ff)](
Executor::KeepAlive<> ka, auto&&) mutable {
auto futureP = [p = std::forward<Policy>(p), ka](
size_t k, exception_wrapper e) {
return p(k, std::move(e)).via(ka);
};
auto futureFF = [ff = std::forward<FF>(ff), ka = std::move(ka)](
size_t v) { return ff(v).via(ka); };
return retrying(k, std::move(futureP), std::move(futureFF));
});
return sf;
}
template <class Policy, class FF>
invoke_result_t<FF, size_t>
retrying(Policy&& p, FF&& ff, retrying_policy_raw_tag) {
......
......@@ -133,6 +133,17 @@ TEST(RetryingTest, policy_basic) {
EXPECT_EQ(2, r.value());
}
TEST(RetryingTest, semifuture_policy_basic) {
auto r = futures::retrying(
futures::retryingPolicyBasic(3),
[](size_t n) {
return n < 2 ? makeSemiFuture<size_t>(runtime_error("ha"))
: makeSemiFuture(n);
})
.wait();
EXPECT_EQ(2, r.value());
}
TEST(RetryingTest, policy_capped_jittered_exponential_backoff) {
multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), [] {
using ms = milliseconds;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment