com.microsoft.azure.storage.RetryPolicy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of azure-storage Show documentation
Show all versions of azure-storage Show documentation
SDK for Microsoft Azure Storage Clients
/**
* Copyright Microsoft Corporation
*
* 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 com.microsoft.azure.storage;
import java.net.HttpURLConnection;
import java.util.Date;
import com.microsoft.azure.storage.core.Utility;
/**
* Abstract class that represents a retry policy.
*/
public abstract class RetryPolicy implements RetryPolicyFactory {
/**
* Represents the default client backoff interval, in milliseconds.
*/
public static final int DEFAULT_CLIENT_BACKOFF = 1000 * 30;
/**
* Represents the default client retry count.
*/
public static final int DEFAULT_CLIENT_RETRY_COUNT = 3;
/**
* Represents the default maximum backoff interval, in milliseconds.
*/
public static final int DEFAULT_MAX_BACKOFF = 1000 * 90;
/**
* Represents the default minimum backoff interval, in milliseconds.
*/
public static final int DEFAULT_MIN_BACKOFF = 1000 * 3;
/**
* Represents the realized backoff interval, in milliseconds.
*/
protected int deltaBackoffIntervalInMs;
/**
* Represents the maximum retries that the retry policy should attempt.
*/
protected int maximumAttempts;
/**
* Represents the time of the last request attempt to the primary location.
*/
protected Date lastPrimaryAttempt = null;
/**
* Represents the time of the last request attempt to the secondary location.
*/
protected Date lastSecondaryAttempt = null;
/**
* Creates an instance of the RetryPolicy
class.
*/
public RetryPolicy() {
// Empty Default Ctor
}
/**
* Creates an instance of the RetryPolicy
class using the specified delta backoff and maximum retry
* attempts.
*
* @param deltaBackoff
* The backoff interval, in milliseconds, between retries.
* @param maxAttempts
* The maximum number of retry attempts.
*/
public RetryPolicy(final int deltaBackoff, final int maxAttempts) {
this.deltaBackoffIntervalInMs = deltaBackoff;
this.maximumAttempts = maxAttempts;
}
/**
* Determines whether the operation should be retried and specifies the interval until the next retry.
*
* @param retryContext
* A {@link RetryContext} object that indicates the number of retries, last request's results, whether
* the next retry should happen in the primary or secondary location, and specifies the location mode.
* @param operationContext
* An {@link OperationContext} object for tracking the current operation.
* @return
* A {@link RetryInfo} object that indicates whether the next retry will happen in the primary or secondary
* location, and specifies the location mode. If null
, the operation will not be retried.
*/
public abstract RetryInfo evaluate(RetryContext retryContext, OperationContext operationContext);
/**
* Determines the time of the last attempt to a storage location and returns a boolean
that specifies
* if a request that was sent to the secondary location failed with 404.
*
* @param retryContext
* A {@link RetryContext} object that indicates the number of retries, last request's results, whether
* the next retry should happen in the primary or secondary location, and specifies the location mode.
* @return
* true
if a request sent to the secondary location fails with 404 (Not Found).
* false
otherwise.
*/
protected boolean evaluateLastAttemptAndSecondaryNotFound(RetryContext retryContext) {
Utility.assertNotNull("retryContext", retryContext);
// Retry interval of a request to a location must take the time spent sending requests
// to other locations into account. For example, assume a request was sent to the primary
// location first, then to the secondary, and then to the primary again. If it
// was supposed to wait 10 seconds between requests to the primary and the request to
// the secondary took 3 seconds in total, retry interval should only be 7 seconds. This is because,
// in total, the requests will be 10 seconds apart from the primary locations' point of view.
// For this calculation, current instance of the retry policy stores the time of the last
// request to a specific location.
if (retryContext.getLastRequestResult().getTargetLocation() == StorageLocation.PRIMARY) {
this.lastPrimaryAttempt = retryContext.getLastRequestResult().getStopDate();
}
else {
this.lastSecondaryAttempt = retryContext.getLastRequestResult().getStopDate();
}
// If a request sent to the secondary location fails with 404 (Not Found), it is possible
// that the the asynchronous geo-replication for the resource has not completed. So, in case of 404 only in the secondary
// location, the failure should still be retried.
return (retryContext.getLastRequestResult().getTargetLocation() == StorageLocation.SECONDARY)
&& (retryContext.getLastRequestResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND);
}
/**
* Determines the {@link RetryInfo} object that indicates whether the next retry will happen in the primary or
* secondary location, and specifies the location mode.
*
* @param retryContext
* A {@link RetryContext} object that indicates the number of retries, last request's results, whether
* the next retry should happen in the primary or secondary location, and specifies the location mode.
*
* @param secondaryNotFound
* A boolean
representing whether a request sent to the secondary location failed with 404
* (Not Found)
* @param retryInterval
* Backoff Interval.
* @return
* A reference to the {@link RetryInfo} object that indicates whether the next retry will happen in the
* primary or secondary location, and specifies the location mode.
*/
protected RetryInfo evaluateRetryInfo(final RetryContext retryContext, final boolean secondaryNotFound,
final long retryInterval) {
RetryInfo retryInfo = new RetryInfo(retryContext);
// Moreover, in case of 404 when trying the secondary location, instead of retrying on the
// secondary, further requests should be sent only to the primary location, as it most
// probably has a higher chance of succeeding there.
if (secondaryNotFound && (retryContext.getLocationMode() != LocationMode.SECONDARY_ONLY)) {
retryInfo.setUpdatedLocationMode(LocationMode.PRIMARY_ONLY);
retryInfo.setTargetLocation(StorageLocation.PRIMARY);
}
// Now is the time to calculate the exact retry interval. ShouldRetry call above already
// returned back how long two requests to the same location should be apart from each other.
// However, for the reasons explained above, the time spent between the last attempt to
// the target location and current time must be subtracted from the total retry interval
// that ShouldRetry returned.
Date lastAttemptTime = retryInfo.getTargetLocation() == StorageLocation.PRIMARY ? this.lastPrimaryAttempt
: this.lastSecondaryAttempt;
if (lastAttemptTime != null) {
long sinceLastAttempt = (new Date().getTime() - lastAttemptTime.getTime() > 0) ? new Date().getTime()
- lastAttemptTime.getTime() : 0;
retryInfo.setRetryInterval((int) (retryInterval - sinceLastAttempt));
}
else {
retryInfo.setRetryInterval(0);
}
return retryInfo;
}
}