d1c429dfd323b40e504ec30df6b587b5af8c72b6
[folly.git] / folly / futures / test / RetryingTest.cpp
1 /*
2  * Copyright 2017 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/Future.h>
22 #include <folly/portability/GTest.h>
23
24 using namespace std;
25 using namespace std::chrono;
26 using namespace folly;
27
28 // Runs func num_times in parallel, expects that all of them will take
29 // at least min_duration and at least 1 execution will take less than
30 // max_duration.
31 template <typename D, typename F>
32 void multiAttemptExpectDurationWithin(size_t num_tries,
33                                       D min_duration,
34                                       D max_duration,
35                                       const F& func) {
36   vector<thread> threads(num_tries);
37   vector<D> durations(num_tries, D::min());
38   for (size_t i = 0; i < num_tries; ++i) {
39     threads[i] = thread([&,i]{
40       auto start = steady_clock::now();
41       func();
42       durations[i] = duration_cast<D>(steady_clock::now() - start);
43     });
44   }
45   for (auto& t : threads) {
46     t.join();
47   }
48   sort(durations.begin(), durations.end());
49   for (auto d : durations) {
50     EXPECT_GE(d, min_duration);
51   }
52   EXPECT_LE(durations[0], max_duration);
53 }
54
55 TEST(RetryingTest, has_op_call) {
56   using ew = exception_wrapper;
57   auto policy_raw = [](size_t n, const ew&) { return n < 3; };
58   auto policy_fut = [](size_t n, const ew&) { return makeFuture(n < 3); };
59   using namespace futures::detail;
60   EXPECT_TRUE(retrying_policy_traits<decltype(policy_raw)>::is_raw::value);
61   EXPECT_TRUE(retrying_policy_traits<decltype(policy_fut)>::is_fut::value);
62 }
63
64 TEST(RetryingTest, basic) {
65   auto r = futures::retrying(
66       [](size_t n, const exception_wrapper&) { return n < 3; },
67       [](size_t n) {
68           return n < 2
69             ? makeFuture<size_t>(runtime_error("ha"))
70             : makeFuture(n);
71       }
72   ).wait();
73   EXPECT_EQ(2, r.value());
74 }
75
76 TEST(RetryingTest, policy_future) {
77   atomic<size_t> sleeps {0};
78   auto r = futures::retrying(
79       [&](size_t n, const exception_wrapper&) {
80           return n < 3
81             ? makeFuture(++sleeps).then([] { return true; })
82             : makeFuture(false);
83       },
84       [](size_t n) {
85           return n < 2
86             ? makeFuture<size_t>(runtime_error("ha"))
87             : makeFuture(n);
88       }
89   ).wait();
90   EXPECT_EQ(2, r.value());
91   EXPECT_EQ(2, sleeps);
92 }
93
94 TEST(RetryingTest, policy_basic) {
95   auto r = futures::retrying(
96       futures::retryingPolicyBasic(3),
97       [](size_t n) {
98           return n < 2
99             ? makeFuture<size_t>(runtime_error("ha"))
100             : makeFuture(n);
101       }
102   ).wait();
103   EXPECT_EQ(2, r.value());
104 }
105
106 TEST(RetryingTest, policy_capped_jittered_exponential_backoff) {
107   multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), []{
108     using ms = milliseconds;
109     auto r = futures::retrying(
110         futures::retryingPolicyCappedJitteredExponentialBackoff(
111           3, ms(100), ms(1000), 0.1, mt19937_64(0),
112           [](size_t, const exception_wrapper&) { return true; }),
113         [](size_t n) {
114             return n < 2
115               ? makeFuture<size_t>(runtime_error("ha"))
116               : makeFuture(n);
117         }
118     ).wait();
119     EXPECT_EQ(2, r.value());
120   });
121 }
122
123 TEST(RetryingTest, policy_sleep_defaults) {
124   multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), []{
125     //  To ensure that this compiles with default params.
126     using ms = milliseconds;
127     auto r = futures::retrying(
128         futures::retryingPolicyCappedJitteredExponentialBackoff(
129           3, ms(100), ms(1000), 0.1),
130         [](size_t n) {
131             return n < 2
132               ? makeFuture<size_t>(runtime_error("ha"))
133               : makeFuture(n);
134         }
135     ).wait();
136     EXPECT_EQ(2, r.value());
137   });
138 }
139
140 /*
141 TEST(RetryingTest, policy_sleep_cancel) {
142   multiAttemptExpectDurationWithin(5, milliseconds(0), milliseconds(10), []{
143     mt19937_64 rng(0);
144     using ms = milliseconds;
145     auto r = futures::retrying(
146         futures::retryingPolicyCappedJitteredExponentialBackoff(
147           5, ms(100), ms(1000), 0.1, rng,
148           [](size_t n, const exception_wrapper&) { return true; }),
149         [](size_t n) {
150             return n < 4
151               ? makeFuture<size_t>(runtime_error("ha"))
152               : makeFuture(n);
153         }
154     );
155     r.cancel();
156     r.wait();
157     EXPECT_EQ(2, r.value());
158   });
159 }
160 */