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.util.Random;
34  
35  import org.apache.hc.client5.http.HttpRoute;
36  import org.apache.hc.client5.http.classic.BackoffManager;
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 TestAIMDBackoffManager {
43  
44      private AIMDBackoffManager impl;
45      private MockConnPoolControl connPerRoute;
46      private HttpRoute route;
47      private MockClock clock;
48  
49      @BeforeEach
50      public void setUp() {
51          connPerRoute = new MockConnPoolControl();
52          route = new HttpRoute(new HttpHost("localhost", 80));
53          clock = new MockClock();
54          impl = new AIMDBackoffManager(connPerRoute, clock);
55          impl.setPerHostConnectionCap(10);
56      }
57  
58      @Test
59      public void isABackoffManager() {
60          assertTrue(impl instanceof BackoffManager);
61      }
62  
63      @Test
64      public void halvesConnectionsOnBackoff() {
65          connPerRoute.setMaxPerRoute(route, 4);
66          impl.backOff(route);
67          assertEquals(2, connPerRoute.getMaxPerRoute(route));
68      }
69  
70      @Test
71      public void doesNotBackoffBelowOneConnection() {
72          connPerRoute.setMaxPerRoute(route, 1);
73          impl.backOff(route);
74          assertEquals(1, connPerRoute.getMaxPerRoute(route));
75      }
76  
77      @Test
78      public void increasesByOneOnProbe() {
79          connPerRoute.setMaxPerRoute(route, 2);
80          impl.probe(route);
81          assertEquals(3, connPerRoute.getMaxPerRoute(route));
82      }
83  
84      @Test
85      public 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      public void backoffDoesNotAdjustDuringCoolDownPeriod() {
95          connPerRoute.setMaxPerRoute(route, 4);
96          final long now = System.currentTimeMillis();
97          clock.setCurrentTime(now);
98          impl.backOff(route);
99          final long max = connPerRoute.getMaxPerRoute(route);
100         clock.setCurrentTime(now + 1);
101         impl.backOff(route);
102         assertEquals(max, connPerRoute.getMaxPerRoute(route));
103     }
104 
105     @Test
106     public void backoffStillAdjustsAfterCoolDownPeriod() {
107         connPerRoute.setMaxPerRoute(route, 8);
108         final long now = System.currentTimeMillis();
109         clock.setCurrentTime(now);
110         impl.backOff(route);
111         final long max = connPerRoute.getMaxPerRoute(route);
112         clock.setCurrentTime(now + 10 * 1000L);
113         impl.backOff(route);
114         assertTrue(max == 1 || max > connPerRoute.getMaxPerRoute(route));
115     }
116 
117     @Test
118     public void probeDoesNotAdjustDuringCooldownPeriod() {
119         connPerRoute.setMaxPerRoute(route, 4);
120         final long now = System.currentTimeMillis();
121         clock.setCurrentTime(now);
122         impl.probe(route);
123         final long max = connPerRoute.getMaxPerRoute(route);
124         clock.setCurrentTime(now + 1);
125         impl.probe(route);
126         assertEquals(max, connPerRoute.getMaxPerRoute(route));
127     }
128 
129     @Test
130     public void probeStillAdjustsAfterCoolDownPeriod() {
131         connPerRoute.setMaxPerRoute(route, 8);
132         final long now = System.currentTimeMillis();
133         clock.setCurrentTime(now);
134         impl.probe(route);
135         final long max = connPerRoute.getMaxPerRoute(route);
136         clock.setCurrentTime(now + 10 * 1000L);
137         impl.probe(route);
138         assertTrue(max < connPerRoute.getMaxPerRoute(route));
139     }
140 
141     @Test
142     public void willBackoffImmediatelyEvenAfterAProbe() {
143         connPerRoute.setMaxPerRoute(route, 8);
144         final long now = System.currentTimeMillis();
145         clock.setCurrentTime(now);
146         impl.probe(route);
147         final long max = connPerRoute.getMaxPerRoute(route);
148         clock.setCurrentTime(now + 1);
149         impl.backOff(route);
150         assertTrue(connPerRoute.getMaxPerRoute(route) < max);
151     }
152 
153     @Test
154     public void backOffFactorIsConfigurable() {
155         connPerRoute.setMaxPerRoute(route, 10);
156         impl.setBackoffFactor(0.9);
157         impl.backOff(route);
158         assertEquals(9, connPerRoute.getMaxPerRoute(route));
159     }
160 
161     @Test
162     public void coolDownPeriodIsConfigurable() {
163         long cd = new Random().nextLong() / 2;
164         if (cd < 0) {
165             cd *= -1;
166         }
167         if (cd < 1) {
168             cd++;
169         }
170         final long now = System.currentTimeMillis();
171         impl.setCoolDown(TimeValue.ofMilliseconds(cd));
172         clock.setCurrentTime(now);
173         impl.probe(route);
174         final int max0 = connPerRoute.getMaxPerRoute(route);
175         clock.setCurrentTime(now);
176         impl.probe(route);
177         assertEquals(max0, connPerRoute.getMaxPerRoute(route));
178         clock.setCurrentTime(now + cd + 1);
179         impl.probe(route);
180         assertTrue(max0 < connPerRoute.getMaxPerRoute(route));
181     }
182 }