Skip to content

Commit

Permalink
Added CappedExponential Delay (#416)
Browse files Browse the repository at this point in the history
  • Loading branch information
shrsrivz authored Apr 4, 2023
1 parent 6232ac3 commit ae86852
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package software.amazon.cloudformation.proxy.delay;

import com.google.common.base.Preconditions;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

public class CappedExponential extends MinDelayAbstractBase {

final double powerBy;

final long startTimeInMillis;

final Duration maxDelay;

CappedExponential(Duration timeout, Duration minDelay, Double powerBy, Duration maxDelay) {
super(timeout, minDelay);
Preconditions.checkArgument(powerBy >= 1.0, "powerBy >= 1.0");
Preconditions.checkArgument(maxDelay != null && maxDelay.toMillis() >= 0, "maxDelay must be > 0");
Preconditions.checkArgument(maxDelay.compareTo(minDelay) >= 0, "maxDelay.compareTo(minDelay) >= 0");
this.powerBy = powerBy;
this.maxDelay = maxDelay;
this.startTimeInMillis = System.currentTimeMillis();
}

public static Builder of() {
return new Builder();
}

public static final class Builder extends MinDelayBasedBuilder<CappedExponential, Builder> {
private double powerBy = 2;
private Duration maxDelay =Duration.ofSeconds(20);

public CappedExponential.Builder powerBy(Double powerBy) {
this.powerBy = powerBy;
return this;
}

public CappedExponential.Builder maxDelay(Duration maxDelay) {
this.maxDelay = maxDelay;
return this;
}

@Override
public CappedExponential build() {
return new CappedExponential(timeout, minDelay, powerBy, maxDelay);
}
}

@Override
public Duration nextDelay(int attempt) {
if (attempt == 0) {
return minDelay;
}
if (System.currentTimeMillis() - startTimeInMillis > timeout.toMillis()) {
return Duration.ZERO;
}
long next = Math.round(minDelay.toMillis() * Math.pow(powerBy, attempt - 1));
return Duration.ofSeconds(Math.min(maxDelay.getSeconds(), TimeUnit.MILLISECONDS.toSeconds(next)));
}

}
52 changes: 47 additions & 5 deletions src/test/java/software/amazon/cloudformation/proxy/DelayTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import software.amazon.cloudformation.proxy.delay.Blended;
import software.amazon.cloudformation.proxy.delay.Constant;
import software.amazon.cloudformation.proxy.delay.Exponential;
import software.amazon.cloudformation.proxy.delay.MultipleOf;
import software.amazon.cloudformation.proxy.delay.ShiftByMultipleOf;
import software.amazon.cloudformation.proxy.delay.*;

public class DelayTest {

Expand Down Expand Up @@ -203,4 +199,50 @@ public void exponentialDelays() {
}
assertThat(3).isEqualTo(attempt);
}

@Test
public void cappedExponentialDelays() {
Duration MAX_DELAY = Duration.ofSeconds(15);
final Delay cappedExponential = CappedExponential.of()
.timeout(Duration.ofMinutes(20))
.maxDelay(MAX_DELAY)
.powerBy(1.3)
.minDelay(Duration.ofSeconds(1))
.build();
int[] results = {1, 1, 1, 1, 2, 2, 3, 4, 6, 8, 10, 13, 15, 15, 15, 15};
for (int tries = 0; tries <= 15; tries++) {
Duration delay = cappedExponential.nextDelay(tries);
assertThat(results[tries]).isEqualTo((int) delay.getSeconds());
if (tries >= 12) {
assertThat(MAX_DELAY.getSeconds()).isEqualTo(delay.getSeconds());
}
}

//If minDelay is not set, the retry is without delay.
final Delay cappedExponentialNoDelay = CappedExponential.of().timeout(Duration.ofSeconds(12)).build();
for (int tries = 0; tries <= 15; tries++) {
Duration delay = cappedExponentialNoDelay.nextDelay(tries);
assertThat(0).isEqualTo((int) delay.getSeconds());
if (tries >= 12) {
assertThat(0).isEqualTo(delay.getSeconds());
}
}

//If powerBy is not passed, it's set to default 2.
final Delay cappedExponentialNoPower = CappedExponential.of()
.timeout(Duration.ofMinutes(20))
.maxDelay(MAX_DELAY)
.minDelay(Duration.ofSeconds(1))
.build();

int[] resultsNoPower = {1, 1, 2, 4, 8, 15, 15, 15, 15, 15};
for (int tries = 0; tries <= 6; tries++) {
Duration delay = cappedExponentialNoPower.nextDelay(tries);
assertThat(resultsNoPower[tries]).isEqualTo((int) delay.getSeconds());
if (tries >= 5) {
assertThat(MAX_DELAY.getSeconds()).isEqualTo(delay.getSeconds());
}
}

}
}

0 comments on commit ae86852

Please sign in to comment.