View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.client5.http.impl.classic;
28  
29  
30  import static org.junit.jupiter.api.Assertions.assertEquals;
31  import static org.junit.jupiter.api.Assertions.assertTrue;
32  
33  import java.time.Instant;
34  import java.util.Map;
35  
36  import org.apache.hc.client5.http.HttpRoute;
37  import org.apache.hc.core5.http.HttpHost;
38  import org.apache.hc.core5.util.TimeValue;
39  import org.junit.jupiter.api.BeforeEach;
40  import org.junit.jupiter.api.Test;
41  
42  public class TestLinearBackoffManager {
43  
44      private LinearBackoffManager impl;
45      private MockConnPoolControl connPerRoute;
46      private HttpRoute route;
47      private static final long DEFAULT_COOL_DOWN_MS = 10;
48  
49      @BeforeEach
50      public void setUp() {
51          connPerRoute = new MockConnPoolControl();
52          route = new HttpRoute(new HttpHost("localhost", 80));
53          impl = new LinearBackoffManager(connPerRoute);
54          impl.setCoolDown(TimeValue.ofMilliseconds(DEFAULT_COOL_DOWN_MS));
55      }
56  
57      @Test
58      public void incrementsConnectionsOnBackoff() {
59          final LinearBackoffManager impl = new LinearBackoffManager(connPerRoute);
60          impl.setCoolDown(TimeValue.ofMilliseconds(DEFAULT_COOL_DOWN_MS)); // Set the cool-down period
61          connPerRoute.setMaxPerRoute(route, 4);
62          impl.backOff(route);
63          assertEquals(5, connPerRoute.getMaxPerRoute(route));
64      }
65  
66      @Test
67      public void decrementsConnectionsOnProbe() {
68          connPerRoute.setMaxPerRoute(route, 4);
69          impl.probe(route);
70          assertEquals(3, connPerRoute.getMaxPerRoute(route));
71      }
72  
73      @Test
74      public void backoffDoesNotAdjustDuringCoolDownPeriod() {
75          // Arrange
76          connPerRoute.setMaxPerRoute(route, 4);
77          impl.backOff(route);
78          final long max = connPerRoute.getMaxPerRoute(route);
79  
80          // Manipulate lastRouteBackoffs to simulate that not enough time has passed for the cooldown period
81          final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
82          lastRouteBackoffs.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS - 1));
83  
84          // Act
85          impl.backOff(route);
86  
87          // Assert
88          assertEquals(max, connPerRoute.getMaxPerRoute(route));
89      }
90      @Test
91      public void backoffStillAdjustsAfterCoolDownPeriod() {
92          // Arrange
93          final LinearBackoffManager impl = new LinearBackoffManager(connPerRoute);
94          impl.setCoolDown(TimeValue.ofMilliseconds(DEFAULT_COOL_DOWN_MS)); // Set the cool-down period
95          connPerRoute.setMaxPerRoute(route, 4);
96          impl.backOff(route);
97          final int max1 = connPerRoute.getMaxPerRoute(route);
98  
99          // Manipulate lastRouteBackoffs to simulate that enough time has passed for the cooldown period
100         final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
101         lastRouteBackoffs.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS + 1));
102 
103         // Act
104         impl.backOff(route);
105         final int max2 = connPerRoute.getMaxPerRoute(route);
106 
107         // Assert
108         assertTrue(max2 > max1);
109     }
110 
111     @Test
112     public void probeDoesNotAdjustDuringCooldownPeriod() {
113         // Arrange
114         connPerRoute.setMaxPerRoute(route, 4);
115         impl.probe(route);
116         final long max = connPerRoute.getMaxPerRoute(route);
117 
118         // Manipulate lastRouteProbes to simulate that not enough time has passed for the cooldown period
119         final Map<HttpRoute, Instant> lastRouteProbes = impl.getLastRouteProbes();
120         lastRouteProbes.put(route, Instant.now());
121 
122         // Act
123         impl.probe(route);
124 
125         // Assert
126         assertEquals(max, connPerRoute.getMaxPerRoute(route));
127     }
128 
129     @Test
130     public void probeStillAdjustsAfterCoolDownPeriod() {
131         // Arrange
132         connPerRoute.setMaxPerRoute(route, 4);
133         impl.probe(route);
134 
135         // Manipulate lastRouteProbes to simulate that enough time has passed for the cooldown period
136         final Map<HttpRoute, Instant> lastRouteProbes = impl.getLastRouteProbes();
137         lastRouteProbes.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS + 1));
138 
139         // Act
140         impl.probe(route);
141         final long newMax = connPerRoute.getMaxPerRoute(route);
142 
143         // Assert
144         assertEquals(2, newMax); // The cap is set to 2 by default
145     }
146 
147 
148     @Test
149     public void testSetPerHostConnectionCap() {
150         connPerRoute.setMaxPerRoute(route, 5);
151         impl.setPerHostConnectionCap(10); // Set the cap to a higher value
152         impl.backOff(route);
153         assertEquals(6, connPerRoute.getMaxPerRoute(route));
154     }
155 
156 
157     public void probeUpdatesRemainingAttemptsIndirectly() {
158         // Set initial max per route
159         connPerRoute.setMaxPerRoute(route, 4);
160 
161         // Apply backOff twice
162         impl.backOff(route);
163 
164         // Manipulate lastRouteBackoffs to simulate that enough time has passed for the cooldown period
165         final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
166         lastRouteBackoffs.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS + 1));
167 
168         impl.backOff(route);
169 
170         // Ensure that connection pool size has increased
171         assertEquals(6, connPerRoute.getMaxPerRoute(route));
172 
173         // Apply probe once
174         impl.probe(route);
175 
176         // Manipulate lastRouteProbes to simulate that enough time has passed for the cooldown period
177         final Map<HttpRoute, Instant> lastRouteProbes = impl.getLastRouteProbes();
178         lastRouteProbes.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS * 2));
179 
180         // Apply probe once more
181         impl.probe(route);
182 
183         // Check that connection pool size has decreased once, indicating that the remaining attempts were updated
184         assertEquals(5, connPerRoute.getMaxPerRoute(route));
185     }
186 
187     @Test
188     public void linearIncrementTest() {
189         final int initialMax = 4;
190         connPerRoute.setMaxPerRoute(route, initialMax);
191 
192         // Manipulate lastRouteBackoffs to simulate that enough time has passed for the cooldown period
193         final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
194 
195         for (int i = 1; i <= 5; i++) {
196             // Simulate that enough time has passed for the cooldown period
197             lastRouteBackoffs.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS + 1));
198 
199             // Act
200             impl.backOff(route);
201 
202             // Assert
203             assertEquals(initialMax + i, connPerRoute.getMaxPerRoute(route));
204         }
205     }
206 
207 }