1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
#include "retry_policy.h"
#include <library/cpp/testing/unittest/registar.h>
Y_UNIT_TEST_SUITE(RetryPolicy) {
Y_UNIT_TEST(NoRetryPolicy) {
auto policy = IRetryPolicy<int>::GetNoRetryPolicy();
UNIT_ASSERT(!policy->CreateRetryState()->GetNextRetryDelay(42));
}
using ITestPolicy = IRetryPolicy<ERetryErrorClass>;
ERetryErrorClass ErrorClassFunction(ERetryErrorClass err) {
return err;
}
#define ASSERT_INTERVAL(from, to, val) { \
auto v = val; \
UNIT_ASSERT(v); \
UNIT_ASSERT_GE_C(*v, from, *v); \
UNIT_ASSERT_LE_C(*v, to, *v); \
}
Y_UNIT_TEST(FixedIntervalPolicy) {
auto policy = ITestPolicy::GetFixedIntervalPolicy(ErrorClassFunction, TDuration::MilliSeconds(100), TDuration::Seconds(100));
auto state = policy->CreateRetryState();
for (int i = 0; i < 5; ++i) {
ASSERT_INTERVAL(TDuration::MilliSeconds(50), TDuration::MilliSeconds(100), state->GetNextRetryDelay(ERetryErrorClass::ShortRetry));
ASSERT_INTERVAL(TDuration::Seconds(50), TDuration::Seconds(100), state->GetNextRetryDelay(ERetryErrorClass::LongRetry));
UNIT_ASSERT(!state->GetNextRetryDelay(ERetryErrorClass::NoRetry));
}
}
Y_UNIT_TEST(ExponentialBackoffPolicy) {
auto policy = ITestPolicy::GetExponentialBackoffPolicy(ErrorClassFunction, TDuration::MilliSeconds(100), TDuration::Seconds(100), TDuration::Seconds(500));
auto state = policy->CreateRetryState();
// Step 1
ASSERT_INTERVAL(TDuration::MilliSeconds(50), TDuration::MilliSeconds(100), state->GetNextRetryDelay(ERetryErrorClass::ShortRetry));
// Step 2
ASSERT_INTERVAL(TDuration::Seconds(50), TDuration::Seconds(100), state->GetNextRetryDelay(ERetryErrorClass::LongRetry));
// Step 3
ASSERT_INTERVAL(TDuration::Seconds(100), TDuration::Seconds(200), state->GetNextRetryDelay(ERetryErrorClass::ShortRetry));
// Step 4
ASSERT_INTERVAL(TDuration::Seconds(200), TDuration::Seconds(400), state->GetNextRetryDelay(ERetryErrorClass::LongRetry));
// Step 5. Max delay
ASSERT_INTERVAL(TDuration::Seconds(250), TDuration::Seconds(500), state->GetNextRetryDelay(ERetryErrorClass::LongRetry));
ASSERT_INTERVAL(TDuration::Seconds(250), TDuration::Seconds(500), state->GetNextRetryDelay(ERetryErrorClass::ShortRetry));
// No retry
UNIT_ASSERT(!state->GetNextRetryDelay(ERetryErrorClass::NoRetry));
}
void TestMaxRetries(bool exponentialBackoff) {
ITestPolicy::TPtr policy;
if (exponentialBackoff) {
policy = ITestPolicy::GetExponentialBackoffPolicy(ErrorClassFunction, TDuration::MilliSeconds(10), TDuration::MilliSeconds(200), TDuration::Seconds(30), 3);
} else {
policy = ITestPolicy::GetFixedIntervalPolicy(ErrorClassFunction, TDuration::MilliSeconds(100), TDuration::MilliSeconds(300), 3);
}
auto state = policy->CreateRetryState();
UNIT_ASSERT(state->GetNextRetryDelay(ERetryErrorClass::ShortRetry));
UNIT_ASSERT(state->GetNextRetryDelay(ERetryErrorClass::ShortRetry));
UNIT_ASSERT(state->GetNextRetryDelay(ERetryErrorClass::ShortRetry));
UNIT_ASSERT(!state->GetNextRetryDelay(ERetryErrorClass::ShortRetry));
UNIT_ASSERT(!state->GetNextRetryDelay(ERetryErrorClass::ShortRetry));
}
void TestMaxTime(bool exponentialBackoff) {
ITestPolicy::TPtr policy;
const TDuration maxDelay = TDuration::Seconds(2);
if (exponentialBackoff) {
policy = ITestPolicy::GetExponentialBackoffPolicy(ErrorClassFunction, TDuration::MilliSeconds(10), TDuration::MilliSeconds(200), TDuration::Seconds(30), 100500, maxDelay);
} else {
policy = ITestPolicy::GetFixedIntervalPolicy(ErrorClassFunction, TDuration::MilliSeconds(100), TDuration::MilliSeconds(300), 100500, maxDelay);
}
const TInstant start = TInstant::Now();
auto state = policy->CreateRetryState();
for (int i = 0; i < 3; ++i) {
auto delay = state->GetNextRetryDelay(ERetryErrorClass::ShortRetry);
const TInstant now = TInstant::Now();
UNIT_ASSERT(delay || now - start >= maxDelay);
}
Sleep(maxDelay);
UNIT_ASSERT(!state->GetNextRetryDelay(ERetryErrorClass::ShortRetry));
UNIT_ASSERT(!state->GetNextRetryDelay(ERetryErrorClass::ShortRetry));
}
Y_UNIT_TEST(MaxRetriesExponentialBackoff) {
TestMaxRetries(true);
}
Y_UNIT_TEST(MaxRetriesFixedInterval) {
TestMaxRetries(false);
}
Y_UNIT_TEST(MaxTimeExponentialBackoff) {
TestMaxTime(true);
}
Y_UNIT_TEST(MaxTimeFixedInterval) {
TestMaxTime(false);
}
}
|