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 { ...@@ -30,14 +30,21 @@ namespace futures {
* policy. * policy.
* *
* The policy must be moveable - retrying will move it a lot - and callable of * 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) * - Future<bool>(size_t, exception_wrapper)
* - SemiFuture<bool>(size_t, exception_wrapper)
* - bool(size_t, exception_wrapper) * - bool(size_t, exception_wrapper)
* Internally, the latter is transformed into the former in the obvious way. * 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 first parameter is the attempt number of the next prospective attempt;
* the second parameter is the most recent exception. The policy returns a * the second parameter is the most recent exception. The policy returns a
* Future<bool> which, when completed with true, indicates that a retry is * (Semi)Future<bool> which, when completed with true, indicates that a retry
* desired. * 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: * We provide a few generic policies:
* - Basic * - Basic
...@@ -111,7 +118,11 @@ void retryingImpl(size_t k, Policy&& p, FF&& ff, Prom prom) { ...@@ -111,7 +118,11 @@ void retryingImpl(size_t k, Policy&& p, FF&& ff, Prom prom) {
} }
template <class Policy, class FF> 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 F = invoke_result_t<FF, size_t>;
using T = typename F::value_type; using T = typename F::value_type;
auto prom = Promise<T>(); auto prom = Promise<T>();
...@@ -121,6 +132,26 @@ invoke_result_t<FF, size_t> retrying(size_t k, Policy&& p, FF&& ff) { ...@@ -121,6 +132,26 @@ invoke_result_t<FF, size_t> retrying(size_t k, Policy&& p, FF&& ff) {
return f; 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> template <class Policy, class FF>
invoke_result_t<FF, size_t> invoke_result_t<FF, size_t>
retrying(Policy&& p, FF&& ff, retrying_policy_raw_tag) { retrying(Policy&& p, FF&& ff, retrying_policy_raw_tag) {
......
...@@ -133,6 +133,17 @@ TEST(RetryingTest, policy_basic) { ...@@ -133,6 +133,17 @@ TEST(RetryingTest, policy_basic) {
EXPECT_EQ(2, r.value()); 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) { TEST(RetryingTest, policy_capped_jittered_exponential_backoff) {
multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), [] { multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), [] {
using ms = milliseconds; 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