/**
 * 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.
/** * Abstract class that represents a retry policy. */
public abstract class RetryPolicy implements RetryPolicyFactory {
Represents the default client backoff interval, in milliseconds.
/** * Represents the default client backoff interval, in milliseconds. */
public static final int DEFAULT_CLIENT_BACKOFF = 1000 * 30;
Represents the default client retry count.
/** * Represents the default client retry count. */
public static final int DEFAULT_CLIENT_RETRY_COUNT = 3;
Represents the default maximum backoff interval, in milliseconds.
/** * 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.
/** * Represents the default minimum backoff interval, in milliseconds. */
public static final int DEFAULT_MIN_BACKOFF = 1000 * 3;
Represents the realized backoff interval, in milliseconds.
/** * Represents the realized backoff interval, in milliseconds. */
protected int deltaBackoffIntervalInMs;
Represents the maximum retries that the retry policy should attempt.
/** * 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.
/** * 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.
/** * Represents the time of the last request attempt to the secondary location. */
protected Date lastSecondaryAttempt = null;
Creates an instance of the RetryPolicy class.
/** * Creates an instance of the <code>RetryPolicy</code> class. */
public RetryPolicy() { // Empty Default Ctor }
Creates an instance of the RetryPolicy class using the specified delta backoff and maximum retry attempts.
Params:
  • deltaBackoff – The backoff interval, in milliseconds, between retries.
  • maxAttempts – The maximum number of retry attempts.
/** * Creates an instance of the <code>RetryPolicy</code> 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.
Params:
  • retryContext – A 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.
  • operationContext – An OperationContext object for tracking the current operation.
Returns: A 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.
/** * 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 <code>null</code>, 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.
Params:
  • retryContext – A 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.
Returns: true if a request sent to the secondary location fails with 404 (Not Found). false otherwise.
/** * Determines the time of the last attempt to a storage location and returns a <code>boolean</code> 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 * <code>true</code> if a request sent to the secondary location fails with 404 (Not Found). * <code>false</code> 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 RetryInfo object that indicates whether the next retry will happen in the primary or secondary location, and specifies the location mode.
Params:
  • retryContext – A 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.
  • secondaryNotFound – A boolean representing whether a request sent to the secondary location failed with 404 (Not Found)
  • retryInterval – Backoff Interval.
Returns: A reference to the RetryInfo object that indicates whether the next retry will happen in the primary or secondary location, and specifies the location mode.
/** * 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 <code>boolean</code> 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; } }