com.rt.storage.api.client.http.ExponentialBackOffPolicy Maven / Gradle / Ivy
package com.rt.storage.api.client.http;
import com.rt.storage.api.client.util.Beta;
import com.rt.storage.api.client.util.ExponentialBackOff;
import com.rt.storage.api.client.util.NanoClock;
import java.io.IOException;
/**
* {@link Beta}
* Implementation of {@link BackOffPolicy} that increases the back off period for each retry attempt
* using a randomization function that grows exponentially.
*
* {@link #getNextBackOffMillis()} is calculated using the following formula:
*
*
* randomized_interval =
* retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])
*
*
* In other words {@link #getNextBackOffMillis()} will range between the randomization factor
* percentage below and above the retry interval. For example, using 2 seconds as the base retry
* interval and 0.5 as the randomization factor, the actual back off period used in the next retry
* attempt will be between 1 and 3 seconds.
*
*
Note: max_interval caps the retry_interval and not the randomized_interval.
*
*
If the time elapsed since an {@link ExponentialBackOffPolicy} instance is created goes past
* the max_elapsed_time then the method {@link #getNextBackOffMillis()} starts returning {@link
* BackOffPolicy#STOP}. The elapsed time can be reset by calling {@link #reset()}.
*
*
Example: The default retry_interval is .5 seconds, default randomization_factor is 0.5,
* default multiplier is 1.5 and the default max_interval is 1 minute. For 10 requests the sequence
* will be (values in seconds) and assuming we go over the max_elapsed_time on the 10th request:
*
*
* request# retry_interval randomized_interval
*
* 1 0.5 [0.25, 0.75]
* 2 0.75 [0.375, 1.125]
* 3 1.125 [0.562, 1.687]
* 4 1.687 [0.8435, 2.53]
* 5 2.53 [1.265, 3.795]
* 6 3.795 [1.897, 5.692]
* 7 5.692 [2.846, 8.538]
* 8 8.538 [4.269, 12.807]
* 9 12.807 [6.403, 19.210]
* 10 19.210 {@link BackOffPolicy#STOP}
*
*
* Implementation is not thread-safe.
*
* @since 1.7
* @author Ravi Mistry
* @deprecated (scheduled to be removed in 1.18). Use {@link HttpBackOffUnsuccessfulResponseHandler}
* with {@link ExponentialBackOff} instead.
*/
@Beta
@Deprecated
public class ExponentialBackOffPolicy implements BackOffPolicy {
/** The default initial interval value in milliseconds (0.5 seconds). */
public static final int DEFAULT_INITIAL_INTERVAL_MILLIS =
ExponentialBackOff.DEFAULT_INITIAL_INTERVAL_MILLIS;
/**
* The default randomization factor (0.5 which results in a random period ranging between 50%
* below and 50% above the retry interval).
*/
public static final double DEFAULT_RANDOMIZATION_FACTOR =
ExponentialBackOff.DEFAULT_RANDOMIZATION_FACTOR;
/** The default multiplier value (1.5 which is 50% increase per back off). */
public static final double DEFAULT_MULTIPLIER = ExponentialBackOff.DEFAULT_MULTIPLIER;
/** The default maximum back off time in milliseconds (1 minute). */
public static final int DEFAULT_MAX_INTERVAL_MILLIS =
ExponentialBackOff.DEFAULT_MAX_INTERVAL_MILLIS;
/** The default maximum elapsed time in milliseconds (15 minutes). */
public static final int DEFAULT_MAX_ELAPSED_TIME_MILLIS =
ExponentialBackOff.DEFAULT_MAX_ELAPSED_TIME_MILLIS;
/** Exponential backoff. */
private final ExponentialBackOff exponentialBackOff;
/**
* Creates an instance of ExponentialBackOffPolicy using default values. To override the defaults
* use {@link #builder}.
*
*
* - {@code initialIntervalMillis} is defaulted to {@link #DEFAULT_INITIAL_INTERVAL_MILLIS}
*
- {@code randomizationFactor} is defaulted to {@link #DEFAULT_RANDOMIZATION_FACTOR}
*
- {@code multiplier} is defaulted to {@link #DEFAULT_MULTIPLIER}
*
- {@code maxIntervalMillis} is defaulted to {@link #DEFAULT_MAX_INTERVAL_MILLIS}
*
- {@code maxElapsedTimeMillis} is defaulted in {@link #DEFAULT_MAX_ELAPSED_TIME_MILLIS}
*
*/
public ExponentialBackOffPolicy() {
this(new Builder());
}
/**
* @param builder builder
* @since 1.14
*/
protected ExponentialBackOffPolicy(Builder builder) {
exponentialBackOff = builder.exponentialBackOffBuilder.build();
}
/**
* Determines if back off is required based on the specified status code.
*
* The idea is that the servers are only temporarily unavailable, and they should not be
* overwhelmed when they are trying to get back up.
*
*
The default implementation requires back off for 500 and 503 status codes. Subclasses may
* override if different status codes are required.
*/
public boolean isBackOffRequired(int statusCode) {
switch (statusCode) {
case HttpStatusCodes.STATUS_CODE_SERVER_ERROR: // 500
case HttpStatusCodes.STATUS_CODE_SERVICE_UNAVAILABLE: // 503
return true;
default:
return false;
}
}
/** Sets the interval back to the initial retry interval and restarts the timer. */
public final void reset() {
exponentialBackOff.reset();
}
/**
* Gets the number of milliseconds to wait before retrying an HTTP request. If {@link #STOP} is
* returned, no retries should be made.
*
*
This method calculates the next back off interval using the formula: randomized_interval =
* retry_interval +/- (randomization_factor * retry_interval)
*
*
Subclasses may override if a different algorithm is required.
*
* @return the number of milliseconds to wait when backing off requests, or {@link #STOP} if no
* more retries should be made
*/
public long getNextBackOffMillis() throws IOException {
return exponentialBackOff.nextBackOffMillis();
}
/** Returns the initial retry interval in milliseconds. */
public final int getInitialIntervalMillis() {
return exponentialBackOff.getInitialIntervalMillis();
}
/**
* Returns the randomization factor to use for creating a range around the retry interval.
*
*
A randomization factor of 0.5 results in a random period ranging between 50% below and 50%
* above the retry interval.
*/
public final double getRandomizationFactor() {
return exponentialBackOff.getRandomizationFactor();
}
/** Returns the current retry interval in milliseconds. */
public final int getCurrentIntervalMillis() {
return exponentialBackOff.getCurrentIntervalMillis();
}
/** Returns the value to multiply the current interval with for each retry attempt. */
public final double getMultiplier() {
return exponentialBackOff.getMultiplier();
}
/**
* Returns the maximum value of the back off period in milliseconds. Once the current interval
* reaches this value it stops increasing.
*/
public final int getMaxIntervalMillis() {
return exponentialBackOff.getMaxIntervalMillis();
}
/**
* Returns the maximum elapsed time in milliseconds.
*
*
If the time elapsed since an {@link ExponentialBackOffPolicy} instance is created goes past
* the max_elapsed_time then the method {@link #getNextBackOffMillis()} starts returning {@link
* BackOffPolicy#STOP}. The elapsed time can be reset by calling {@link #reset()}.
*/
public final int getMaxElapsedTimeMillis() {
return exponentialBackOff.getMaxElapsedTimeMillis();
}
/**
* Returns the elapsed time in milliseconds since an {@link ExponentialBackOffPolicy} instance is
* created and is reset when {@link #reset()} is called.
*
*
The elapsed time is computed using {@link System#nanoTime()}.
*/
public final long getElapsedTimeMillis() {
return exponentialBackOff.getElapsedTimeMillis();
}
/** Returns an instance of a new builder. */
public static Builder builder() {
return new Builder();
}
/**
* {@link Beta}
* Builder for {@link ExponentialBackOffPolicy}.
*
*
Implementation is not thread-safe.
*
* @since 1.7
*/
@Beta
@Deprecated
public static class Builder {
/** Exponential back-off builder. */
final ExponentialBackOff.Builder exponentialBackOffBuilder = new ExponentialBackOff.Builder();
protected Builder() {}
/** Builds a new instance of {@link ExponentialBackOffPolicy}. */
public ExponentialBackOffPolicy build() {
return new ExponentialBackOffPolicy(this);
}
/**
* Returns the initial retry interval in milliseconds. The default value is {@link
* #DEFAULT_INITIAL_INTERVAL_MILLIS}.
*/
public final int getInitialIntervalMillis() {
return exponentialBackOffBuilder.getInitialIntervalMillis();
}
/**
* Sets the initial retry interval in milliseconds. The default value is {@link
* #DEFAULT_INITIAL_INTERVAL_MILLIS}. Must be {@code > 0}.
*
*
Overriding is only supported for the purpose of calling the super implementation and
* changing the return type, but nothing else.
*/
public Builder setInitialIntervalMillis(int initialIntervalMillis) {
exponentialBackOffBuilder.setInitialIntervalMillis(initialIntervalMillis);
return this;
}
/**
* Returns the randomization factor to use for creating a range around the retry interval. The
* default value is {@link #DEFAULT_RANDOMIZATION_FACTOR}.
*
*
A randomization factor of 0.5 results in a random period ranging between 50% below and 50%
* above the retry interval.
*
*
Overriding is only supported for the purpose of calling the super implementation and
* changing the return type, but nothing else.
*/
public final double getRandomizationFactor() {
return exponentialBackOffBuilder.getRandomizationFactor();
}
/**
* Sets the randomization factor to use for creating a range around the retry interval. The
* default value is {@link #DEFAULT_RANDOMIZATION_FACTOR}. Must fall in the range {@code 0 <=
* randomizationFactor < 1}.
*
*
A randomization factor of 0.5 results in a random period ranging between 50% below and 50%
* above the retry interval.
*
*
Overriding is only supported for the purpose of calling the super implementation and
* changing the return type, but nothing else.
*/
public Builder setRandomizationFactor(double randomizationFactor) {
exponentialBackOffBuilder.setRandomizationFactor(randomizationFactor);
return this;
}
/**
* Returns the value to multiply the current interval with for each retry attempt. The default
* value is {@link #DEFAULT_MULTIPLIER}.
*/
public final double getMultiplier() {
return exponentialBackOffBuilder.getMultiplier();
}
/**
* Sets the value to multiply the current interval with for each retry attempt. The default
* value is {@link #DEFAULT_MULTIPLIER}. Must be {@code >= 1}.
*
*
Overriding is only supported for the purpose of calling the super implementation and
* changing the return type, but nothing else.
*/
public Builder setMultiplier(double multiplier) {
exponentialBackOffBuilder.setMultiplier(multiplier);
return this;
}
/**
* Returns the maximum value of the back off period in milliseconds. Once the current interval
* reaches this value it stops increasing. The default value is {@link
* #DEFAULT_MAX_INTERVAL_MILLIS}. Must be {@code >= initialInterval}.
*/
public final int getMaxIntervalMillis() {
return exponentialBackOffBuilder.getMaxIntervalMillis();
}
/**
* Sets the maximum value of the back off period in milliseconds. Once the current interval
* reaches this value it stops increasing. The default value is {@link
* #DEFAULT_MAX_INTERVAL_MILLIS}.
*
*
Overriding is only supported for the purpose of calling the super implementation and
* changing the return type, but nothing else.
*/
public Builder setMaxIntervalMillis(int maxIntervalMillis) {
exponentialBackOffBuilder.setMaxIntervalMillis(maxIntervalMillis);
return this;
}
/**
* Returns the maximum elapsed time in milliseconds. The default value is {@link
* #DEFAULT_MAX_ELAPSED_TIME_MILLIS}.
*
*
If the time elapsed since an {@link ExponentialBackOffPolicy} instance is created goes
* past the max_elapsed_time then the method {@link #getNextBackOffMillis()} starts returning
* {@link BackOffPolicy#STOP}. The elapsed time can be reset by calling {@link #reset()}.
*/
public final int getMaxElapsedTimeMillis() {
return exponentialBackOffBuilder.getMaxElapsedTimeMillis();
}
/**
* Sets the maximum elapsed time in milliseconds. The default value is {@link
* #DEFAULT_MAX_ELAPSED_TIME_MILLIS}. Must be {@code > 0}.
*
*
If the time elapsed since an {@link ExponentialBackOffPolicy} instance is created goes
* past the max_elapsed_time then the method {@link #getNextBackOffMillis()} starts returning
* {@link BackOffPolicy#STOP}. The elapsed time can be reset by calling {@link #reset()}.
*
*
Overriding is only supported for the purpose of calling the super implementation and
* changing the return type, but nothing else.
*/
public Builder setMaxElapsedTimeMillis(int maxElapsedTimeMillis) {
exponentialBackOffBuilder.setMaxElapsedTimeMillis(maxElapsedTimeMillis);
return this;
}
/**
* Returns the nano clock.
*
* @since 1.14
*/
public final NanoClock getNanoClock() {
return exponentialBackOffBuilder.getNanoClock();
}
/**
* Sets the nano clock ({@link NanoClock#SYSTEM} by default).
*
*
Overriding is only supported for the purpose of calling the super implementation and
* changing the return type, but nothing else.
*
* @since 1.14
*/
public Builder setNanoClock(NanoClock nanoClock) {
exponentialBackOffBuilder.setNanoClock(nanoClock);
return this;
}
}
}