mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 08:24:13 +00:00
Merge branch '7.0.x'
This commit is contained in:
@@ -81,6 +81,13 @@ public void sendNotification() {
|
|||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
====
|
||||||
|
When `delay` is `0` combined with a positive `jitter`, the delay never grows
|
||||||
|
regardless of any configured `multiplier`, so the full configured `jitter` is
|
||||||
|
applied directly as a random delay in the range from `0` to `min(jitter, maxDelay)`.
|
||||||
|
====
|
||||||
|
|
||||||
Last but not least, `@Retryable` also works for reactive methods with a reactive return
|
Last but not least, `@Retryable` also works for reactive methods with a reactive return
|
||||||
type, decorating the pipeline with Reactor's retry capabilities:
|
type, decorating the pipeline with Reactor's retry capabilities:
|
||||||
|
|
||||||
@@ -263,6 +270,13 @@ and an exponential back-off strategy with a bit of jitter.
|
|||||||
() -> jmsClient.destination("notifications").send(...));
|
() -> jmsClient.destination("notifications").send(...));
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
====
|
||||||
|
When `delay` is zero combined with a positive `jitter`, the delay never grows
|
||||||
|
regardless of any configured `multiplier`, so the full configured `jitter` is
|
||||||
|
applied directly as a random delay in the range from zero to `min(jitter, maxDelay)`.
|
||||||
|
====
|
||||||
|
|
||||||
[TIP]
|
[TIP]
|
||||||
====
|
====
|
||||||
Although the factory methods and builder API for `RetryPolicy` cover most common
|
Although the factory methods and builder API for `RetryPolicy` cover most common
|
||||||
|
|||||||
@@ -197,6 +197,10 @@ public @interface Retryable {
|
|||||||
* and {@code delay + jitter} but never below the base {@link #delay()} or
|
* and {@code delay + jitter} but never below the base {@link #delay()} or
|
||||||
* above {@link #maxDelay()}. If a multiplier is specified, it is applied
|
* above {@link #maxDelay()}. If a multiplier is specified, it is applied
|
||||||
* to the jitter value as well.
|
* to the jitter value as well.
|
||||||
|
* <p>When {@link #delay()} is {@code 0} combined with a positive jitter,
|
||||||
|
* the delay never grows regardless of any configured multiplier, so the
|
||||||
|
* full configured jitter is applied directly as a random delay in the range
|
||||||
|
* from {@code 0} to {@code min(jitter, maxDelay)}.
|
||||||
* <p>The time unit is milliseconds by default but can be overridden via
|
* <p>The time unit is milliseconds by default but can be overridden via
|
||||||
* {@link #timeUnit}.
|
* {@link #timeUnit}.
|
||||||
* <p>The default is 0 (no jitter).
|
* <p>The default is 0 (no jitter).
|
||||||
|
|||||||
@@ -281,6 +281,11 @@ public interface RetryPolicy {
|
|||||||
* {@linkplain #maxDelay(Duration) max delay}.
|
* {@linkplain #maxDelay(Duration) max delay}.
|
||||||
* <p>If a {@linkplain #multiplier(double) multiplier} is specified, it
|
* <p>If a {@linkplain #multiplier(double) multiplier} is specified, it
|
||||||
* is applied to the jitter value as well.
|
* is applied to the jitter value as well.
|
||||||
|
* <p>When the configured {@linkplain #delay(Duration) delay} is zero
|
||||||
|
* combined with a positive jitter, the delay never grows regardless of
|
||||||
|
* any configured multiplier, so the full configured jitter is applied
|
||||||
|
* directly as a random delay in the range from zero to
|
||||||
|
* {@code min(jitter, maxDelay)}.
|
||||||
* <p>The default is no jitter.
|
* <p>The default is no jitter.
|
||||||
* <p>The supplied value will override any previously configured value.
|
* <p>The supplied value will override any previously configured value.
|
||||||
* <p>You should not specify this configuration option if you have
|
* <p>You should not specify this configuration option if you have
|
||||||
|
|||||||
@@ -154,6 +154,10 @@ public class ExponentialBackOff implements BackOff {
|
|||||||
* {@code initialInterval} or above {@code maxInterval}.
|
* {@code initialInterval} or above {@code maxInterval}.
|
||||||
* <p>If a {@code multiplier} is specified, it is applied to the jitter value
|
* <p>If a {@code multiplier} is specified, it is applied to the jitter value
|
||||||
* as well.
|
* as well.
|
||||||
|
* <p>When {@code initialInterval} is {@code 0} combined with a positive
|
||||||
|
* jitter, the interval never grows regardless of any configured multiplier,
|
||||||
|
* so the full configured jitter is applied directly as a random interval in
|
||||||
|
* the range from {@code 0} to {@code min(jitter, maxInterval)}.
|
||||||
* @param jitter the jitter value in milliseconds
|
* @param jitter the jitter value in milliseconds
|
||||||
* @since 7.0
|
* @since 7.0
|
||||||
*/
|
*/
|
||||||
@@ -311,7 +315,9 @@ public class ExponentialBackOff implements BackOff {
|
|||||||
long jitter = getJitter();
|
long jitter = getJitter();
|
||||||
if (jitter > 0) {
|
if (jitter > 0) {
|
||||||
long initialInterval = getInitialInterval();
|
long initialInterval = getInitialInterval();
|
||||||
long applicableJitter = jitter * (interval / initialInterval);
|
// When initialInterval is 0 the interval never grows, so the scale factor
|
||||||
|
// stays at its baseline value of 1 and the full configured jitter is applied.
|
||||||
|
long applicableJitter = jitter * (initialInterval > 0 ? (interval / initialInterval) : 1);
|
||||||
long min = Math.max(interval - applicableJitter, initialInterval);
|
long min = Math.max(interval - applicableJitter, initialInterval);
|
||||||
long max = Math.min(interval + applicableJitter, getMaxInterval());
|
long max = Math.min(interval + applicableJitter, getMaxInterval());
|
||||||
return min + (long) (Math.random() * (max - min));
|
return min + (long) (Math.random() * (max - min));
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import org.springframework.util.backoff.ExponentialBackOff;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link ExponentialBackOff}.
|
* Tests for {@link ExponentialBackOff}.
|
||||||
@@ -118,6 +119,18 @@ class ExponentialBackOffTests {
|
|||||||
assertThatIllegalArgumentException().isThrownBy(() -> backOff.setMultiplier(0.9));
|
assertThatIllegalArgumentException().isThrownBy(() -> backOff.setMultiplier(0.9));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // gh-36932
|
||||||
|
void jitterWithZeroInitialInterval() {
|
||||||
|
ExponentialBackOff backOff = new ExponentialBackOff();
|
||||||
|
backOff.setInitialInterval(0);
|
||||||
|
backOff.setJitter(100);
|
||||||
|
BackOffExecution execution = backOff.start();
|
||||||
|
|
||||||
|
// 'initialInterval = 0' and 'jitter > 0' are both individually accepted
|
||||||
|
// configurations, so their combination must not throw.
|
||||||
|
assertThatNoException().isThrownBy(execution::nextBackOff);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void maxIntervalReachedImmediately() {
|
void maxIntervalReachedImmediately() {
|
||||||
ExponentialBackOff backOff = new ExponentialBackOff(1000L, 2.0);
|
ExponentialBackOff backOff = new ExponentialBackOff(1000L, 2.0);
|
||||||
|
|||||||
Reference in New Issue
Block a user