diff --git a/src/main/java/org/springframework/retry/backoff/ExponentialRandomBackOffPolicy.java b/src/main/java/org/springframework/retry/backoff/ExponentialRandomBackOffPolicy.java index ed66e9f..2f231b0 100644 --- a/src/main/java/org/springframework/retry/backoff/ExponentialRandomBackOffPolicy.java +++ b/src/main/java/org/springframework/retry/backoff/ExponentialRandomBackOffPolicy.java @@ -40,6 +40,7 @@ import java.util.Random; * {@link ExponentialRandomBackOffPolicy} may yield [50, 100, 100, 100, 600] * or [50, 100, 150, 400, 800] * @author Jon Travis + * @author Dave Syer */ public class ExponentialRandomBackOffPolicy extends ExponentialBackOffPolicy { /** @@ -56,19 +57,17 @@ public class ExponentialRandomBackOffPolicy extends ExponentialBackOffPolicy { static class ExponentialRandomBackOffContext extends ExponentialBackOffPolicy.ExponentialBackOffContext { private final Random r = new Random(); - private final long initialInterval; - private long intervalIdx; public ExponentialRandomBackOffContext(long expSeed, double multiplier, long maxInterval) { super(expSeed, multiplier, maxInterval); - this.initialInterval = expSeed; - this.intervalIdx = 0; + } + + @Override + public synchronized long getSleepAndIncrement() { + long next = super.getSleepAndIncrement(); + next = (long)(next*(1 + r.nextFloat()*(getMultiplier()-1))); + return next; } - @Override - protected synchronized long getNextInterval() { - intervalIdx++; - return initialInterval + initialInterval * Math.max(1, r.nextInt((int)Math.pow(getMultiplier(), intervalIdx))); - } } } diff --git a/src/main/java/org/springframework/retry/backoff/SleepingBackOffPolicy.java b/src/main/java/org/springframework/retry/backoff/SleepingBackOffPolicy.java index 96d2ea6..c18d0c5 100644 --- a/src/main/java/org/springframework/retry/backoff/SleepingBackOffPolicy.java +++ b/src/main/java/org/springframework/retry/backoff/SleepingBackOffPolicy.java @@ -20,7 +20,7 @@ package org.springframework.retry.backoff; * A interface which can be mixed in by {@link BackOffPolicy}s indicating that they sleep * when backing off. */ -public interface SleepingBackOffPolicy extends BackOffPolicy { +public interface SleepingBackOffPolicy> extends BackOffPolicy { /** * Clone the policy and return a new policy which uses the passed sleeper. * diff --git a/src/main/java/org/springframework/retry/support/RetrySimulation.java b/src/main/java/org/springframework/retry/support/RetrySimulation.java index 1475646..3d328e7 100644 --- a/src/main/java/org/springframework/retry/support/RetrySimulation.java +++ b/src/main/java/org/springframework/retry/support/RetrySimulation.java @@ -16,99 +16,99 @@ package org.springframework.retry.support; - -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * The results of a simulation. */ public class RetrySimulation { - private final List sleepSequences = new ArrayList(); - private final Map sleepHistogram = new HashMap(); - /** - * Add a sequence of sleeps to the simulation. - */ - public void addSequence(List sleeps) { - for (Long sleep : sleeps) { - Long existingHisto = sleepHistogram.get(sleep); - if (existingHisto == null) { - sleepHistogram.put(sleep, 1l); - } else { - sleepHistogram.put(sleep, existingHisto + 1); - } - } + private final List sleepSequences = new ArrayList(); - sleepSequences.add(new SleepSequence(sleeps)); - } + private final List sleepHistogram = new ArrayList(); - /** - * @return Returns a list of all the unique sleep values which were executed within - * all simulations. - */ - public List getUniqueSleeps() { - List res = new ArrayList(sleepHistogram.keySet()); - Collections.sort(res); - return res; - } + public RetrySimulation() { + } - /** - * @return the count of each sleep which was seen throughout all sleeps. - * histogram[i] = sum(getUniqueSleeps()[i]) - */ - public List getUniqueSleepsHistogram() { - List res = new ArrayList(sleepHistogram.size()); - for (Long sleep : getUniqueSleeps()) { - res.add(sleepHistogram.get(sleep)); - } - return res; - } + /** + * Add a sequence of sleeps to the simulation. + */ + public void addSequence(List sleeps) { + sleepHistogram.addAll(sleeps); + sleepSequences.add(new SleepSequence(sleeps)); + } - /** - * @return the longest total time slept by a retry sequence. - */ - public SleepSequence getLongestTotalSleepSequence() { - SleepSequence longest = null; - for (SleepSequence sequence : sleepSequences) { - if (longest == null || sequence.getTotalSleep() > longest.getTotalSleep()) { - longest = sequence; - } - } - return longest; - } + /** + * @return Returns a list of all the unique sleep values which were executed within all simulations. + */ + public List getPercentiles() { + List res = new ArrayList(); + for (double percentile : new double[] { 10, 20, 30, 40, 50, 60, 70, 80, 90 }) { + res.add(getPercentile(percentile / 100)); + } + return res; + } - public static class SleepSequence { - private final List sleeps; - private final long longestSleep; - private final long totalSleep; + public double getPercentile(double p) { + Collections.sort(sleepHistogram); + int size = sleepHistogram.size(); + double pos = p * (size - 1); + int i0 = (int) pos; + int i1 = i0 + 1; + double weight = pos - i0; + return sleepHistogram.get(i0) * (1 - weight) + sleepHistogram.get(i1) * weight; - public SleepSequence(List sleeps) { - this.sleeps = sleeps; - this.longestSleep = Collections.max(sleeps); - long totalSleep = 0; - for (Long sleep : sleeps) { - totalSleep += sleep; - } - this.totalSleep = totalSleep; - } + } - public List getSleeps() { - return sleeps; - } + /** + * @return the longest total time slept by a retry sequence. + */ + public SleepSequence getLongestTotalSleepSequence() { + SleepSequence longest = null; + for (SleepSequence sequence : sleepSequences) { + if (longest == null || sequence.getTotalSleep() > longest.getTotalSleep()) { + longest = sequence; + } + } + return longest; + } - /** - * Returns the longest individual sleep within this sequence. - */ - public long getLongestSleep() { - return longestSleep; - } + public static class SleepSequence { + private final List sleeps; - public long getTotalSleep() { - return totalSleep; - } + private final long longestSleep; - public String toString() { - return "totalSleep=" + totalSleep + ": " + sleeps.toString(); - } - } + private final long totalSleep; + + public SleepSequence(List sleeps) { + this.sleeps = sleeps; + this.longestSleep = Collections.max(sleeps); + long totalSleep = 0; + for (Long sleep : sleeps) { + totalSleep += sleep; + } + this.totalSleep = totalSleep; + } + + public List getSleeps() { + return sleeps; + } + + /** + * Returns the longest individual sleep within this sequence. + */ + public long getLongestSleep() { + return longestSleep; + } + + public long getTotalSleep() { + return totalSleep; + } + + public String toString() { + return "totalSleep=" + totalSleep + ": " + sleeps.toString(); + } + } } diff --git a/src/main/java/org/springframework/retry/support/RetrySimulator.java b/src/main/java/org/springframework/retry/support/RetrySimulator.java index 39e7e0e..e848e34 100644 --- a/src/main/java/org/springframework/retry/support/RetrySimulator.java +++ b/src/main/java/org/springframework/retry/support/RetrySimulator.java @@ -16,15 +16,15 @@ package org.springframework.retry.support; +import java.util.ArrayList; +import java.util.List; + import org.springframework.retry.RetryCallback; import org.springframework.retry.RetryContext; import org.springframework.retry.RetryPolicy; import org.springframework.retry.backoff.Sleeper; import org.springframework.retry.backoff.SleepingBackOffPolicy; -import java.util.ArrayList; -import java.util.List; - /** * A {@link RetrySimulator} is a tool for exercising retry + backoff operations. * @@ -49,10 +49,11 @@ import java.util.List; * @author Jon Travis */ public class RetrySimulator { - private final SleepingBackOffPolicy backOffPolicy; + + private final SleepingBackOffPolicy backOffPolicy; private final RetryPolicy retryPolicy; - public RetrySimulator(SleepingBackOffPolicy backOffPolicy, RetryPolicy retryPolicy) { + public RetrySimulator(SleepingBackOffPolicy backOffPolicy, RetryPolicy retryPolicy) { this.backOffPolicy = backOffPolicy; this.retryPolicy = retryPolicy; } @@ -78,14 +79,14 @@ public class RetrySimulator { */ public List executeSingleSimulation() { StealingSleeper stealingSleeper = new StealingSleeper(); - SleepingBackOffPolicy stealingBackoff = backOffPolicy.withSleeper(stealingSleeper); + SleepingBackOffPolicy stealingBackoff = backOffPolicy.withSleeper(stealingSleeper); RetryTemplate template = new RetryTemplate(); template.setBackOffPolicy(stealingBackoff); template.setRetryPolicy(retryPolicy); try { - template.execute(new FailingRetryCallback()); + template.execute(new FailingRetryCallback()); } catch(FailingRetryException e) { } catch(Exception e) { @@ -95,7 +96,7 @@ public class RetrySimulator { return stealingSleeper.getSleeps(); } - static class FailingRetryCallback implements RetryCallback { + static class FailingRetryCallback implements RetryCallback { public Object doWithRetry(RetryContext context) throws Exception { throw new FailingRetryException(); } diff --git a/src/test/java/org/springframework/retry/backoff/ExponentialRandomBackOffPolicyTests.java b/src/test/java/org/springframework/retry/backoff/ExponentialRandomBackOffPolicyTests.java index aeacb02..496253b 100644 --- a/src/test/java/org/springframework/retry/backoff/ExponentialRandomBackOffPolicyTests.java +++ b/src/test/java/org/springframework/retry/backoff/ExponentialRandomBackOffPolicyTests.java @@ -16,15 +16,14 @@ package org.springframework.retry.backoff; +import java.util.List; + import junit.framework.TestCase; -import org.springframework.retry.RetryContext; -import org.springframework.retry.RetryPolicy; + import org.springframework.retry.policy.SimpleRetryPolicy; import org.springframework.retry.support.RetrySimulation; import org.springframework.retry.support.RetrySimulator; -import java.util.*; - public class ExponentialRandomBackOffPolicyTests extends TestCase { static final int NUM_TRIALS = 10000; static final int MAX_RETRIES = 6; @@ -53,9 +52,7 @@ public class ExponentialRandomBackOffPolicyTests extends TestCase { assertEquals(MAX_RETRIES - 1, sleeps.size()); long initialInterval = backOffPolicy.getInitialInterval(); for (int i=0; i allSleeps = simulation.getUniqueSleeps(); - for (int j=0; j 1000); - } - } + RetrySimulator simulator = new RetrySimulator(backOffPolicy, retryPolicy); + RetrySimulation simulation = simulator.executeSimulation(10000); + System.out.println(backOffPolicy); + System.out.println("Longest sequence " + simulation.getLongestTotalSleepSequence()); + System.out.println("Percentiles: " + simulation.getPercentiles()); + + assertTrue(simulation.getPercentiles().size() > 4); + } } diff --git a/template.mf b/template.mf index 290dcac..cbb20ae 100644 --- a/template.mf +++ b/template.mf @@ -5,6 +5,7 @@ Bundle-ManifestVersion: 2 Import-Template: org.springframework.beans.*;version="[3.0.0, 4.0.0)", org.springframework.context.*;version="[3.0.0, 4.0.0)", + org.springframework.aop.*;version="[3.0.0, 4.0.0)", org.springframework.core.*;version="[3.0.0, 4.0.0)", org.springframework.jdbc.*;version="[3.0.0, 4.0.0)", org.springframework.stereotype.*;version="[3.0.0, 4.0.0)", @@ -14,4 +15,5 @@ Import-Template: org.springframework.web.*;version="[3.0.0, 4.0.0)", org.springframework.validation.*;version="[3.0.0, 4.0.0)", org.springframework.util;version="[3.0.0, 4.0.0)", + org.aopalliance.*;version="[1.0.0, 2.0.0)", org.apache.commons.logging;version="[1.1.1, 2.0.0)"