Fix features BackoffManager Unit Tests in Resource-Constrained Environments. (#494)
This commit improves the reliability of BackoffManager unit tests by replacing the use of Thread.sleep() with a more robust approach that manipulates lastRouteProbes to simulate the cooldown period. This enhancement ensures that the tests run successfully even in resource-constrained environments, making them more resilient and reliable.
This commit is contained in:
parent
f6a37780cf
commit
5c69779f7d
|
@ -30,6 +30,8 @@ package org.apache.hc.client5.http.impl.classic;
|
|||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
@ -97,60 +99,97 @@ public class TestAIMDBackoffManager {
|
|||
|
||||
@Test
|
||||
public void backoffDoesNotAdjustDuringCoolDownPeriod() {
|
||||
// Arrange
|
||||
connPerRoute.setMaxPerRoute(route, 4);
|
||||
impl.backOff(route);
|
||||
final long max = connPerRoute.getMaxPerRoute(route);
|
||||
|
||||
// Replace Thread.sleep(1) with busy waiting
|
||||
final long end = System.currentTimeMillis() + 1;
|
||||
while (System.currentTimeMillis() < end) {
|
||||
// Busy waiting
|
||||
}
|
||||
|
||||
// Act
|
||||
impl.backOff(route);
|
||||
assertEquals(max, connPerRoute.getMaxPerRoute(route));
|
||||
final long max1 = connPerRoute.getMaxPerRoute(route);
|
||||
|
||||
// Manipulate lastRouteBackoffs to simulate that not enough time has passed
|
||||
final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
|
||||
lastRouteBackoffs.put(route, Instant.now().minusMillis(1));
|
||||
|
||||
// Act again
|
||||
impl.backOff(route);
|
||||
final long max2 = connPerRoute.getMaxPerRoute(route);
|
||||
|
||||
// Assert
|
||||
assertEquals(max1, max2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void backoffStillAdjustsAfterCoolDownPeriod() throws InterruptedException {
|
||||
public void backoffStillAdjustsAfterCoolDownPeriod() {
|
||||
// Arrange: Initialize the maximum number of connections for a route to 8
|
||||
connPerRoute.setMaxPerRoute(route, 8);
|
||||
|
||||
// Act: Perform the first backoff operation
|
||||
impl.backOff(route);
|
||||
final long max = connPerRoute.getMaxPerRoute(route);
|
||||
Thread.sleep(DEFAULT_COOL_DOWN_MS + 100); // Sleep for cooldown period + 100 ms
|
||||
final long initialMax = connPerRoute.getMaxPerRoute(route);
|
||||
|
||||
// Act: Simulate that the cooldown period has passed
|
||||
final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
|
||||
lastRouteBackoffs.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS + 1));
|
||||
|
||||
// Act: Perform the second backoff operation
|
||||
impl.backOff(route);
|
||||
assertTrue(max == 1 || max > connPerRoute.getMaxPerRoute(route));
|
||||
final long finalMax = connPerRoute.getMaxPerRoute(route);
|
||||
|
||||
// Assert: Verify that the maximum number of connections has decreased or reached the minimum limit (1)
|
||||
if (initialMax != 1) {
|
||||
assertTrue(finalMax < initialMax, "Max connections should decrease after cooldown");
|
||||
} else {
|
||||
assertEquals(1, finalMax, "Max connections should remain 1 if it's already at the minimum");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void probeDoesNotAdjustDuringCooldownPeriod() {
|
||||
// Arrange
|
||||
connPerRoute.setMaxPerRoute(route, 4);
|
||||
impl.probe(route);
|
||||
final long max = connPerRoute.getMaxPerRoute(route);
|
||||
|
||||
// Replace Thread.sleep(1) with busy waiting
|
||||
final long end = System.currentTimeMillis() + 1;
|
||||
while (System.currentTimeMillis() < end) {
|
||||
// Busy waiting
|
||||
}
|
||||
|
||||
// First probe
|
||||
impl.probe(route);
|
||||
assertEquals(max, connPerRoute.getMaxPerRoute(route));
|
||||
final long max1 = connPerRoute.getMaxPerRoute(route);
|
||||
|
||||
// Manipulate lastRouteProbes to simulate that not enough time has passed
|
||||
final Map<HttpRoute, Instant> lastRouteProbes = impl.getLastRouteProbes();
|
||||
lastRouteProbes.put(route, Instant.now().minusMillis(1));
|
||||
|
||||
// Second probe
|
||||
impl.probe(route);
|
||||
final long max2 = connPerRoute.getMaxPerRoute(route);
|
||||
|
||||
// Assert
|
||||
assertEquals(max1, max2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void probeStillAdjustsAfterCoolDownPeriod() throws InterruptedException {
|
||||
public void probeStillAdjustsAfterCoolDownPeriod() {
|
||||
connPerRoute.setMaxPerRoute(route, 8);
|
||||
|
||||
// First probe
|
||||
impl.probe(route);
|
||||
final long max = connPerRoute.getMaxPerRoute(route);
|
||||
Thread.sleep(DEFAULT_COOL_DOWN_MS + 100); // Sleep for cooldown period + 1 ms
|
||||
|
||||
// Manipulate lastRouteProbes to simulate that enough time has passed for the cooldown period
|
||||
final Map<HttpRoute, Instant> lastRouteProbes = impl.getLastRouteProbes();
|
||||
lastRouteProbes.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS + 1));
|
||||
|
||||
// Second probe
|
||||
impl.probe(route);
|
||||
|
||||
// Assert that the max connections have increased
|
||||
assertTrue(max < connPerRoute.getMaxPerRoute(route));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void willBackoffImmediatelyEvenAfterAProbe() {
|
||||
connPerRoute.setMaxPerRoute(route, 8);
|
||||
final long now = System.currentTimeMillis();
|
||||
impl.probe(route);
|
||||
final long max = connPerRoute.getMaxPerRoute(route);
|
||||
impl.backOff(route);
|
||||
|
@ -166,19 +205,26 @@ public class TestAIMDBackoffManager {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void coolDownPeriodIsConfigurable() throws InterruptedException {
|
||||
public void coolDownPeriodIsConfigurable() {
|
||||
final long cd = new Random().nextInt(500) + 500; // Random cooldown period between 500 and 1000 milliseconds
|
||||
impl.setCoolDown(TimeValue.ofMilliseconds(cd));
|
||||
|
||||
// Probe and check if the connection count remains the same during the cooldown period
|
||||
impl.probe(route);
|
||||
final int max0 = connPerRoute.getMaxPerRoute(route);
|
||||
Thread.sleep(cd / 2 + 100); // Sleep for half the cooldown period + 100 ms buffer
|
||||
|
||||
// Manipulate lastRouteProbes to simulate that not enough time has passed
|
||||
final Map<HttpRoute, Instant> lastRouteProbes = impl.getLastRouteProbes();
|
||||
lastRouteProbes.put(route, Instant.now().minusMillis(cd / 2));
|
||||
|
||||
// Probe again
|
||||
impl.probe(route);
|
||||
assertEquals(max0, connPerRoute.getMaxPerRoute(route));
|
||||
|
||||
// Probe and check if the connection count increases after the cooldown period
|
||||
Thread.sleep(cd / 2 + 100); // Sleep for the remaining half of the cooldown period + 100 ms buffer
|
||||
// Manipulate lastRouteProbes to simulate that enough time has passed
|
||||
lastRouteProbes.put(route, Instant.now().minusMillis(cd + 1));
|
||||
|
||||
// Probe again
|
||||
impl.probe(route);
|
||||
assertTrue(max0 < connPerRoute.getMaxPerRoute(route));
|
||||
}
|
||||
|
|
|
@ -27,15 +27,18 @@
|
|||
package org.apache.hc.client5.http.impl.classic;
|
||||
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.hc.client5.http.HttpRoute;
|
||||
import org.apache.hc.core5.http.HttpHost;
|
||||
import org.apache.hc.core5.util.TimeValue;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class TestLinearBackoffManager {
|
||||
|
||||
private LinearBackoffManager impl;
|
||||
|
@ -69,61 +72,79 @@ public class TestLinearBackoffManager {
|
|||
|
||||
@Test
|
||||
public void backoffDoesNotAdjustDuringCoolDownPeriod() {
|
||||
// Arrange
|
||||
connPerRoute.setMaxPerRoute(route, 4);
|
||||
impl.backOff(route);
|
||||
final long max = connPerRoute.getMaxPerRoute(route);
|
||||
// Replace Thread.sleep(1) with busy waiting
|
||||
final long end = System.currentTimeMillis() + 1;
|
||||
while (System.currentTimeMillis() < end) {
|
||||
// Busy waiting
|
||||
}
|
||||
|
||||
// Manipulate lastRouteBackoffs to simulate that not enough time has passed for the cooldown period
|
||||
final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
|
||||
lastRouteBackoffs.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS - 1));
|
||||
|
||||
// Act
|
||||
impl.backOff(route);
|
||||
|
||||
// Assert
|
||||
assertEquals(max, connPerRoute.getMaxPerRoute(route));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void backoffStillAdjustsAfterCoolDownPeriod() throws InterruptedException {
|
||||
public void backoffStillAdjustsAfterCoolDownPeriod() {
|
||||
// Arrange
|
||||
final LinearBackoffManager impl = new LinearBackoffManager(connPerRoute);
|
||||
impl.setCoolDown(TimeValue.ofMilliseconds(DEFAULT_COOL_DOWN_MS)); // Set the cool-down period
|
||||
connPerRoute.setMaxPerRoute(route, 4);
|
||||
impl.backOff(route);
|
||||
final int max1 = connPerRoute.getMaxPerRoute(route);
|
||||
|
||||
Thread.sleep(DEFAULT_COOL_DOWN_MS + 1); // Sleep for cooldown period + 1 ms
|
||||
// Manipulate lastRouteBackoffs to simulate that enough time has passed for the cooldown period
|
||||
final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
|
||||
lastRouteBackoffs.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS + 1));
|
||||
|
||||
// Act
|
||||
impl.backOff(route);
|
||||
final int max2 = connPerRoute.getMaxPerRoute(route);
|
||||
|
||||
// Assert
|
||||
assertTrue(max2 > max1);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void probeDoesNotAdjustDuringCooldownPeriod() {
|
||||
// Arrange
|
||||
connPerRoute.setMaxPerRoute(route, 4);
|
||||
impl.probe(route);
|
||||
final long max = connPerRoute.getMaxPerRoute(route);
|
||||
// Replace Thread.sleep(1) with busy waiting
|
||||
final long end = System.currentTimeMillis() + 1;
|
||||
while (System.currentTimeMillis() < end) {
|
||||
// Busy waiting
|
||||
}
|
||||
|
||||
// Manipulate lastRouteProbes to simulate that not enough time has passed for the cooldown period
|
||||
final Map<HttpRoute, Instant> lastRouteProbes = impl.getLastRouteProbes();
|
||||
lastRouteProbes.put(route, Instant.now());
|
||||
|
||||
// Act
|
||||
impl.probe(route);
|
||||
|
||||
// Assert
|
||||
assertEquals(max, connPerRoute.getMaxPerRoute(route));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void probeStillAdjustsAfterCoolDownPeriod() throws InterruptedException {
|
||||
public void probeStillAdjustsAfterCoolDownPeriod() {
|
||||
// Arrange
|
||||
connPerRoute.setMaxPerRoute(route, 4);
|
||||
impl.probe(route);
|
||||
Thread.sleep(DEFAULT_COOL_DOWN_MS + 1); // Sleep for cooldown period + 1 ms
|
||||
|
||||
// Manipulate lastRouteProbes to simulate that enough time has passed for the cooldown period
|
||||
final Map<HttpRoute, Instant> lastRouteProbes = impl.getLastRouteProbes();
|
||||
lastRouteProbes.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS + 1));
|
||||
|
||||
// Act
|
||||
impl.probe(route);
|
||||
final long newMax = connPerRoute.getMaxPerRoute(route);
|
||||
|
||||
// Assert
|
||||
assertEquals(2, newMax); // The cap is set to 2 by default
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSetPerHostConnectionCap() {
|
||||
connPerRoute.setMaxPerRoute(route, 5);
|
||||
|
@ -132,14 +153,18 @@ public class TestLinearBackoffManager {
|
|||
assertEquals(6, connPerRoute.getMaxPerRoute(route));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void probeUpdatesRemainingAttemptsIndirectly() throws InterruptedException {
|
||||
|
||||
public void probeUpdatesRemainingAttemptsIndirectly() {
|
||||
// Set initial max per route
|
||||
connPerRoute.setMaxPerRoute(route, 4);
|
||||
|
||||
// Apply backOff twice
|
||||
impl.backOff(route);
|
||||
Thread.sleep(DEFAULT_COOL_DOWN_MS + 1);
|
||||
|
||||
// Manipulate lastRouteBackoffs to simulate that enough time has passed for the cooldown period
|
||||
final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
|
||||
lastRouteBackoffs.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS + 1));
|
||||
|
||||
impl.backOff(route);
|
||||
|
||||
// Ensure that connection pool size has increased
|
||||
|
@ -148,8 +173,9 @@ public class TestLinearBackoffManager {
|
|||
// Apply probe once
|
||||
impl.probe(route);
|
||||
|
||||
// Wait for a longer cool down period
|
||||
Thread.sleep(DEFAULT_COOL_DOWN_MS * 2);
|
||||
// Manipulate lastRouteProbes to simulate that enough time has passed for the cooldown period
|
||||
final Map<HttpRoute, Instant> lastRouteProbes = impl.getLastRouteProbes();
|
||||
lastRouteProbes.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS * 2));
|
||||
|
||||
// Apply probe once more
|
||||
impl.probe(route);
|
||||
|
@ -159,14 +185,23 @@ public class TestLinearBackoffManager {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void linearIncrementTest() throws InterruptedException {
|
||||
public void linearIncrementTest() {
|
||||
final int initialMax = 4;
|
||||
connPerRoute.setMaxPerRoute(route, initialMax);
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
impl.backOff(route);
|
||||
assertEquals(initialMax + i, connPerRoute.getMaxPerRoute(route));
|
||||
Thread.sleep(DEFAULT_COOL_DOWN_MS + 1);
|
||||
|
||||
// Manipulate lastRouteBackoffs to simulate that enough time has passed for the cooldown period
|
||||
final Map<HttpRoute, Instant> lastRouteBackoffs = impl.getLastRouteBackoffs();
|
||||
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
// Simulate that enough time has passed for the cooldown period
|
||||
lastRouteBackoffs.put(route, Instant.now().minusMillis(DEFAULT_COOL_DOWN_MS + 1));
|
||||
|
||||
// Act
|
||||
impl.backOff(route);
|
||||
|
||||
// Assert
|
||||
assertEquals(initialMax + i, connPerRoute.getMaxPerRoute(route));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue