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