/*
* Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights
* Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is
* distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either
* express or implied. See the License for the specific language
* governing
* permissions and limitations under the License.
*/
package com.amazonaws.retry;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class PredefinedBackoffStrategiesTest {
private static final int BASE_DELAY = 100;
private static final int BOUNDED_MAX_DELAY = 20000;
private static final int UNBOUNDED_MAX_DELAY = Integer.MAX_VALUE;
private static final int[] EXPONENTIAL_BACKOFF_VALUES = new int[]{100, 200, 400, 800, 1600, 3200, 6400, 12800, 20000, 20000};
@Test
public void testFullJitterStrategy() {
int[] expectedLowerBound = new int[10];
expectInRange(new PredefinedBackoffStrategies.FullJitterBackoffStrategy(BASE_DELAY, BOUNDED_MAX_DELAY), expectedLowerBound, EXPONENTIAL_BACKOFF_VALUES);
expectMeanIncreasing(new PredefinedBackoffStrategies.FullJitterBackoffStrategy(BASE_DELAY, UNBOUNDED_MAX_DELAY));
expectNonZeroStandardDeviation(new PredefinedBackoffStrategies.FullJitterBackoffStrategy(BASE_DELAY, UNBOUNDED_MAX_DELAY));
}
@Test
public void testEqualJitterStrategy() {
int[] expectedLowerBound = new int[]{50, 100, 200, 400, 800, 1600, 3200, 6400, 10000, 10000};
expectInRange(new PredefinedBackoffStrategies.EqualJitterBackoffStrategy(BASE_DELAY, BOUNDED_MAX_DELAY), expectedLowerBound, EXPONENTIAL_BACKOFF_VALUES);
expectMeanIncreasing(new PredefinedBackoffStrategies.EqualJitterBackoffStrategy(BASE_DELAY, UNBOUNDED_MAX_DELAY));
expectNonZeroStandardDeviation(new PredefinedBackoffStrategies.EqualJitterBackoffStrategy(BASE_DELAY, UNBOUNDED_MAX_DELAY));
}
@Test
public void testExponentialBackoffStrategy() {
RetryPolicy.BackoffStrategy exponential =
new PredefinedBackoffStrategies.ExponentialBackoffStrategy(BASE_DELAY, BOUNDED_MAX_DELAY);
for (int attempt = 0; attempt < 10; attempt++) {
for (int i = 0; i < 10000; i++) {
long delay = exponential.delayBeforeNextRetry(null, null, attempt);
Assert.assertTrue("Expected a fixed delay value", delay == EXPONENTIAL_BACKOFF_VALUES[attempt]);
}
}
}
@Test
public void testHandleOverflow() {
int maxInt = Integer.MAX_VALUE;
RetryPolicy.BackoffStrategy fullJitter = new PredefinedBackoffStrategies.FullJitterBackoffStrategy(BASE_DELAY, maxInt);
RetryPolicy.BackoffStrategy equalJitter = new PredefinedBackoffStrategies.EqualJitterBackoffStrategy(BASE_DELAY, maxInt);
RetryPolicy.BackoffStrategy exponential = new PredefinedBackoffStrategies.ExponentialBackoffStrategy(BASE_DELAY, maxInt);
int maxRetries = 40;
for (int i = 0; i < maxRetries; i++) {
long fullJitterDelay = fullJitter.delayBeforeNextRetry(null, null, i);
Assert.assertTrue(fullJitterDelay >= 0);
Assert.assertTrue(fullJitterDelay <= maxInt);
long equalJitterDelay = equalJitter.delayBeforeNextRetry(null, null, i);
Assert.assertTrue(equalJitterDelay >= 0);
Assert.assertTrue(equalJitterDelay <= maxInt);
long exponentialJitterDelay = exponential.delayBeforeNextRetry(null, null, i);
Assert.assertTrue(exponentialJitterDelay >= 0);
Assert.assertTrue(exponentialJitterDelay <= maxInt);
}
}
@Test
public void testMinimumValuesReturnedByBackoffStrategies() {
int maxInt = Integer.MAX_VALUE;
int value = 1;
RetryPolicy.BackoffStrategy fullJitter = new PredefinedBackoffStrategies.FullJitterBackoffStrategy(value, value);
RetryPolicy.BackoffStrategy equalJitter = new PredefinedBackoffStrategies.EqualJitterBackoffStrategy(value, value);
RetryPolicy.BackoffStrategy exponential = new PredefinedBackoffStrategies.ExponentialBackoffStrategy(value, value);
long fullJitterDelay = fullJitter.delayBeforeNextRetry(null, null, 0);
Assert.assertTrue(fullJitterDelay >= 0);
Assert.assertTrue(fullJitterDelay <= maxInt);
long equalJitterDelay = equalJitter.delayBeforeNextRetry(null, null, 0);
Assert.assertTrue(equalJitterDelay >= 0);
Assert.assertTrue(equalJitterDelay <= maxInt);
long exponentialJitterDelay = exponential.delayBeforeNextRetry(null, null, 0);
Assert.assertTrue(exponentialJitterDelay >= 0);
Assert.assertTrue(exponentialJitterDelay <= maxInt);
}
/*
* The max value that can be returned by ExponentialBackoffStrategy.delayBeforeNextRetry is Integer.MAX_VALUE
*/
@Test
public void test_MaxValue_Returned_InExponentialBackoffDelay_IsEqualTo_IntegerMax() {
int maxInt = Integer.MAX_VALUE;
RetryPolicy.BackoffStrategy exponential = new PredefinedBackoffStrategies.ExponentialBackoffStrategy(maxInt, maxInt);
Assert.assertEquals(maxInt, exponential.delayBeforeNextRetry(null, null, 40));
}
private void expectInRange(RetryPolicy.BackoffStrategy strategy, int[] expectedLowerBound, int[] expectedUpperBound) {
for (int attempt = 0; attempt < 10; attempt++) {
for (int i = 0; i < 10000; i++) {
long delay = strategy.delayBeforeNextRetry(null, null, attempt);
Assert.assertTrue("Delay should always be greater or equal to expected lower bound", delay >= expectedLowerBound[attempt]);
Assert.assertTrue("Delay should be within expected upper bound", delay <= expectedUpperBound[attempt]);
}
}
}
private void expectMeanIncreasing(RetryPolicy.BackoffStrategy strategy) {
double[] mean = new double[10];
for (int attempt = 0; attempt < 10; attempt++) {
long sum = 0L;
for (int i = 0; i < 10000; i++) {
sum += strategy.delayBeforeNextRetry(null, null, attempt);
}
mean[attempt] = sum / 10000;
}
for (int attempt = 1; attempt < 10; attempt++) {
Assert.assertTrue("Average delay from this attempt should be greater than that of the previous attempt",
mean[attempt] > mean[attempt - 1]);
}
}
private void expectNonZeroStandardDeviation(RetryPolicy.BackoffStrategy strategy) {
for (double value : getStandardDeviationValues(strategy)) {
Assert.assertTrue("Standard deviation should be non-zero", value > 0);
}
}
private List<Double> getStandardDeviationValues(RetryPolicy.BackoffStrategy strategy) {
List<Double> standardDeviationValues = new ArrayList<Double>();
for (int attempt = 0; attempt < 10; attempt++) {
List<Long> delayValues = new ArrayList<Long>();
for (int i = 0; i < 10000; i++) {
delayValues.add(strategy.delayBeforeNextRetry(null, null, attempt));
}
long sum = 0L;
for (long value : delayValues) {
sum += value;
}
double mean = sum / delayValues.size();
double temp = 0L;
for (long value : delayValues) {
temp += Math.pow((mean - value), 2);
}
double variance = temp / delayValues.size();
standardDeviationValues.add(Math.sqrt(variance));
}
return standardDeviationValues;
}
}