aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/retry/retry_policy_ut.cpp
blob: 32a5896e0829cd395385d0600fc29f0d651f0d03 (plain) (blame)
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);
    }
}