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