1 /*
2  * Copyright 2011-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */

15 package com.amazonaws.retry;
16
17 import com.amazonaws.retry.v2.BackoffStrategy;
18 import com.amazonaws.retry.v2.RetryPolicyContext;
19 import com.amazonaws.util.ValidationUtils;
20
21 import java.util.Random;
22
23 /**
24  * This class includes a set of pre-defined backoff policies.
25  * See this blog for more information on the different algorithms:
26  * https://www.awsarchitectureblog.com/2015/03/backoff.html
27  */

28 public class PredefinedBackoffStrategies {
29
30     /**
31      * Default base sleep time (milliseconds) for non-throttled exceptions.
32      **/

33     private static final int SDK_DEFAULT_BASE_DELAY = 100;
34
35     /**
36      * Default base sleep time (milliseconds) for throttled exceptions.
37      **/

38     static final int SDK_DEFAULT_THROTTLED_BASE_DELAY = 500;
39
40     /**
41      * Default maximum back-off time before retrying a request
42      */

43     static final int SDK_DEFAULT_MAX_BACKOFF_IN_MILLISECONDS = 20 * 1000;
44
45     /**
46      * Default base sleep time for DynamoDB.
47      **/

48     static final int DYNAMODB_DEFAULT_BASE_DELAY = 25;
49
50     /**
51      * Maximum retry limit. Avoids integer overflow issues.
52      *
53      * NOTE: If the value is greater than 30, there can be integer overflow
54      * issues during delay calculation.
55      **/

56     private static final int MAX_RETRIES = 30;
57
58     public static class FullJitterBackoffStrategy extends V2CompatibleBackoffStrategyAdapter {
59
60         private final int baseDelay;
61         private final int maxBackoffTime;
62         private final Random random = new Random();
63
64         public FullJitterBackoffStrategy(final int baseDelay,
65                                          final int maxBackoffTime) {
66             this.baseDelay = ValidationUtils.assertIsPositive(baseDelay, "Base delay");
67             this.maxBackoffTime = ValidationUtils.assertIsPositive(maxBackoffTime, "Max backoff");
68         }
69
70
71         @Override
72         public long computeDelayBeforeNextRetry(RetryPolicyContext context) {
73             int ceil = calculateExponentialDelay(context.retriesAttempted(), baseDelay, maxBackoffTime);
74             return random.nextInt(ceil);
75         }
76     }
77
78     public static class EqualJitterBackoffStrategy extends V2CompatibleBackoffStrategyAdapter {
79
80         private final int baseDelay;
81         private final int maxBackoffTime;
82         private final Random random = new Random();
83
84         public EqualJitterBackoffStrategy(final int baseDelay,
85                                           final int maxBackoffTime) {
86             this.baseDelay = ValidationUtils.assertIsPositive(baseDelay, "Base delay");
87             this.maxBackoffTime = ValidationUtils.assertIsPositive(maxBackoffTime, "Max backoff");
88         }
89
90         @Override
91         public long computeDelayBeforeNextRetry(RetryPolicyContext context) {
92             int ceil = calculateExponentialDelay(context.retriesAttempted(), baseDelay, maxBackoffTime);
93             return (ceil / 2) + random.nextInt((ceil / 2) + 1);
94         }
95     }
96
97     public static class ExponentialBackoffStrategy extends V2CompatibleBackoffStrategyAdapter {
98
99         private final int baseDelay;
100         private final int maxBackoffTime;
101
102         public ExponentialBackoffStrategy(final int baseDelay,
103                                           final int maxBackoffTime) {
104             this.baseDelay = ValidationUtils.assertIsPositive(baseDelay, "Base delay");
105             this.maxBackoffTime = ValidationUtils.assertIsPositive(maxBackoffTime, "Max backoff");
106         }
107
108         @Override
109         public long computeDelayBeforeNextRetry(RetryPolicyContext context) {
110             return calculateExponentialDelay(context.retriesAttempted(), baseDelay, maxBackoffTime);
111         }
112     }
113
114     private static int calculateExponentialDelay(int retriesAttempted, int baseDelay, int maxBackoffTime) {
115         int retries = Math.min(retriesAttempted, MAX_RETRIES);
116         return (int) Math.min((1L << retries) * baseDelay, maxBackoffTime);
117     }
118
119
120     /**
121      * A private class that implements the default back-off strategy.
122      **/

123     public static class SDKDefaultBackoffStrategy extends V2CompatibleBackoffStrategyAdapter {
124
125         private final BackoffStrategy fullJitterBackoffStrategy;
126         private final BackoffStrategy equalJitterBackoffStrategy;
127
128         public SDKDefaultBackoffStrategy() {
129             fullJitterBackoffStrategy = new PredefinedBackoffStrategies.FullJitterBackoffStrategy(
130                     SDK_DEFAULT_BASE_DELAY, SDK_DEFAULT_MAX_BACKOFF_IN_MILLISECONDS);
131             equalJitterBackoffStrategy = new PredefinedBackoffStrategies.EqualJitterBackoffStrategy(
132                     SDK_DEFAULT_THROTTLED_BASE_DELAY, SDK_DEFAULT_MAX_BACKOFF_IN_MILLISECONDS);
133         }
134
135         public SDKDefaultBackoffStrategy(final int baseDelay, final int throttledBaseDelay, final int maxBackoff) {
136             fullJitterBackoffStrategy = new PredefinedBackoffStrategies.FullJitterBackoffStrategy(
137                     baseDelay, maxBackoff);
138             equalJitterBackoffStrategy = new PredefinedBackoffStrategies.EqualJitterBackoffStrategy(
139                     throttledBaseDelay, maxBackoff);
140         }
141
142         @Override
143         public long computeDelayBeforeNextRetry(RetryPolicyContext context) {
144             /*
145              * We use the full jitter scheme for non-throttled exceptions and the
146              * equal jitter scheme for throttled exceptions.  This gives a preference
147              * to quicker response and larger retry distribution for service errors
148              * and guarantees a minimum delay for throttled exceptions.
149              */

150             if (RetryUtils.isThrottlingException(context.exception())) {
151                 return equalJitterBackoffStrategy.computeDelayBeforeNextRetry(context);
152             } else {
153                 return fullJitterBackoffStrategy.computeDelayBeforeNextRetry(context);
154             }
155         }
156     }
157
158 }