RESOLVED - issue BATCH-198: Make backoff policy tests less sensitive to virtualisation

This commit is contained in:
dsyer
2008-04-01 09:49:11 +00:00
parent afaa668347
commit c2bb83ce73
9 changed files with 217 additions and 46 deletions

View File

@@ -31,7 +31,6 @@ import org.springframework.util.ClassUtils;
*
* @author Rob Harrop
* @author Dave Syer
* @since 2.1
*/
public class ExponentialBackOffPolicy implements BackOffPolicy {
@@ -67,6 +66,16 @@ public class ExponentialBackOffPolicy implements BackOffPolicy {
*/
private volatile double multiplier = DEFAULT_MULTIPLIER;
private Sleeper sleeper = new ObjectWaitSleeper();
/**
* Public setter for the {@link Sleeper} strategy.
* @param sleeper the sleeper to set defaults to {@link ObjectWaitSleeper}.
*/
public void setSleeper(Sleeper sleeper) {
this.sleeper = sleeper;
}
/**
* Set the initial sleep interval value. Default is <code>1</code>
* millisecond. Cannot be set to a value less than one.
@@ -107,9 +116,7 @@ public class ExponentialBackOffPolicy implements BackOffPolicy {
public void backOff(BackOffContext backOffContext) throws BackOffInterruptedException {
ExponentialBackOffContext context = (ExponentialBackOffContext) backOffContext;
try {
synchronized (context) {
context.wait(context.getSleepAndIncrement());
}
sleeper.sleep(context.getSleepAndIncrement());
}
catch (InterruptedException e) {
throw new BackOffInterruptedException("Thread interrupted while sleeping", e);
@@ -130,7 +137,7 @@ public class ExponentialBackOffPolicy implements BackOffPolicy {
this.maxInterval = maxInterval;
}
public long getSleepAndIncrement() {
public synchronized long getSleepAndIncrement() {
long sleep = this.interval;
if (sleep > maxInterval) {
sleep = (long) maxInterval;

View File

@@ -25,7 +25,7 @@ package org.springframework.batch.retry.backoff;
* this may cause a single retry operation to have pauses of different
* intervals.
* @author Rob Harrop
* @since 2.1
* @author Dave Syer
*/
public class FixedBackOffPolicy extends StatelessBackOffPolicy {
@@ -39,6 +39,17 @@ public class FixedBackOffPolicy extends StatelessBackOffPolicy {
*/
private volatile long backOffPeriod = DEFAULT_BACK_OFF_PERIOD;
private Sleeper sleeper = new ObjectWaitSleeper();
/**
* Public setter for the {@link Sleeper} strategy.
* @param sleeper the sleeper to set defaults to {@link ObjectWaitSleeper}.
*/
public void setSleeper(Sleeper sleeper) {
this.sleeper = sleeper;
}
/**
* Set the back off period in milliseconds. Cannot be &lt; 1. Default value
* is 1000ms.
@@ -48,15 +59,12 @@ public class FixedBackOffPolicy extends StatelessBackOffPolicy {
}
/**
* Pause for the {@link #backOffPeriod} using {@link Thread#sleep}.
* Pause for the {@link #backOffPeriod}.
* @throws BackOffInterruptedException if interrupted during sleep.
*/
protected void doBackOff() throws BackOffInterruptedException {
try {
Object mutex = new Object();
synchronized (mutex) {
mutex.wait(this.backOffPeriod);
}
sleeper.sleep(backOffPeriod);
}
catch (InterruptedException e) {
throw new BackOffInterruptedException("Thread interrupted while sleeping", e);

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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 org.springframework.batch.retry.backoff;
/**
* Simple {@link Sleeper} implementation that just waits on a local Object.
*
* @author Dave Syer
*
*/
public class ObjectWaitSleeper implements Sleeper {
/*
* (non-Javadoc)
* @see org.springframework.batch.retry.backoff.Sleeper#sleep(long)
*/
public void sleep(long backOffPeriod) throws InterruptedException {
Object mutex = new Object();
synchronized (mutex) {
mutex.wait(backOffPeriod);
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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 org.springframework.batch.retry.backoff;
/**
* Strategy interface for backoff policies to delegate the pausing of execution.
*
* @author Dave Syer
*
*/
public interface Sleeper {
/**
* Pause for the specified period using whatever means available.
*
* @param backOffPeriod
*/
void sleep(long backOffPeriod) throws InterruptedException;
}

View File

@@ -24,7 +24,6 @@ import org.springframework.batch.retry.RetryContext;
*
* @author Rob Harrop
* @author Dave Syer
* @since 2.1
*/
public abstract class StatelessBackOffPolicy implements BackOffPolicy {

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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 org.springframework.batch.retry.backoff;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Simple {@link Sleeper} implementation that just waits on a local Object.
*
* @author Dave Syer
*
*/
public class DummySleeper implements Sleeper {
private List backOffs = new ArrayList();
/**
* Public getter for the long.
* @return the lastBackOff
*/
public long getLastBackOff() {
return ((Long) backOffs.get(backOffs.size()-1)).longValue();
}
public long[] getBackOffs() {
long[] result = new long[backOffs.size()];
int i = 0;
for (Iterator iterator = backOffs.iterator(); iterator.hasNext();) {
Long value = (Long) iterator.next();
result[i++] =value.longValue();
}
return result ;
}
/*
* (non-Javadoc)
* @see org.springframework.batch.retry.backoff.Sleeper#sleep(long)
*/
public void sleep(long backOffPeriod) throws InterruptedException {
this.backOffs.add(new Long(backOffPeriod));
}
}

View File

@@ -21,10 +21,11 @@ import junit.framework.TestCase;
/**
* @author Rob Harrop
* @author Dave Syer
* @since 2.1
*/
public class ExponentialBackOffPolicyTests extends TestCase {
private DummySleeper sleeper = new DummySleeper();
public void testSetMaxInterval() throws Exception {
ExponentialBackOffPolicy strategy = new ExponentialBackOffPolicy();
strategy.setMaxInterval(1000);
@@ -52,44 +53,34 @@ public class ExponentialBackOffPolicyTests extends TestCase {
public void testSingleBackOff() throws Exception {
ExponentialBackOffPolicy strategy = new ExponentialBackOffPolicy();
strategy.setSleeper(sleeper);
BackOffContext context = strategy.start(null);
long before = System.currentTimeMillis();
strategy.backOff(context);
long after = System.currentTimeMillis();
assertEqualsApprox(ExponentialBackOffPolicy.DEFAULT_INITIAL_INTERVAL, after - before, 30);
assertEquals(ExponentialBackOffPolicy.DEFAULT_INITIAL_INTERVAL, sleeper.getLastBackOff());
}
public void testMaximumBackOff() throws Exception {
ExponentialBackOffPolicy strategy = new ExponentialBackOffPolicy();
strategy.setMaxInterval(50);
strategy.setSleeper(sleeper);
BackOffContext context = strategy.start(null);
long before = System.currentTimeMillis();
strategy.backOff(context);
long after = System.currentTimeMillis();
assertEqualsApprox(50L, after - before, 15);
assertEquals(50, sleeper.getLastBackOff());
}
public void testMultiBackOff() throws Exception {
ExponentialBackOffPolicy strategy = new ExponentialBackOffPolicy();
long seed = 40; // not too small or Windoze won't resolve the difference
double multiplier = 1.2; // not too large or the test takes ages!
long seed = 40;
double multiplier = 1.2;
strategy.setInitialInterval(seed);
strategy.setMultiplier(multiplier);
strategy.setSleeper(sleeper);
BackOffContext context = strategy.start(null);
for (int x = 0; x < 5; x++) {
long before = System.currentTimeMillis();
strategy.backOff(context);
long after = System.currentTimeMillis();
assertFalse(after == before);
assertEqualsApprox(seed, after - before, 20);
assertEquals(seed, sleeper.getLastBackOff());
seed *= multiplier;
}
}
private void assertEqualsApprox(long desired, long actual, long variance) {
long lower = desired - variance;
long upper = desired + 5 * variance / 2;
assertTrue("Expected value to be between '" + lower + "' and '" + upper + "' but was '" + actual + "'",
lower <= actual);
}
}

View File

@@ -25,42 +25,38 @@ import junit.framework.TestCase;
*/
public class FixedBackOffPolicyTests extends TestCase {
private DummySleeper sleeper = new DummySleeper();
public void testSetBackoffPeriodNegative() throws Exception {
FixedBackOffPolicy strategy = new FixedBackOffPolicy();
strategy.setBackOffPeriod(-1000L);
long before = System.currentTimeMillis();
strategy.setSleeper(sleeper);
strategy.backOff(null);
long after = System.currentTimeMillis();
// We should see a zero backoff if we try to set it negative
assertEqualsApprox(0, after - before, 25);
assertEquals(1, sleeper.getBackOffs().length);
assertEquals(1, sleeper.getLastBackOff());
}
public void testSingleBackOff() throws Exception {
int backOffPeriod = 50;
FixedBackOffPolicy strategy = new FixedBackOffPolicy();
strategy.setBackOffPeriod(backOffPeriod);
long before = System.currentTimeMillis();
strategy.setSleeper(sleeper);
strategy.backOff(null);
long after = System.currentTimeMillis();
assertEqualsApprox(backOffPeriod, after - before, 25);
assertEquals(1, sleeper.getBackOffs().length);
assertEquals(backOffPeriod, sleeper.getLastBackOff());
}
public void testManyBackOffCalls() throws Exception {
int backOffPeriod = 50;
FixedBackOffPolicy strategy = new FixedBackOffPolicy();
strategy.setBackOffPeriod(backOffPeriod);
strategy.setSleeper(sleeper);
for (int x = 0; x < 10; x++) {
long before = System.currentTimeMillis();
strategy.backOff(null);
long after = System.currentTimeMillis();
assertEqualsApprox(backOffPeriod, after - before, 25);
assertEquals(backOffPeriod, sleeper.getLastBackOff());
}
assertEquals(10, sleeper.getBackOffs().length);
}
private void assertEqualsApprox(long desired, long actual, long variance) {
long lower = desired - variance;
long upper = desired + 2 * variance;
assertTrue("Expected value to be between '" + lower + "' and '" + upper + "' but was '" + actual + "'",
lower <= actual);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2006-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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 org.springframework.batch.retry.backoff;
import junit.framework.TestCase;
/**
* @author Dave Syer
*/
public class ObjectWaitSleeperTests extends TestCase {
public void testSingleBackOff() throws Exception {
long backOffPeriod = 50;
ObjectWaitSleeper strategy = new ObjectWaitSleeper();
long before = System.currentTimeMillis();
strategy.sleep(backOffPeriod);
long after = System.currentTimeMillis();
assertEqualsApprox(backOffPeriod, after - before, 25);
}
private void assertEqualsApprox(long desired, long actual, long variance) {
long lower = desired - variance;
long upper = desired + 2 * variance;
assertTrue("Expected value to be between '" + lower + "' and '" + upper + "' but was '" + actual + "'",
lower <= actual);
}
}