xref: /aosp_15_r20/external/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/retrying/RetrySettings.java (revision 882aa7c72c3cd3b66e72a261bdd69b93f7de7670)
1 /*
2  * Copyright 2016 Google LLC
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google LLC nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 package com.google.api.gax.retrying;
31 
32 import com.google.api.core.BetaApi;
33 import com.google.auto.value.AutoValue;
34 import com.google.common.annotations.VisibleForTesting;
35 import java.io.Serializable;
36 import org.threeten.bp.Duration;
37 
38 /**
39  * Holds the parameters for <b>retry</b> or <b>poll</b> logic with jitter, timeout and exponential
40  * backoff. Actual implementation of the logic is elsewhere.
41  *
42  * <p>The intent of these settings is to be used with a call to a remote server, which could either
43  * fail (and return an error code) or not respond (and cause a timeout). When there is a failure or
44  * timeout, the logic should keep trying until the total timeout has passed.
45  *
46  * <p>The "total timeout" and "max attempts" settings have ultimate control over how long the logic
47  * should keep trying the remote call until it gives up completely. The remote call will be retried
48  * until one of those thresholds is crossed. To avoid unbounded rpc calls, it is required to
49  * configure one of those settings. If both are 0, then retries will be disabled. The other settings
50  * are considered more advanced.
51  *
52  * <p>Retry delay and timeout start at specific values, and are tracked separately from each other.
53  * The very first call (before any retries) will use the initial timeout.
54  *
55  * <p>If the last remote call is a failure, then the retrier will wait for the current retry delay
56  * before attempting another call, and then the retry delay will be multiplied by the retry delay
57  * multiplier for the next failure. The timeout will not be affected, except in the case where the
58  * timeout would result in a deadline past the total timeout; in that circumstance, a new timeout
59  * value is computed which will terminate the call when the total time is up.
60  *
61  * <p>If the last remote call is a timeout, then the retrier will compute a new timeout and make
62  * another call. The new timeout is computed by multiplying the current timeout by the timeout
63  * multiplier, but if that results in a deadline after the total timeout, then a new timeout value
64  * is computed which will terminate the call when the total time is up.
65  *
66  * <p>Server streaming RPCs interpret RPC timeouts a bit differently. For server streaming RPCs, the
67  * RPC timeout gets converted into a wait timeout {@link
68  * com.google.api.gax.rpc.ApiCallContext#withStreamWaitTimeout(Duration)}.
69  */
70 @AutoValue
71 public abstract class RetrySettings implements Serializable {
72 
73   private static final long serialVersionUID = 8258475264439710899L;
74 
75   /**
76    * TotalTimeout has ultimate control over how long the logic should keep trying the remote call
77    * until it gives up completely. The higher the total timeout, the more retries can be attempted.
78    * The default value is {@code Duration.ZERO}.
79    */
getTotalTimeout()80   public abstract Duration getTotalTimeout();
81 
82   /**
83    * InitialRetryDelay controls the delay before the first retry. Subsequent retries will use this
84    * value adjusted according to the RetryDelayMultiplier. The default value is {@code
85    * Duration.ZERO}.
86    */
getInitialRetryDelay()87   public abstract Duration getInitialRetryDelay();
88 
89   /**
90    * RetryDelayMultiplier controls the change in retry delay. The retry delay of the previous call
91    * is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next call. The
92    * default value is {@code 1.0}.
93    */
getRetryDelayMultiplier()94   public abstract double getRetryDelayMultiplier();
95 
96   /**
97    * MaxRetryDelay puts a limit on the value of the retry delay, so that the RetryDelayMultiplier
98    * can't increase the retry delay higher than this amount. The default value is {@code
99    * Duration.ZERO}.
100    */
getMaxRetryDelay()101   public abstract Duration getMaxRetryDelay();
102 
103   /**
104    * MaxAttempts defines the maximum number of attempts to perform. The default value is {@code 0}.
105    * If this value is greater than 0, and the number of attempts reaches this limit, the logic will
106    * give up retrying even if the total retry time is still lower than TotalTimeout.
107    */
getMaxAttempts()108   public abstract int getMaxAttempts();
109 
110   /**
111    * Jitter determines if the delay time should be randomized. In most cases, if jitter is set to
112    * {@code true} the actual delay time is calculated in the following way:
113    *
114    * <pre>{@code actualDelay = rand_between(0, min(maxRetryDelay, delay))}</pre>
115    *
116    * The default value is {@code true}.
117    *
118    * @deprecated Retries always jitter.
119    */
120   @Deprecated
121   @VisibleForTesting
isJittered()122   public abstract boolean isJittered();
123 
124   /**
125    * InitialRpcTimeout controls the timeout for the initial RPC. Subsequent calls will use this
126    * value adjusted according to the RpcTimeoutMultiplier. The default value is {@code
127    * Duration.ZERO}.
128    */
getInitialRpcTimeout()129   public abstract Duration getInitialRpcTimeout();
130 
131   /**
132    * RpcTimeoutMultiplier controls the change in RPC timeout. The timeout of the previous call is
133    * multiplied by the RpcTimeoutMultiplier to calculate the timeout for the next call. The default
134    * value is {@code 1.0}.
135    */
getRpcTimeoutMultiplier()136   public abstract double getRpcTimeoutMultiplier();
137 
138   /**
139    * MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the RpcTimeoutMultiplier
140    * can't increase the RPC timeout higher than this amount. The default value is {@code
141    * Duration.ZERO}.
142    */
getMaxRpcTimeout()143   public abstract Duration getMaxRpcTimeout();
144 
newBuilder()145   public static Builder newBuilder() {
146     return new AutoValue_RetrySettings.Builder()
147         .setTotalTimeout(Duration.ZERO)
148         .setInitialRetryDelay(Duration.ZERO)
149         .setRetryDelayMultiplier(1.0)
150         .setMaxRetryDelay(Duration.ZERO)
151         .setMaxAttempts(0)
152         .setJittered(true)
153         .setInitialRpcTimeout(Duration.ZERO)
154         .setRpcTimeoutMultiplier(1.0)
155         .setMaxRpcTimeout(Duration.ZERO);
156   }
157 
toBuilder()158   public abstract Builder toBuilder();
159 
160   /**
161    * A base builder class for {@link RetrySettings}. See the class documentation of {@link
162    * RetrySettings} for a description of the different values that can be set.
163    */
164   @AutoValue.Builder
165   public abstract static class Builder {
166 
167     /**
168      * TotalTimeout has ultimate control over how long the logic should keep trying the remote call
169      * until it gives up completely. The higher the total timeout, the more retries can be
170      * attempted. The default value is {@code Duration.ZERO}.
171      */
setTotalTimeout(Duration totalTimeout)172     public abstract Builder setTotalTimeout(Duration totalTimeout);
173 
174     /**
175      * InitialRetryDelay controls the delay before the first retry. Subsequent retries will use this
176      * value adjusted according to the RetryDelayMultiplier. The default value is {@code
177      * Duration.ZERO}.
178      */
setInitialRetryDelay(Duration initialDelay)179     public abstract Builder setInitialRetryDelay(Duration initialDelay);
180 
181     /**
182      * RetryDelayMultiplier controls the change in retry delay. The retry delay of the previous call
183      * is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next call. The
184      * default value is {@code 1.0}.
185      */
setRetryDelayMultiplier(double multiplier)186     public abstract Builder setRetryDelayMultiplier(double multiplier);
187 
188     /**
189      * MaxRetryDelay puts a limit on the value of the retry delay, so that the RetryDelayMultiplier
190      * can't increase the retry delay higher than this amount. The default value is {@code
191      * Duration.ZERO}.
192      */
setMaxRetryDelay(Duration maxDelay)193     public abstract Builder setMaxRetryDelay(Duration maxDelay);
194 
195     /**
196      * MaxAttempts defines the maximum number of attempts to perform. The default value is {@code
197      * 0}. If this value is greater than 0, and the number of attempts reaches this limit, the logic
198      * will give up retrying even if the total retry time is still lower than TotalTimeout.
199      */
setMaxAttempts(int maxAttempts)200     public abstract Builder setMaxAttempts(int maxAttempts);
201 
202     /**
203      * Jitter determines if the delay time should be randomized. If jitter is set to {@code true}
204      * the actual delay time is calculated in the following way:
205      *
206      * <pre>{@code actualDelay = rand_between(0, min(maxRetryDelay, exponentialDelay))}</pre>
207      *
208      * The default value is {@code true}, and this method will be a no-op soon.
209      *
210      * @deprecated Retries always jitter.
211      */
212     @Deprecated
213     @VisibleForTesting
setJittered(boolean jittered)214     public abstract Builder setJittered(boolean jittered);
215 
216     /**
217      * InitialRpcTimeout controls the timeout for the initial RPC. Subsequent calls will use this
218      * value adjusted according to the RpcTimeoutMultiplier. The default value is {@code
219      * Duration.ZERO}.
220      */
setInitialRpcTimeout(Duration initialTimeout)221     public abstract Builder setInitialRpcTimeout(Duration initialTimeout);
222 
223     /**
224      * See the class documentation of {@link RetrySettings} for a description of what this value
225      * does. The default value is {@code 1.0}.
226      */
setRpcTimeoutMultiplier(double multiplier)227     public abstract Builder setRpcTimeoutMultiplier(double multiplier);
228 
229     /**
230      * MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the RpcTimeoutMultiplier
231      * can't increase the RPC timeout higher than this amount. The default value is {@code
232      * Duration.ZERO}.
233      */
setMaxRpcTimeout(Duration maxTimeout)234     public abstract Builder setMaxRpcTimeout(Duration maxTimeout);
235 
236     /**
237      * TotalTimeout has ultimate control over how long the logic should keep trying the remote call
238      * until it gives up completely. The higher the total timeout, the more retries can be
239      * attempted. The default value is {@code Duration.ZERO}.
240      */
getTotalTimeout()241     public abstract Duration getTotalTimeout();
242 
243     /**
244      * InitialRetryDelay controls the delay before the first retry. Subsequent retries will use this
245      * value adjusted according to the RetryDelayMultiplier. The default value is {@code
246      * Duration.ZERO}.
247      */
getInitialRetryDelay()248     public abstract Duration getInitialRetryDelay();
249 
250     /**
251      * RetryDelayMultiplier controls the change in retry delay. The retry delay of the previous call
252      * is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next call. The
253      * default value is {@code 1.0}.
254      */
getRetryDelayMultiplier()255     public abstract double getRetryDelayMultiplier();
256 
257     /**
258      * MaxAttempts defines the maximum number of attempts to perform. The default value is {@code
259      * 0}. If this value is greater than 0, and the number of attempts reaches this limit, the logic
260      * will give up retrying even if the total retry time is still lower than TotalTimeout.
261      */
getMaxAttempts()262     public abstract int getMaxAttempts();
263 
264     /**
265      * Jitter determines if the delay time should be randomized. In most cases, if jitter is set to
266      * {@code true} the actual delay time is calculated in the following way:
267      *
268      * <pre>{@code actualDelay = rand_between(0, min(maxRetryDelay, exponentialDelay))}</pre>
269      *
270      * The default value is {@code true}.
271      */
isJittered()272     public abstract boolean isJittered();
273 
274     /**
275      * MaxRetryDelay puts a limit on the value of the retry delay, so that the RetryDelayMultiplier
276      * can't increase the retry delay higher than this amount. The default value is {@code
277      * Duration.ZERO}.
278      */
getMaxRetryDelay()279     public abstract Duration getMaxRetryDelay();
280 
281     /**
282      * InitialRpcTimeout controls the timeout for the initial RPC. Subsequent calls will use this
283      * value adjusted according to the RpcTimeoutMultiplier. The default value is {@code
284      * Duration.ZERO}.
285      */
getInitialRpcTimeout()286     public abstract Duration getInitialRpcTimeout();
287 
288     /**
289      * See the class documentation of {@link RetrySettings} for a description of what this value
290      * does. The default value is {@code 1.0}.
291      */
getRpcTimeoutMultiplier()292     public abstract double getRpcTimeoutMultiplier();
293 
294     /**
295      * MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the RpcTimeoutMultiplier
296      * can't increase the RPC timeout higher than this amount.
297      */
getMaxRpcTimeout()298     public abstract Duration getMaxRpcTimeout();
299 
300     /**
301      * Configures the timeout settings with the given timeout such that the logical call will take
302      * no longer than the given timeout and each RPC attempt will use only the time remaining in the
303      * logical call as a timeout.
304      *
305      * <p>Using this method in conjunction with individual {@link RetrySettings} timeout field
306      * setters is not advised, because only the order in which they are invoked determines which
307      * setter is respected.
308      */
309     @BetaApi
setLogicalTimeout(Duration timeout)310     public Builder setLogicalTimeout(Duration timeout) {
311       return setRpcTimeoutMultiplier(1)
312           .setInitialRpcTimeout(timeout)
313           .setMaxRpcTimeout(timeout)
314           .setTotalTimeout(timeout);
315     }
316 
autoBuild()317     abstract RetrySettings autoBuild();
318 
build()319     public RetrySettings build() {
320       RetrySettings params = autoBuild();
321       if (params.getTotalTimeout().toMillis() < 0) {
322         throw new IllegalStateException("total timeout must not be negative");
323       }
324       if (params.getInitialRetryDelay().toMillis() < 0) {
325         throw new IllegalStateException("initial retry delay must not be negative");
326       }
327       if (params.getRetryDelayMultiplier() < 1.0) {
328         throw new IllegalStateException("retry delay multiplier must be at least 1");
329       }
330       if (params.getMaxRetryDelay().compareTo(params.getInitialRetryDelay()) < 0) {
331         throw new IllegalStateException("max retry delay must not be shorter than initial delay");
332       }
333       if (params.getMaxAttempts() < 0) {
334         throw new IllegalStateException("max attempts must be non-negative");
335       }
336       if (params.getInitialRpcTimeout().toMillis() < 0) {
337         throw new IllegalStateException("initial rpc timeout must not be negative");
338       }
339       if (params.getMaxRpcTimeout().compareTo(params.getInitialRpcTimeout()) < 0) {
340         throw new IllegalStateException("max rpc timeout must not be shorter than initial timeout");
341       }
342       if (params.getRpcTimeoutMultiplier() < 1.0) {
343         throw new IllegalStateException("rpc timeout multiplier must be at least 1");
344       }
345       return params;
346     }
347 
merge(RetrySettings.Builder newSettings)348     public RetrySettings.Builder merge(RetrySettings.Builder newSettings) {
349       if (newSettings.getTotalTimeout() != null) {
350         setTotalTimeout(newSettings.getTotalTimeout());
351       }
352       if (newSettings.getInitialRetryDelay() != null) {
353         setInitialRetryDelay(newSettings.getInitialRetryDelay());
354       }
355       if (newSettings.getRetryDelayMultiplier() >= 1) {
356         setRetryDelayMultiplier(newSettings.getRetryDelayMultiplier());
357       }
358       if (newSettings.getMaxRetryDelay() != null) {
359         setMaxRetryDelay(newSettings.getMaxRetryDelay());
360       }
361       setMaxAttempts(newSettings.getMaxAttempts());
362       setJittered(newSettings.isJittered());
363       if (newSettings.getInitialRpcTimeout() != null) {
364         setInitialRpcTimeout(newSettings.getInitialRpcTimeout());
365       }
366       if (newSettings.getRpcTimeoutMultiplier() >= 1) {
367         setRpcTimeoutMultiplier(newSettings.getRpcTimeoutMultiplier());
368       }
369       if (newSettings.getMaxRpcTimeout() != null) {
370         setMaxRpcTimeout(newSettings.getMaxRpcTimeout());
371       }
372       return this;
373     }
374   }
375 }
376