2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
21 #include <folly/futures/Retrying.h>
22 #include <folly/portability/GTest.h>
23 #include <folly/portability/SysResource.h>
24 #include "TestExecutor.h"
27 using namespace std::chrono;
28 using namespace folly;
30 // Runs func num_times in parallel, expects that all of them will take
31 // at least min_duration and at least 1 execution will take less than
33 template <typename D, typename F>
34 void multiAttemptExpectDurationWithin(size_t num_tries,
38 vector<thread> threads(num_tries);
39 vector<D> durations(num_tries, D::min());
40 for (size_t i = 0; i < num_tries; ++i) {
41 threads[i] = thread([&,i]{
42 auto start = steady_clock::now();
44 durations[i] = duration_cast<D>(steady_clock::now() - start);
47 for (auto& t : threads) {
50 sort(durations.begin(), durations.end());
51 for (auto d : durations) {
52 EXPECT_GE(d, min_duration);
54 EXPECT_LE(durations[0], max_duration);
57 TEST(RetryingTest, has_op_call) {
58 using ew = exception_wrapper;
59 auto policy_raw = [](size_t n, const ew&) { return n < 3; };
60 auto policy_fut = [](size_t n, const ew&) { return makeFuture(n < 3); };
61 using namespace futures::detail;
62 EXPECT_TRUE(retrying_policy_traits<decltype(policy_raw)>::is_raw::value);
63 EXPECT_TRUE(retrying_policy_traits<decltype(policy_fut)>::is_fut::value);
66 TEST(RetryingTest, basic) {
67 auto r = futures::retrying(
68 [](size_t n, const exception_wrapper&) { return n < 3; },
71 ? makeFuture<size_t>(runtime_error("ha"))
75 EXPECT_EQ(2, r.value());
78 TEST(RetryingTest, future_factory_throws) {
79 struct ReturnedException : exception {};
80 struct ThrownException : exception {};
81 auto result = futures::retrying(
82 [](size_t n, const exception_wrapper&) { return n < 2; },
86 return makeFuture<size_t>(
87 make_exception_wrapper<ReturnedException>());
89 throw ThrownException();
96 EXPECT_THROW(result.throwIfFailed(), ThrownException);
99 TEST(RetryingTest, policy_future) {
100 atomic<size_t> sleeps {0};
101 auto r = futures::retrying(
102 [&](size_t n, const exception_wrapper&) {
104 ? makeFuture(++sleeps).then([] { return true; })
109 ? makeFuture<size_t>(runtime_error("ha"))
113 EXPECT_EQ(2, r.value());
114 EXPECT_EQ(2, sleeps);
117 TEST(RetryingTest, policy_basic) {
118 auto r = futures::retrying(
119 futures::retryingPolicyBasic(3),
122 ? makeFuture<size_t>(runtime_error("ha"))
126 EXPECT_EQ(2, r.value());
129 TEST(RetryingTest, policy_capped_jittered_exponential_backoff) {
130 multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), []{
131 using ms = milliseconds;
132 auto r = futures::retrying(
133 futures::retryingPolicyCappedJitteredExponentialBackoff(
134 3, ms(100), ms(1000), 0.1, mt19937_64(0),
135 [](size_t, const exception_wrapper&) { return true; }),
138 ? makeFuture<size_t>(runtime_error("ha"))
142 EXPECT_EQ(2, r.value());
146 TEST(RetryingTest, policy_sleep_defaults) {
147 multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), []{
148 // To ensure that this compiles with default params.
149 using ms = milliseconds;
150 auto r = futures::retrying(
151 futures::retryingPolicyCappedJitteredExponentialBackoff(
152 3, ms(100), ms(1000), 0.1),
155 ? makeFuture<size_t>(runtime_error("ha"))
159 EXPECT_EQ(2, r.value());
163 TEST(RetryingTest, large_retries) {
165 PCHECK(getrlimit(RLIMIT_AS, &oldMemLimit) == 0);
168 newMemLimit.rlim_cur =
169 std::min(static_cast<rlim_t>(1UL << 30), oldMemLimit.rlim_max);
170 newMemLimit.rlim_max = oldMemLimit.rlim_max;
171 if (!folly::kIsSanitizeAddress) { // ASAN reserves outside of the rlimit
172 PCHECK(setrlimit(RLIMIT_AS, &newMemLimit) == 0);
175 PCHECK(setrlimit(RLIMIT_AS, &oldMemLimit) == 0);
178 TestExecutor executor(4);
179 // size of implicit promise is at least the size of the return.
180 using LargeReturn = array<uint64_t, 16000>;
181 auto func = [&executor](size_t retryNum) -> Future<LargeReturn> {
182 return via(&executor).then([retryNum] {
183 return retryNum < 10000
184 ? makeFuture<LargeReturn>(
185 make_exception_wrapper<std::runtime_error>("keep trying"))
186 : makeFuture<LargeReturn>(LargeReturn());
190 vector<Future<LargeReturn>> futures;
191 for (auto idx = 0; idx < 40; ++idx) {
192 futures.emplace_back(futures::retrying(
193 [&executor](size_t, const exception_wrapper&) {
194 return via(&executor).then([] { return true; });
199 // 40 * 10,000 * 16,000B > 1GB; we should avoid OOM
201 for (auto& f : futures) {
203 EXPECT_TRUE(f.hasValue());
208 TEST(RetryingTest, policy_sleep_cancel) {
209 multiAttemptExpectDurationWithin(5, milliseconds(0), milliseconds(10), []{
211 using ms = milliseconds;
212 auto r = futures::retrying(
213 futures::retryingPolicyCappedJitteredExponentialBackoff(
214 5, ms(100), ms(1000), 0.1, rng,
215 [](size_t n, const exception_wrapper&) { return true; }),
218 ? makeFuture<size_t>(runtime_error("ha"))
224 EXPECT_EQ(2, r.value());