proxygen
RetryingTest.cpp
Go to the documentation of this file.
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>
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>
35  size_t num_tries,
36  D min_duration,
37  D max_duration,
38  const F& func) {
39  vector<thread> threads(num_tries);
40  vector<D> durations(num_tries, D::min());
41  for (size_t i = 0; i < num_tries; ++i) {
42  threads[i] = thread([&, i] {
43  auto start = steady_clock::now();
44  func();
45  durations[i] = duration_cast<D>(steady_clock::now() - start);
46  });
47  }
48  for (auto& t : threads) {
49  t.join();
50  }
51  sort(durations.begin(), durations.end());
52  for (auto d : durations) {
53  EXPECT_GE(d, min_duration);
54  }
55  EXPECT_LE(durations[0], max_duration);
56 }
57 
58 TEST(RetryingTest, has_op_call) {
59  using ew = exception_wrapper;
60  auto policy_raw = [](size_t n, const ew&) { return n < 3; };
61  auto policy_fut = [](size_t n, const ew&) { return makeFuture(n < 3); };
62  using namespace futures::detail;
63  EXPECT_TRUE(retrying_policy_traits<decltype(policy_raw)>::is_raw::value);
64  EXPECT_TRUE(retrying_policy_traits<decltype(policy_fut)>::is_fut::value);
65 }
66 
67 TEST(RetryingTest, basic) {
68  auto r = futures::retrying(
69  [](size_t n, const exception_wrapper&) { return n < 3; },
70  [](size_t n) {
71  return n < 2 ? 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_throws) {
100  struct eggs : exception {};
101  auto r = futures::retrying(
102  [](size_t, exception_wrapper) -> bool { throw eggs(); },
103  [](size_t) -> Future<size_t> { throw std::runtime_error("ha"); });
104  EXPECT_THROW(std::move(r).get(), eggs);
105 }
106 
107 TEST(RetryingTest, policy_future) {
108  atomic<size_t> sleeps{0};
109  auto r =
111  [&](size_t n, const exception_wrapper&) {
112  return n < 3
113  ? makeFuture(++sleeps).thenValue([](auto&&) { return true; })
114  : makeFuture(false);
115  },
116  [](size_t n) {
117  return n < 2 ? makeFuture<size_t>(runtime_error("ha"))
118  : makeFuture(n);
119  })
120  .wait();
121  EXPECT_EQ(2, r.value());
122  EXPECT_EQ(2, sleeps);
123 }
124 
125 TEST(RetryingTest, policy_basic) {
126  auto r = futures::retrying(
128  [](size_t n) {
129  return n < 2 ? makeFuture<size_t>(runtime_error("ha"))
130  : makeFuture(n);
131  })
132  .wait();
133  EXPECT_EQ(2, r.value());
134 }
135 
136 TEST(RetryingTest, policy_capped_jittered_exponential_backoff) {
137  multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), [] {
138  using ms = milliseconds;
139  auto r = futures::retrying(
141  3,
142  ms(100),
143  ms(1000),
144  0.1,
145  mt19937_64(0),
146  [](size_t, const exception_wrapper&) { return true; }),
147  [](size_t n) {
148  return n < 2 ? makeFuture<size_t>(runtime_error("ha"))
149  : makeFuture(n);
150  })
151  .wait();
152  EXPECT_EQ(2, r.value());
153  });
154 }
155 
156 TEST(RetryingTest, policy_capped_jittered_exponential_backoff_many_retries) {
157  using namespace futures::detail;
158  mt19937_64 rng(0);
159  Duration min_backoff(1);
160 
161  Duration max_backoff(10000000);
163  80, min_backoff, max_backoff, 0, rng);
164  EXPECT_EQ(backoff, max_backoff);
165 
168  63, min_backoff, max_backoff, 0, rng);
169  EXPECT_LT(backoff, max_backoff);
170 
173  64, min_backoff, max_backoff, 0, rng);
174  EXPECT_EQ(backoff, max_backoff);
175 }
176 
177 TEST(RetryingTest, policy_sleep_defaults) {
178  multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), [] {
179  // To ensure that this compiles with default params.
180  using ms = milliseconds;
181  auto r = futures::retrying(
183  3, ms(100), ms(1000), 0.1),
184  [](size_t n) {
185  return n < 2 ? makeFuture<size_t>(runtime_error("ha"))
186  : makeFuture(n);
187  })
188  .wait();
189  EXPECT_EQ(2, r.value());
190  });
191 }
192 
193 TEST(RetryingTest, large_retries) {
194 #ifndef _WIN32
195  rlimit oldMemLimit;
196  PCHECK(getrlimit(RLIMIT_AS, &oldMemLimit) == 0);
197 
198  rlimit newMemLimit;
199  newMemLimit.rlim_cur =
200  std::min(static_cast<rlim_t>(1UL << 30), oldMemLimit.rlim_max);
201  newMemLimit.rlim_max = oldMemLimit.rlim_max;
202  if (!folly::kIsSanitizeAddress) { // ASAN reserves outside of the rlimit
203  PCHECK(setrlimit(RLIMIT_AS, &newMemLimit) == 0);
204  }
205  SCOPE_EXIT {
206  PCHECK(setrlimit(RLIMIT_AS, &oldMemLimit) == 0);
207  };
208 #endif
209 
211  // size of implicit promise is at least the size of the return.
212  using LargeReturn = array<uint64_t, 16000>;
213  auto func = [&executor](size_t retryNum) -> Future<LargeReturn> {
214  return via(&executor).thenValue([retryNum](auto&&) {
215  return retryNum < 10000
216  ? makeFuture<LargeReturn>(
217  make_exception_wrapper<std::runtime_error>("keep trying"))
218  : makeFuture<LargeReturn>(LargeReturn());
219  });
220  };
221 
222  vector<Future<LargeReturn>> futures;
223  for (auto idx = 0; idx < 40; ++idx) {
224  futures.emplace_back(futures::retrying(
225  [&executor](size_t, const exception_wrapper&) {
226  return via(&executor).thenValue([](auto&&) { return true; });
227  },
228  func));
229  }
230 
231  // 40 * 10,000 * 16,000B > 1GB; we should avoid OOM
232 
233  for (auto& f : futures) {
234  f.wait();
235  EXPECT_TRUE(f.hasValue());
236  }
237 }
238 
239 /*
240 TEST(RetryingTest, policy_sleep_cancel) {
241  multiAttemptExpectDurationWithin(5, milliseconds(0), milliseconds(10), []{
242  mt19937_64 rng(0);
243  using ms = milliseconds;
244  auto r = futures::retrying(
245  futures::retryingPolicyCappedJitteredExponentialBackoff(
246  5, ms(100), ms(1000), 0.1, rng,
247  [](size_t n, const exception_wrapper&) { return true; }),
248  [](size_t n) {
249  return n < 4
250  ? makeFuture<size_t>(runtime_error("ha"))
251  : makeFuture(n);
252  }
253  );
254  r.cancel();
255  r.wait();
256  EXPECT_EQ(2, r.value());
257  });
258 }
259 */
#define EXPECT_LE(val1, val2)
Definition: gtest.h:1928
std::function< Future< bool >size_t, const exception_wrapper &)> retryingPolicyCappedJitteredExponentialBackoff(size_t max_tries, Duration backoff_min, Duration backoff_max, double jitter_param, URNG &&rng, Policy &&p)
Definition: Retrying.h:159
auto f
#define EXPECT_THROW(statement, expected_exception)
Definition: gtest.h:1843
LogLevel max
Definition: LogLevel.cpp:31
Duration retryingJitteredExponentialBackoffDur(size_t n, Duration backoff_min, Duration backoff_max, double jitter_param, URNG &rng)
Definition: Retrying.h:141
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
std::chrono::steady_clock::time_point now()
STL namespace.
constexpr bool kIsSanitizeAddress
Definition: Portability.h:118
#define SCOPE_EXIT
Definition: ScopeGuard.h:274
TEST(RetryingTest, has_op_call)
static void basic()
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
#define EXPECT_GE(val1, val2)
Definition: gtest.h:1932
PUSHMI_INLINE_VAR constexpr __adl::get_executor_fn executor
PUSHMI_INLINE_VAR constexpr detail::via_fn via
Definition: via.h:166
std::vector< std::thread::id > threads
auto rng
Definition: CollectTest.cpp:31
std::chrono::milliseconds Duration
Definition: Types.h:36
LogLevel min
Definition: LogLevel.cpp:30
#define D(name, bit)
Definition: CpuId.h:145
bool wait(Waiter *waiter, bool shouldSleep, Waiter *&next)
static const char *const value
Definition: Conv.cpp:50
auto start
void multiAttemptExpectDurationWithin(size_t num_tries, D min_duration, D max_duration, const F &func)
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
static eggs_t eggs("eggs")
StatsClock::duration Duration
std::function< bool(size_t, const exception_wrapper &)> retryingPolicyBasic(size_t max_tries)
Definition: Retrying.h:240
#define EXPECT_LT(val1, val2)
Definition: gtest.h:1930
Future< typename std::decay< T >::type > makeFuture(T &&t)
Definition: Future-inl.h:1310
invoke_result_t< FF, size_t > retrying(Policy &&p, FF &&ff)
Definition: Retrying.h:234