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 package org.apache.hc.client5.http.impl.classic;
28
29 import org.apache.hc.client5.http.HttpRoute;
30 import org.apache.hc.core5.http.HttpHost;
31 import org.apache.hc.core5.util.TimeValue;
32 import org.junit.jupiter.api.BeforeEach;
33 import org.junit.jupiter.api.Test;
34
35
36 import static org.junit.jupiter.api.Assertions.assertEquals;
37 import static org.junit.jupiter.api.Assertions.assertTrue;
38
39 import java.time.Instant;
40 import java.util.Map;
41
42 class TestExponentialBackoffManager {
43
44 private ExponentialBackoffManager impl;
45 private MockConnPoolControl connPerRoute;
46 private HttpRoute route;
47 private static final long DEFAULT_COOL_DOWN_MS = 5000;
48
49 @BeforeEach
50 void setUp() {
51 connPerRoute = new MockConnPoolControl();
52 route = new HttpRoute(new HttpHost("localhost", 80));
53 impl = new ExponentialBackoffManager(connPerRoute);
54 impl.setPerHostConnectionCap(10);
55 impl.setCoolDown(TimeValue.ofMilliseconds(DEFAULT_COOL_DOWN_MS));
56 impl.setBackoffFactor(1.75);
57 }
58
59 @Test
60 void exponentialBackoffApplied() {
61 connPerRoute.setMaxPerRoute(route, 4);
62 impl.setBackoffFactor(2);
63 impl.backOff(route);
64 assertEquals(1, connPerRoute.getMaxPerRoute(route));
65 }
66
67 @Test
68 void exponentialGrowthRateIsConfigurable() {
69 final int customCoolDownMs = 500;
70 connPerRoute.setMaxPerRoute(route, 4);
71 impl.setBackoffFactor(0.5);
72 impl.setCoolDown(TimeValue.ofMilliseconds(customCoolDownMs));
73 impl.backOff(route);
74 assertEquals(2, connPerRoute.getMaxPerRoute(route));
75
76
77 final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
78 lastRouteBackoffs.put(route, Instant.now().minusMillis(customCoolDownMs + 1));
79
80 impl.backOff(route);
81 assertEquals(1, connPerRoute.getMaxPerRoute(route));
82 }
83
84 @Test
85 void doesNotIncreaseBeyondPerHostMaxOnProbe() {
86 connPerRoute.setDefaultMaxPerRoute(5);
87 connPerRoute.setMaxPerRoute(route, 5);
88 impl.setPerHostConnectionCap(5);
89 impl.probe(route);
90 assertEquals(5, connPerRoute.getMaxPerRoute(route));
91 }
92
93 @Test
94 void backoffDoesNotAdjustDuringCoolDownPeriod() {
95 connPerRoute.setMaxPerRoute(route, 4);
96 impl.backOff(route);
97 final long max = connPerRoute.getMaxPerRoute(route);
98
99
100 final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
101 lastRouteBackoffs.put(route, Instant.now().minusMillis(10));
102
103
104 impl.backOff(route);
105 assertEquals(max, connPerRoute.getMaxPerRoute(route));
106 }
107
108 @Test
109 void backoffStillAdjustsAfterCoolDownPeriod() {
110 connPerRoute.setMaxPerRoute(route, 8);
111 impl.backOff(route);
112 final long max = connPerRoute.getMaxPerRoute(route);
113
114
115 final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
116 lastRouteBackoffs.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS + 1));
117
118
119 impl.backOff(route);
120
121
122 assertTrue(max == 1 || max > connPerRoute.getMaxPerRoute(route));
123 }
124
125
126 @Test
127 void probeDoesNotAdjustDuringCooldownPeriod() {
128 connPerRoute.setMaxPerRoute(route, 4);
129 impl.probe(route);
130 final long max = connPerRoute.getMaxPerRoute(route);
131
132
133 final Map<HttpRoute, Instant> lastRouteProbes = impl.getLastRouteProbes();
134 lastRouteProbes.put(route, Instant.now().minusMillis(0));
135
136 impl.probe(route);
137 assertEquals(max, connPerRoute.getMaxPerRoute(route));
138 }
139
140 @Test
141 void probeStillAdjustsAfterCoolDownPeriod() {
142 connPerRoute.setMaxPerRoute(route, 8);
143 impl.probe(route);
144 final long max = connPerRoute.getMaxPerRoute(route);
145
146
147 final Map<HttpRoute, Instant> lastRouteProbes = impl.getLastRouteProbes();
148 lastRouteProbes.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS + 1));
149
150 impl.probe(route);
151 assertTrue(max < connPerRoute.getMaxPerRoute(route));
152 }
153
154 @Test
155 void willBackoffImmediatelyEvenAfterAProbe() {
156 connPerRoute.setMaxPerRoute(route, 8);
157 impl.probe(route);
158 final long max = connPerRoute.getMaxPerRoute(route);
159 impl.backOff(route);
160 assertTrue(connPerRoute.getMaxPerRoute(route) < max);
161 }
162
163 @Test
164 void coolDownPeriodIsConfigurable() {
165 final long cd = 500;
166 impl.setCoolDown(TimeValue.ofMilliseconds(cd));
167 connPerRoute.setMaxPerRoute(route, 4);
168 impl.probe(route);
169 final int max0 = connPerRoute.getMaxPerRoute(route);
170
171
172 final Map<HttpRoute, Instant> lastRouteProbes = impl.getLastRouteProbes();
173 lastRouteProbes.put(route, Instant.now().minusMillis(cd / 2));
174
175 impl.probe(route);
176 assertEquals(max0, connPerRoute.getMaxPerRoute(route));
177
178
179 lastRouteProbes.put(route, Instant.now().minusMillis(cd + 1));
180
181 impl.probe(route);
182 assertTrue(max0 < connPerRoute.getMaxPerRoute(route));
183 }
184
185 }