RESOLVED - issue BATCH-198: Make backoff policy tests less sensitive to virtualisation
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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 < 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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user