io.hekate.messaging.retry.ExponentialBackoffPolicy Maven / Gradle / Ivy
/*
* Copyright 2022 The Hekate Project
*
* The Hekate Project licenses this file to you 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 io.hekate.messaging.retry;
import io.hekate.core.internal.util.ArgAssert;
import io.hekate.util.format.ToString;
import io.hekate.util.format.ToStringIgnore;
/**
* A backoff policy that waits exponentially longer between each attempt (but keeps a constant delay once a maximum delay is reached).
*
*
* This policy uses the following formula to calculate the backoff delay:
*
*
* {@code
* (2 ^ (attempt -1)) * baseDelay
* }
*
*
* Note: for the very first attempt the answer is always {@code 0} (i.e. retry immediately).
*
*
*
* Parameters of this policy are:
*
*
*
* - {@code BaseDelay} - multiplier for each attempt
* - {@code MaxDelay} - maximum delay (calculated delay will never exceed this value)
*
*
*
* Examples of calculations:
*
*
* {@code
* BaseDelay: 10 MaxDelay: 3000
* -------------------------------
* Attempt 0 -> 0
* Attempt 1 -> 10
* Attempt 2 -> 20
* Attempt 3 -> 40
* Attempt 4 -> 80
* Attempt 5 -> 160
* Attempt 6 -> 320
* Attempt 7 -> 640
* Attempt 8 -> 1280
* Attempt 9 -> 2560
* Attempt 10 -> 3000
* ... -> 3000
* }
* {@code
* BaseDelay: 500 MaxDelay: 30000
* -------------------------------
* Attempt 0 -> 0
* Attempt 1 -> 500
* Attempt 2 -> 1000
* Attempt 3 -> 2000
* Attempt 4 -> 4000
* Attempt 5 -> 8000
* Attempt 6 -> 16000
* Attempt 7 -> 30000
* ... -> 30000
* }
*/
public class ExponentialBackoffPolicy implements RetryBackoffPolicy {
/** Default base delay (={@value}) in milliseconds. */
public static final long DEFAULT_BASE_DELAY = 50;
/** Default maximum delay (={@value}) in milliseconds. */
public static final long DEFAULT_MAX_DELAY = 3000;
/** Base delay. */
private final long baseDelay;
/** Maximum delay. */
private final long maxDelay;
/** Max attempt limit (max value before calculations overflow). */
@ToStringIgnore
private final int attemptOverflow;
/**
* Constructs a new instance with default values.
*
* @see #DEFAULT_BASE_DELAY
* @see #DEFAULT_MAX_DELAY
*/
public ExponentialBackoffPolicy() {
this(DEFAULT_BASE_DELAY, DEFAULT_MAX_DELAY);
}
/**
* Constructs a new instance.
*
* @param baseDelay Multiplier for each attempt (in milliseconds).
* @param maxDelay Maximum delay in milliseconds (calculated delay will never exceed this value).
*/
public ExponentialBackoffPolicy(long baseDelay, long maxDelay) {
ArgAssert.positive(baseDelay, "Base delay");
ArgAssert.positive(maxDelay, "Maximum delay");
ArgAssert.check(baseDelay <= maxDelay, "Base delay can't be less than max delay.");
this.baseDelay = baseDelay;
this.maxDelay = maxDelay;
// Maximum attempts before we start to overflow.
this.attemptOverflow = Long.SIZE - Long.numberOfLeadingZeros(Long.MAX_VALUE / baseDelay) - 1;
}
/**
* Returns the base delay of this policy in milliseconds.
*
* @return Base delay in milliseconds.
*/
public long baseDelay() {
return baseDelay;
}
/**
* Returns the maximum delay of this policy in milliseconds.
*
* @return Maximum delay in milliseconds.
*/
public long maxDelay() {
return maxDelay;
}
@Override
public long delayBeforeRetry(int attempt) {
if (attempt == 0) {
return 0;
} else if (attempt >= attemptOverflow) {
return maxDelay;
} else {
int calcAttempt = attempt - 1; // <- First attempt must be always retried immediately.
return (long)Math.min(Math.pow(2, calcAttempt) * baseDelay, maxDelay);
}
}
@Override
public String toString() {
return ToString.format(this);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy