1
15
16 package software.amazon.awssdk.core.retry.backoff;
17
18 import static software.amazon.awssdk.utils.NumericUtils.min;
19 import static software.amazon.awssdk.utils.Validate.isNotNegative;
20
21 import java.time.Duration;
22 import java.util.Random;
23 import software.amazon.awssdk.annotations.SdkPublicApi;
24 import software.amazon.awssdk.core.retry.RetryPolicyContext;
25 import software.amazon.awssdk.utils.ToString;
26 import software.amazon.awssdk.utils.builder.CopyableBuilder;
27 import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
28
29
40 @SdkPublicApi
41 public final class FullJitterBackoffStrategy implements BackoffStrategy,
42 ToCopyableBuilder<FullJitterBackoffStrategy.Builder,
43 FullJitterBackoffStrategy> {
44 private static final Duration BASE_DELAY_CEILING = Duration.ofMillis(Integer.MAX_VALUE);
45 private static final Duration MAX_BACKOFF_CEILING = Duration.ofMillis(Integer.MAX_VALUE);
46
47 private final Duration baseDelay;
48 private final Duration maxBackoffTime;
49 private final Random random;
50
51 private FullJitterBackoffStrategy(BuilderImpl builder) {
52 this(builder.baseDelay, builder.maxBackoffTime, new Random());
53 }
54
55 FullJitterBackoffStrategy(final Duration baseDelay, final Duration maxBackoffTime, final Random random) {
56 this.baseDelay = min(isNotNegative(baseDelay, "baseDelay"), BASE_DELAY_CEILING);
57 this.maxBackoffTime = min(isNotNegative(maxBackoffTime, "maxBackoffTime"), MAX_BACKOFF_CEILING);
58 this.random = random;
59 }
60
61 @Override
62 public Duration computeDelayBeforeNextRetry(RetryPolicyContext context) {
63 int ceil = calculateExponentialDelay(context.retriesAttempted(), baseDelay, maxBackoffTime);
64 return Duration.ofMillis(random.nextInt(ceil));
65 }
66
67 @Override
68 public Builder toBuilder() {
69 return builder().baseDelay(baseDelay).maxBackoffTime(maxBackoffTime);
70 }
71
72 public static Builder builder() {
73 return new BuilderImpl();
74 }
75
76 public interface Builder extends CopyableBuilder<Builder, FullJitterBackoffStrategy> {
77 Builder baseDelay(Duration baseDelay);
78
79 Duration baseDelay();
80
81 Builder maxBackoffTime(Duration maxBackoffTime);
82
83 Duration maxBackoffTime();
84
85 FullJitterBackoffStrategy build();
86 }
87
88 private static final class BuilderImpl implements Builder {
89
90 private Duration baseDelay;
91 private Duration maxBackoffTime;
92
93 private BuilderImpl() {
94 }
95
96 @Override
97 public Builder baseDelay(Duration baseDelay) {
98 this.baseDelay = baseDelay;
99 return this;
100 }
101
102 public void setBaseDelay(Duration baseDelay) {
103 baseDelay(baseDelay);
104 }
105
106 @Override
107 public Duration baseDelay() {
108 return baseDelay;
109 }
110
111 @Override
112 public Builder maxBackoffTime(Duration maxBackoffTime) {
113 this.maxBackoffTime = maxBackoffTime;
114 return this;
115 }
116
117 public void setMaxBackoffTime(Duration maxBackoffTime) {
118 maxBackoffTime(maxBackoffTime);
119 }
120
121 @Override
122 public Duration maxBackoffTime() {
123 return maxBackoffTime;
124 }
125
126 @Override
127 public FullJitterBackoffStrategy build() {
128 return new FullJitterBackoffStrategy(this);
129 }
130 }
131
132 @Override
133 public boolean equals(Object o) {
134 if (this == o) {
135 return true;
136 }
137 if (o == null || getClass() != o.getClass()) {
138 return false;
139 }
140
141 FullJitterBackoffStrategy that = (FullJitterBackoffStrategy) o;
142
143 if (!baseDelay.equals(that.baseDelay)) {
144 return false;
145 }
146 return maxBackoffTime.equals(that.maxBackoffTime);
147 }
148
149 @Override
150 public int hashCode() {
151 int result = baseDelay.hashCode();
152 result = 31 * result + maxBackoffTime.hashCode();
153 return result;
154 }
155
156 @Override
157 public String toString() {
158 return ToString.builder("FullJitterBackoffStrategy")
159 .add("baseDelay", baseDelay)
160 .add("maxBackoffTime", maxBackoffTime)
161 .build();
162 }
163 }
164