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