Fix copyright lines
[folly.git] / folly / futures / test / RetryingTest.cpp
1 /*
2  * Copyright 2015-present Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <algorithm>
18 #include <atomic>
19 #include <vector>
20
21 #include <folly/futures/Retrying.h>
22 #include <folly/portability/GTest.h>
23 #include <folly/portability/SysResource.h>
24 #include "TestExecutor.h"
25
26 using namespace std;
27 using namespace std::chrono;
28 using namespace folly;
29
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
32 // max_duration.
33 template <typename D, typename F>
34 void multiAttemptExpectDurationWithin(size_t num_tries,
35                                       D min_duration,
36                                       D max_duration,
37                                       const F& func) {
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();
43       func();
44       durations[i] = duration_cast<D>(steady_clock::now() - start);
45     });
46   }
47   for (auto& t : threads) {
48     t.join();
49   }
50   sort(durations.begin(), durations.end());
51   for (auto d : durations) {
52     EXPECT_GE(d, min_duration);
53   }
54   EXPECT_LE(durations[0], max_duration);
55 }
56
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);
64 }
65
66 TEST(RetryingTest, basic) {
67   auto r = futures::retrying(
68       [](size_t n, const exception_wrapper&) { return n < 3; },
69       [](size_t n) {
70           return n < 2
71             ? makeFuture<size_t>(runtime_error("ha"))
72             : makeFuture(n);
73       }
74   ).wait();
75   EXPECT_EQ(2, r.value());
76 }
77
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; },
83                     [](size_t n) {
84                       switch (n) {
85                         case 0:
86                           return makeFuture<size_t>(
87                               make_exception_wrapper<ReturnedException>());
88                         case 1:
89                           throw ThrownException();
90                         default:
91                           return makeFuture(n);
92                       }
93                     })
94                     .wait()
95                     .getTry();
96   EXPECT_THROW(result.throwIfFailed(), ThrownException);
97 }
98
99 TEST(RetryingTest, policy_future) {
100   atomic<size_t> sleeps {0};
101   auto r = futures::retrying(
102       [&](size_t n, const exception_wrapper&) {
103           return n < 3
104             ? makeFuture(++sleeps).then([] { return true; })
105             : makeFuture(false);
106       },
107       [](size_t n) {
108           return n < 2
109             ? makeFuture<size_t>(runtime_error("ha"))
110             : makeFuture(n);
111       }
112   ).wait();
113   EXPECT_EQ(2, r.value());
114   EXPECT_EQ(2, sleeps);
115 }
116
117 TEST(RetryingTest, policy_basic) {
118   auto r = futures::retrying(
119       futures::retryingPolicyBasic(3),
120       [](size_t n) {
121           return n < 2
122             ? makeFuture<size_t>(runtime_error("ha"))
123             : makeFuture(n);
124       }
125   ).wait();
126   EXPECT_EQ(2, r.value());
127 }
128
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; }),
136         [](size_t n) {
137             return n < 2
138               ? makeFuture<size_t>(runtime_error("ha"))
139               : makeFuture(n);
140         }
141     ).wait();
142     EXPECT_EQ(2, r.value());
143   });
144 }
145
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),
153         [](size_t n) {
154             return n < 2
155               ? makeFuture<size_t>(runtime_error("ha"))
156               : makeFuture(n);
157         }
158     ).wait();
159     EXPECT_EQ(2, r.value());
160   });
161 }
162
163 TEST(RetryingTest, large_retries) {
164   rlimit oldMemLimit;
165   PCHECK(getrlimit(RLIMIT_AS, &oldMemLimit) == 0);
166
167   rlimit newMemLimit;
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);
173   }
174   SCOPE_EXIT {
175     PCHECK(setrlimit(RLIMIT_AS, &oldMemLimit) == 0);
176   };
177
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());
187     });
188   };
189
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; });
195         },
196         func));
197   }
198
199   // 40 * 10,000 * 16,000B > 1GB; we should avoid OOM
200
201   for (auto& f : futures) {
202     f.wait();
203     EXPECT_TRUE(f.hasValue());
204   }
205 }
206
207 /*
208 TEST(RetryingTest, policy_sleep_cancel) {
209   multiAttemptExpectDurationWithin(5, milliseconds(0), milliseconds(10), []{
210     mt19937_64 rng(0);
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; }),
216         [](size_t n) {
217             return n < 4
218               ? makeFuture<size_t>(runtime_error("ha"))
219               : makeFuture(n);
220         }
221     );
222     r.cancel();
223     r.wait();
224     EXPECT_EQ(2, r.value());
225   });
226 }
227 */