blob: 50edee1005e315b2e0815b3ea1dccdc8685d3ac3 [file] [log] [blame]
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <atomic>
#include <exception>
#include <vector>
#include <folly/futures/Future.h>
#include <gtest/gtest.h>
using namespace std;
using namespace std::chrono;
using namespace folly;
// Runs func num_times in parallel, expects that all of them will take
// at least min_duration and at least 1 execution will take less than
// max_duration.
template <typename D, typename F>
void multiAttemptExpectDurationWithin(size_t num_tries,
D min_duration,
D max_duration,
const F& func) {
vector<thread> threads(num_tries);
vector<D> durations(num_tries, D::min());
for (size_t i = 0; i < num_tries; ++i) {
threads[i] = thread([&,i]{
auto start = steady_clock::now();
func();
durations[i] = duration_cast<D>(steady_clock::now() - start);
});
}
for (auto& t : threads) {
t.join();
}
sort(durations.begin(), durations.end());
for (auto d : durations) {
EXPECT_GE(d, min_duration);
}
EXPECT_LE(durations[0], max_duration);
}
TEST(RetryingTest, has_op_call) {
using ew = exception_wrapper;
auto policy_raw = [](size_t n, const ew&) { return n < 3; };
auto policy_fut = [](size_t n, const ew&) { return makeFuture(n < 3); };
using namespace futures::detail;
EXPECT_TRUE(retrying_policy_traits<decltype(policy_raw)>::is_raw::value);
EXPECT_TRUE(retrying_policy_traits<decltype(policy_fut)>::is_fut::value);
}
TEST(RetryingTest, basic) {
auto r = futures::retrying(
[](size_t n, const exception_wrapper&) { return n < 3; },
[](size_t n) {
return n < 2
? makeFuture<size_t>(runtime_error("ha"))
: makeFuture(n);
}
).wait();
EXPECT_EQ(2, r.value());
}
TEST(RetryingTest, policy_future) {
atomic<size_t> sleeps {0};
auto r = futures::retrying(
[&](size_t n, const exception_wrapper&) {
return n < 3
? makeFuture(++sleeps).then([] { return true; })
: makeFuture(false);
},
[](size_t n) {
return n < 2
? makeFuture<size_t>(runtime_error("ha"))
: makeFuture(n);
}
).wait();
EXPECT_EQ(2, r.value());
EXPECT_EQ(2, sleeps);
}
TEST(RetryingTest, policy_basic) {
auto r = futures::retrying(
futures::retryingPolicyBasic(3),
[](size_t n) {
return n < 2
? makeFuture<size_t>(runtime_error("ha"))
: makeFuture(n);
}
).wait();
EXPECT_EQ(2, r.value());
}
TEST(RetryingTest, policy_capped_jittered_exponential_backoff) {
multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), []{
using ms = milliseconds;
auto r = futures::retrying(
futures::retryingPolicyCappedJitteredExponentialBackoff(
3, ms(100), ms(1000), 0.1, mt19937_64(0),
[](size_t, const exception_wrapper&) { return true; }),
[](size_t n) {
return n < 2
? makeFuture<size_t>(runtime_error("ha"))
: makeFuture(n);
}
).wait();
EXPECT_EQ(2, r.value());
});
}
TEST(RetryingTest, policy_sleep_defaults) {
multiAttemptExpectDurationWithin(5, milliseconds(200), milliseconds(400), []{
// To ensure that this compiles with default params.
using ms = milliseconds;
auto r = futures::retrying(
futures::retryingPolicyCappedJitteredExponentialBackoff(
3, ms(100), ms(1000), 0.1),
[](size_t n) {
return n < 2
? makeFuture<size_t>(runtime_error("ha"))
: makeFuture(n);
}
).wait();
EXPECT_EQ(2, r.value());
});
}
/*
TEST(RetryingTest, policy_sleep_cancel) {
multiAttemptExpectDurationWithin(5, milliseconds(0), milliseconds(10), []{
mt19937_64 rng(0);
using ms = milliseconds;
auto r = futures::retrying(
futures::retryingPolicyCappedJitteredExponentialBackoff(
5, ms(100), ms(1000), 0.1, rng,
[](size_t n, const exception_wrapper&) { return true; }),
[](size_t n) {
return n < 4
? makeFuture<size_t>(runtime_error("ha"))
: makeFuture(n);
}
);
r.cancel();
r.wait();
EXPECT_EQ(2, r.value());
});
}
*/