Skip to content

Commit

Permalink
Fixes the exponential backoff for small delay values
Browse files Browse the repository at this point in the history
  • Loading branch information
zack-shoylev committed Jan 21, 2015
1 parent c902fbf commit f5523c9
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.jclouds.http.handlers;

import static java.lang.Math.max;
import static org.jclouds.http.HttpUtils.releasePayload;

import java.io.IOException;
Expand Down Expand Up @@ -124,10 +125,16 @@ public void imposeBackoffExponentialDelay(long period, int pow, int failureCount

public void imposeBackoffExponentialDelay(long period, long maxPeriod, int pow, int failureCount, int max,
String commandDescription) {
if (period == 0) {
// Essentially disables the exponential backoff
logger.debug("Retry %d/%d: delaying for %d ms: %s", failureCount, max, 0, commandDescription);
return;
}
long delayMs = (long) (period * Math.pow(failureCount, pow));
// Add random delay to avoid thundering herd problem when multiple
// simultaneous failed requests retry after sleeping for the same delay.
delayMs += new Random().nextInt((int) (delayMs / 10));
// Throws an exception for a value of 0
delayMs += new Random().nextInt((int) (max(delayMs / 10, 1) ));
delayMs = delayMs > maxPeriod ? maxPeriod : delayMs;
logger.debug("Retry %d/%d: delaying for %d ms: %s", failureCount, max, delayMs, commandDescription);
try {
Expand All @@ -136,5 +143,4 @@ public void imposeBackoffExponentialDelay(long period, long maxPeriod, int pow,
Throwables.propagate(e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,42 @@ void testExponentialBackoffDelayDefaultMaxInterval500() throws InterruptedExcept

}

@Test
void testExponentialBackoffDelaySmallInterval5() throws InterruptedException {
long period = 5;
long acceptableDelay = period - 1;

long startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(period, 2, 1, 5, "TEST FAILURE: 1");
long elapsedTime = (System.nanoTime() - startTime) / 1000000;
assert elapsedTime >= period - 1 : elapsedTime;
assertTrue(elapsedTime < period + acceptableDelay);
}

@Test
void testExponentialBackoffDelaySmallInterval1() throws InterruptedException {
long period = 1;
long acceptableDelay = 5;

long startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(period, 2, 1, 5, "TEST FAILURE: 1");
long elapsedTime = (System.nanoTime() - startTime) / 1000000;
assert elapsedTime >= period - 1 : elapsedTime;
assertTrue(elapsedTime < period + acceptableDelay);
}

@Test
void testExponentialBackoffDelaySmallInterval0() throws InterruptedException {
long period = 0;
long acceptableDelay = 5;

long startTime = System.nanoTime();
handler.imposeBackoffExponentialDelay(period, 2, 1, 5, "TEST FAILURE: 1");
long elapsedTime = (System.nanoTime() - startTime) / 1000000;
assert elapsedTime >= period - 1 : elapsedTime;
assertTrue(elapsedTime < period + acceptableDelay);
}

@Test
void testClosesInputStream() throws InterruptedException, IOException, SecurityException, NoSuchMethodException {
HttpCommand command = createCommand();
Expand Down Expand Up @@ -126,7 +162,7 @@ public int available() throws IOException {
private final Function<Invocation, HttpRequest> processor = ContextBuilder
.newBuilder(AnonymousProviderMetadata.forApiOnEndpoint(IntegrationTestClient.class, "http://localhost"))
.buildInjector().getInstance(RestAnnotationProcessor.class);


private HttpCommand createCommand() throws SecurityException, NoSuchMethodException {
Invokable<IntegrationTestClient, String> method = method(IntegrationTestClient.class, "download", String.class);
Expand Down

0 comments on commit f5523c9

Please sign in to comment.