Introduce java.time.Duration

* Introduce `java.time.Duration` variants into `RetryTemplateBuilder`

* Fix non-null assertion messages
This commit is contained in:
Andreas Ahlenstorf
2023-05-31 15:20:32 +02:00
committed by GitHub
parent daa04dbf1e
commit 41b7df83bb
2 changed files with 208 additions and 5 deletions

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.retry.support;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
@@ -83,6 +84,7 @@ import org.springframework.util.Assert;
* @author Aleksandr Shamukov
* @author Artem Bilan
* @author Kim In Hoi
* @author Andreas Ahlenstorf
* @since 1.3
*/
public class RetryTemplateBuilder {
@@ -122,16 +124,43 @@ public class RetryTemplateBuilder {
* @param timeout whole execution timeout in milliseconds
* @return this
* @see TimeoutRetryPolicy
* @deprecated Use {@link #withTimeout(long)} instead.
*/
@Deprecated(since = "2.0.2", forRemoval = true)
public RetryTemplateBuilder withinMillis(long timeout) {
Assert.isTrue(timeout > 0, "Timeout should be positive");
return withTimeout(timeout);
}
/**
* Retry until {@code timeout} has passed since the initial attempt.
* @param timeoutMillis timeout in milliseconds
* @return this
* @see TimeoutRetryPolicy
* @throws IllegalArgumentException if timeout is {@literal <=} 0 or if another retry
* policy has already been configured.
* @since 2.0.2
*/
public RetryTemplateBuilder withTimeout(long timeoutMillis) {
Assert.isTrue(timeoutMillis > 0, "timeoutMillis should be greater than 0");
Assert.isNull(this.baseRetryPolicy, "You have already selected another retry policy");
TimeoutRetryPolicy timeoutRetryPolicy = new TimeoutRetryPolicy();
timeoutRetryPolicy.setTimeout(timeout);
this.baseRetryPolicy = timeoutRetryPolicy;
this.baseRetryPolicy = new TimeoutRetryPolicy(timeoutMillis);
return this;
}
/**
* Retry until {@code timeout} has passed since the initial attempt.
* @param timeout duration for how long retries should be attempted
* @return this
* @see TimeoutRetryPolicy
* @throws IllegalArgumentException if timeout is {@code null} or 0, or if another
* retry policy has already been configured.
* @since 2.0.2
*/
public RetryTemplateBuilder withTimeout(Duration timeout) {
Assert.notNull(timeout, "timeout must not be null");
return withTimeout(timeout.toMillis());
}
/**
* Allows infinite retry, do not limit attempts by number or time.
* <p>
@@ -180,6 +209,27 @@ public class RetryTemplateBuilder {
return exponentialBackoff(initialInterval, multiplier, maxInterval, false);
}
/**
* Use exponential backoff policy. The formula of backoff period:
* <p>
* {@code currentInterval = Math.min(initialInterval * Math.pow(multiplier, retryNum), maxInterval)}
* <p>
* (for first attempt retryNum = 0)
* @param initialInterval initial sleep duration
* @param multiplier backoff interval multiplier
* @param maxInterval maximum backoff duration
* @return this
* @see ExponentialBackOffPolicy
* @throws IllegalArgumentException if initialInterval is {@code null}, multiplier is
* {@literal <=} 1, or if maxInterval is {@code null}
* @since 2.0.2
*/
public RetryTemplateBuilder exponentialBackoff(Duration initialInterval, double multiplier, Duration maxInterval) {
Assert.notNull(initialInterval, "initialInterval must not be null");
Assert.notNull(maxInterval, "maxInterval must not be null");
return exponentialBackoff(initialInterval.toMillis(), multiplier, maxInterval.toMillis(), false);
}
/**
* Use exponential backoff policy. The formula of backoff period (without randomness):
* <p>
@@ -210,6 +260,31 @@ public class RetryTemplateBuilder {
return this;
}
/**
* Use exponential backoff policy. The formula of backoff period (without randomness):
* <p>
* {@code currentInterval = Math.min(initialInterval * Math.pow(multiplier, retryNum), maxInterval)}
* <p>
* (for first attempt retryNum = 0)
* @param initialInterval initial sleep duration
* @param multiplier backoff interval multiplier
* @param maxInterval maximum backoff duration
* @param withRandom adds some randomness to backoff intervals. For details, see
* {@link ExponentialRandomBackOffPolicy}
* @return this
* @see ExponentialBackOffPolicy
* @see ExponentialRandomBackOffPolicy
* @throws IllegalArgumentException if initialInterval is {@code null}, multiplier is
* {@literal <=} 1, or maxInterval is {@code null}
* @since 2.0.2
*/
public RetryTemplateBuilder exponentialBackoff(Duration initialInterval, double multiplier, Duration maxInterval,
boolean withRandom) {
Assert.notNull(initialInterval, "initialInterval most not be null");
Assert.notNull(maxInterval, "maxInterval must not be null");
return this.exponentialBackoff(initialInterval.toMillis(), multiplier, maxInterval.toMillis(), withRandom);
}
/**
* Perform each retry after fixed amount of time.
* @param interval fixed interval in milliseconds
@@ -225,6 +300,24 @@ public class RetryTemplateBuilder {
return this;
}
/**
* Perform each retry after fixed amount of time.
* @param interval fixed backoff duration
* @return this
* @see FixedBackOffPolicy
* @throws IllegalArgumentException if another backoff policy has already been
* configured, interval is {@code null} or less than 1 millisecond
* @since 2.0.2
*/
public RetryTemplateBuilder fixedBackoff(Duration interval) {
Assert.notNull(interval, "interval must not be null");
long millis = interval.toMillis();
Assert.isTrue(millis >= 1, "interval is less than 1 millisecond");
return this.fixedBackoff(millis);
}
/**
* Use {@link UniformRandomBackOffPolicy}, see it's doc for details.
* @param minInterval in milliseconds
@@ -244,6 +337,23 @@ public class RetryTemplateBuilder {
return this;
}
/**
* Use {@link UniformRandomBackOffPolicy}.
* @param minInterval minimum backoff duration
* @param maxInterval maximum backoff duration
* @return this
* @see UniformRandomBackOffPolicy
* @throws IllegalArgumentException if minInterval is {@code null} or {@literal <} 1,
* maxInterval is {@code null} or {@literal <} 1, maxInterval {@literal >=}
* minInterval or if another backoff policy has already been configured.
* @since 2.0.2
*/
public RetryTemplateBuilder uniformRandomBackoff(Duration minInterval, Duration maxInterval) {
Assert.notNull(minInterval, "minInterval must not be null");
Assert.notNull(maxInterval, "maxInterval must not be null");
return this.uniformRandomBackoff(minInterval.toMillis(), maxInterval.toMillis());
}
/**
* Do not pause between attempts, retry immediately.
* @return this

View File

@@ -18,6 +18,7 @@ package org.springframework.retry.support;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -29,6 +30,7 @@ import org.springframework.retry.RetryListener;
import org.springframework.retry.RetryPolicy;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.backoff.ExponentialRandomBackOffPolicy;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.backoff.NoBackOffPolicy;
import org.springframework.retry.backoff.UniformRandomBackOffPolicy;
import org.springframework.retry.policy.AlwaysRetryPolicy;
@@ -53,6 +55,7 @@ import static org.springframework.retry.util.test.TestUtils.getPropertyValue;
* @author Aleksandr Shamukov
* @author Kim In Hoi
* @author Gary Russell
* @author Andreas Ahlenstorf
*/
public class RetryTemplateBuilderTests {
@@ -113,10 +116,11 @@ public class RetryTemplateBuilderTests {
@Test
public void testFailOnRetryPoliciesConflict() {
assertThatIllegalArgumentException()
.isThrownBy(() -> RetryTemplate.builder().maxAttempts(3).withinMillis(1000).build());
.isThrownBy(() -> RetryTemplate.builder().maxAttempts(3).withTimeout(1000).build());
}
@Test
@SuppressWarnings("removal")
public void testTimeoutPolicy() {
RetryTemplate template = RetryTemplate.builder().withinMillis(10000).build();
@@ -127,6 +131,28 @@ public class RetryTemplateBuilderTests {
assertThat(((TimeoutRetryPolicy) policyTuple.baseRetryPolicy).getTimeout()).isEqualTo(10000);
}
@Test
public void testTimeoutMillis() {
RetryTemplate template = RetryTemplate.builder().withTimeout(10000).build();
PolicyTuple policyTuple = PolicyTuple.extractWithAsserts(template);
assertDefaultClassifier(policyTuple);
assertThat(policyTuple.baseRetryPolicy).isInstanceOf(TimeoutRetryPolicy.class);
assertThat(((TimeoutRetryPolicy) policyTuple.baseRetryPolicy).getTimeout()).isEqualTo(10000);
}
@Test
public void testTimeoutDuration() {
RetryTemplate template = RetryTemplate.builder().withTimeout(Duration.ofSeconds(3)).build();
PolicyTuple policyTuple = PolicyTuple.extractWithAsserts(template);
assertDefaultClassifier(policyTuple);
assertThat(policyTuple.baseRetryPolicy).isInstanceOf(TimeoutRetryPolicy.class);
assertThat(((TimeoutRetryPolicy) policyTuple.baseRetryPolicy).getTimeout()).isEqualTo(3000);
}
@Test
public void testInfiniteRetry() {
RetryTemplate template = RetryTemplate.builder().infiniteRetry().build();
@@ -190,24 +216,91 @@ public class RetryTemplateBuilderTests {
.isThrownBy(() -> RetryTemplate.builder().noBackoff().fixedBackoff(1000).build());
}
@Test
public void testFixedBackoff() {
RetryTemplate template = RetryTemplate.builder().fixedBackoff(200).build();
FixedBackOffPolicy policy = getPropertyValue(template, "backOffPolicy", FixedBackOffPolicy.class);
assertThat(policy.getBackOffPeriod()).isEqualTo(200);
}
@Test
public void testFixedBackoffDuration() {
RetryTemplate template = RetryTemplate.builder().fixedBackoff(Duration.ofSeconds(1)).build();
FixedBackOffPolicy policy = getPropertyValue(template, "backOffPolicy", FixedBackOffPolicy.class);
assertThat(policy.getBackOffPeriod()).isEqualTo(1000);
}
@Test
public void testUniformRandomBackOff() {
RetryTemplate template = RetryTemplate.builder().uniformRandomBackoff(10, 100).build();
assertThat(getPropertyValue(template, "backOffPolicy")).isInstanceOf(UniformRandomBackOffPolicy.class);
}
@Test
public void testUniformRandomBackOffDuration() {
RetryTemplate template = RetryTemplate.builder()
.uniformRandomBackoff(Duration.ofSeconds(1), Duration.ofSeconds(2))
.build();
UniformRandomBackOffPolicy policy = getPropertyValue(template, "backOffPolicy",
UniformRandomBackOffPolicy.class);
assertThat(policy.getMinBackOffPeriod()).isEqualTo(1000);
assertThat(policy.getMaxBackOffPeriod()).isEqualTo(2000);
}
@Test
public void testNoBackOff() {
RetryTemplate template = RetryTemplate.builder().noBackoff().build();
assertThat(getPropertyValue(template, "backOffPolicy")).isInstanceOf(NoBackOffPolicy.class);
}
@Test
public void testExponentialBackoff() {
RetryTemplate template = RetryTemplate.builder().exponentialBackoff(10, 2, 500).build();
ExponentialBackOffPolicy policy = getPropertyValue(template, "backOffPolicy", ExponentialBackOffPolicy.class);
assertThat(policy.getInitialInterval()).isEqualTo(10);
assertThat(policy.getMultiplier()).isEqualTo(2);
assertThat(policy.getMaxInterval()).isEqualTo(500);
}
@Test
public void testExponentialBackoffDuration() {
RetryTemplate template = RetryTemplate.builder()
.exponentialBackoff(Duration.ofSeconds(2), 2, Duration.ofSeconds(3))
.build();
ExponentialBackOffPolicy policy = getPropertyValue(template, "backOffPolicy", ExponentialBackOffPolicy.class);
assertThat(policy.getInitialInterval()).isEqualTo(2000);
assertThat(policy.getMultiplier()).isEqualTo(2);
assertThat(policy.getMaxInterval()).isEqualTo(3000);
assertThat(policy.getMaxInterval()).isEqualTo(3000);
}
@Test
public void testExpBackOffWithRandom() {
RetryTemplate template = RetryTemplate.builder().exponentialBackoff(10, 2, 500, true).build();
assertThat(getPropertyValue(template, "backOffPolicy")).isInstanceOf(ExponentialRandomBackOffPolicy.class);
}
@Test
public void testExponentialRandomBackoffDuration() {
RetryTemplate template = RetryTemplate.builder()
.exponentialBackoff(Duration.ofSeconds(2), 2, Duration.ofSeconds(3), true)
.build();
ExponentialRandomBackOffPolicy policy = getPropertyValue(template, "backOffPolicy",
ExponentialRandomBackOffPolicy.class);
assertThat(policy.getInitialInterval()).isEqualTo(2000);
assertThat(policy.getMultiplier()).isEqualTo(2);
assertThat(policy.getMaxInterval()).isEqualTo(3000);
}
@Test
public void testValidateInitAndMax() {
assertThatIllegalArgumentException()