All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.azure.storage.RetryPolicy Maven / Gradle / Ivy

/**
 * 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;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy