Skip to content
This repository was archived by the owner on Sep 26, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,11 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import com.google.api.core.ApiClock;
import com.google.api.core.ApiFuture;
import com.google.api.core.ListenableFutureToApiFuture;
import com.google.api.gax.core.FakeApiClock;
import com.google.api.gax.grpc.testing.FakeMethodDescriptor;
import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.retrying.TimedAttemptSettings;
import com.google.api.gax.rpc.ClientContext;
import com.google.api.gax.rpc.OperationCallSettings;
import com.google.api.gax.rpc.OperationCallable;
Expand Down Expand Up @@ -87,10 +85,11 @@ public class GrpcOperationCallableImplTest {
.setRetryDelayMultiplier(1)
.setMaxRetryDelay(Duration.ofMillis(1L))
.setInitialRpcTimeout(Duration.ofMillis(1L))
.setMaxAttempts(0)
.setJittered(false)
.setRpcTimeoutMultiplier(1)
.setMaxRpcTimeout(Duration.ofMillis(1L))
.setTotalTimeout(Duration.ofMillis(5L))
.setMaxAttempts(0)
.build();

private ManagedChannel initialChannel;
Expand All @@ -101,7 +100,7 @@ public class GrpcOperationCallableImplTest {
private OperationCallSettings<Integer, Color, Money, Operation> callSettings;

private FakeApiClock clock;
private DefiniteOperationPollTimedAlgorithm pollingAlgorithm;
private OperationTimedPollAlgorithm pollingAlgorithm;

@Before
public void setUp() throws IOException {
Expand All @@ -112,7 +111,7 @@ public void setUp() throws IOException {

clock = new FakeApiClock(0L);
executor = RecordingScheduler.create(clock);
pollingAlgorithm = new DefiniteOperationPollTimedAlgorithm(FAST_RETRY_SETTINGS, clock);
pollingAlgorithm = new OperationTimedPollAlgorithm(FAST_RETRY_SETTINGS, clock);

OperationsSettings.Builder settingsBuilder = OperationsSettings.defaultBuilder();
settingsBuilder
Expand Down Expand Up @@ -412,7 +411,7 @@ public void testFutureCallPollDoneOnMany() throws Exception {
mockResponse(pollChannel, Code.OK, (Object[]) pollOperations);

pollingAlgorithm =
new DefiniteOperationPollTimedAlgorithm(
new OperationTimedPollAlgorithm(
FAST_RETRY_SETTINGS
.toBuilder()
.setTotalTimeout(Duration.ofMillis(iterationsCount))
Expand Down Expand Up @@ -507,7 +506,7 @@ public void testFutureCallPollCancelOnLongTimeoutExceeded() throws Exception {
mockResponse(pollChannel, Code.OK, (Object[]) pollOperations);

pollingAlgorithm =
new DefiniteOperationPollTimedAlgorithm(
new OperationTimedPollAlgorithm(
FAST_RETRY_SETTINGS.toBuilder().setTotalTimeout(Duration.ofMillis(1000L)).build(),
clock);
callSettings = callSettings.toBuilder().setPollingAlgorithm(pollingAlgorithm).build();
Expand Down Expand Up @@ -843,20 +842,4 @@ private void mockResponse(ManagedChannel channel, Status.Code statusCode, Object
private UnaryCallable<Integer, Operation> createDirectCallable() {
return new GrpcDirectCallable<>(FakeMethodDescriptor.<Integer, Operation>create());
}

private static class DefiniteOperationPollTimedAlgorithm extends OperationTimedPollAlgorithm {
private DefiniteOperationPollTimedAlgorithm(RetrySettings globalSettings, ApiClock clock) {
super(globalSettings, clock);
}

@Override
public TimedAttemptSettings createNextAttempt(TimedAttemptSettings prevSettings) {
return super.createNextAttempt(prevSettings);
}

@Override
protected long nextRandomLong(long bound) {
return bound;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ public boolean shouldRetry(TimedAttemptSettings nextAttemptSettings) {

// Injecting Random is not possible here, as Random does not provide nextLong(long bound) method
protected long nextRandomLong(long bound) {
return bound > 0 ? ThreadLocalRandom.current().nextLong(bound) : bound;
return bound > 0 && globalSettings.isJittered()
? ThreadLocalRandom.current().nextLong(bound)
: bound;
}
}
158 changes: 101 additions & 57 deletions gax/src/main/java/com/google/api/gax/retrying/RetrySettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,52 +65,68 @@ public abstract class RetrySettings implements Serializable {
private static final long serialVersionUID = 8258475264439710899L;

/**
* The TotalTimeout parameter has ultimate control over how long the logic should keep trying the
* remote call until it gives up completely. The higher the total timeout, the more retries can be
* attempted.
* TotalTimeout has ultimate control over how long the logic should keep trying the remote call
* until it gives up completely. The higher the total timeout, the more retries can be attempted.
* The default value is {@code Duration.ZERO}.
*/
public abstract Duration getTotalTimeout();

/**
* The InitialRetryDelay parameter controls the delay before the first retry. Subsequent retries
* will use this value adjusted according to the RetryDelayMultiplier.
* InitialRetryDelay controls the delay before the first retry. Subsequent retries will use this
* value adjusted according to the RetryDelayMultiplier. The default value is {@code
* Duration.ZERO}.
*/
public abstract Duration getInitialRetryDelay();

/**
* The RetryDelayMultiplier controls the change in retry delay. The retry delay of the previous
* call is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next call.
* RetryDelayMultiplier controls the change in retry delay. The retry delay of the previous call
* is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next call. The
* default value is {@code 1.0}.
*/
public abstract double getRetryDelayMultiplier();

/**
* The MaxRetryDelay puts a limit on the value of the retry delay, so that the
* RetryDelayMultiplier can't increase the retry delay higher than this amount.
* MaxRetryDelay puts a limit on the value of the retry delay, so that the RetryDelayMultiplier
* can't increase the retry delay higher than this amount. The default value is {@code
* Duration.ZERO}.
*/
public abstract Duration getMaxRetryDelay();

/**
* MaxAttempts defines the maximum number of attempts to perform. The default value is 0. If this
* value is greater than 0, and the number of attempts reaches this limit, the logic will give up
* retrying even if the total retry time is still lower than TotalTimeout.
* MaxAttempts defines the maximum number of attempts to perform. The default value is {@code 0}.
* If this value is greater than 0, and the number of attempts reaches this limit, the logic will
* give up retrying even if the total retry time is still lower than TotalTimeout.
*/
public abstract int getMaxAttempts();

/**
* The InitialRpcTimeout parameter controls the timeout for the initial RPC. Subsequent calls will
* use this value adjusted according to the RpcTimeoutMultiplier.
* Jitter determines if the delay time should be randomized. In most cases, if jitter is set to
* {@code true} the actual delay time is calculated in the following way:
*
* <pre>{@code actualDelay = rand_between(0, min(maxRetryDelay, delay))}</pre>
*
* The default value is {@code true}.
*/
public abstract boolean isJittered();

/**
* InitialRpcTimeout controls the timeout for the initial RPC. Subsequent calls will use this
* value adjusted according to the RpcTimeoutMultiplier. The default value is {@code
* Duration.ZERO}.
*/
public abstract Duration getInitialRpcTimeout();

/**
* The RpcTimeoutMultiplier controls the change in RPC timeout. The timeout of the previous call
* is multiplied by the RpcTimeoutMultiplier to calculate the timeout for the next call.
* RpcTimeoutMultiplier controls the change in RPC timeout. The timeout of the previous call is
* multiplied by the RpcTimeoutMultiplier to calculate the timeout for the next call. The default
* value is {@code 1.0}.
*/
public abstract double getRpcTimeoutMultiplier();

/**
* The MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the
* RpcTimeoutMultiplier can't increase the RPC timeout higher than this amount.
* MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the RpcTimeoutMultiplier
* can't increase the RPC timeout higher than this amount. The default value is {@code
* Duration.ZERO}.
*/
public abstract Duration getMaxRpcTimeout();

Expand All @@ -120,11 +136,11 @@ public static Builder newBuilder() {
.setInitialRetryDelay(Duration.ZERO)
.setRetryDelayMultiplier(1.0)
.setMaxRetryDelay(Duration.ZERO)
.setMaxAttempts(1)
.setMaxAttempts(0)
.setJittered(true)
.setInitialRpcTimeout(Duration.ZERO)
.setRpcTimeoutMultiplier(1.0)
.setMaxRpcTimeout(Duration.ZERO)
.setMaxAttempts(0);
.setMaxRpcTimeout(Duration.ZERO);
}

public Builder toBuilder() {
Expand All @@ -139,104 +155,131 @@ public Builder toBuilder() {
public abstract static class Builder {

/**
* The TotalTimeout parameter has ultimate control over how long the logic should keep trying
* the remote call until it gives up completely. The higher the total timeout, the more retries
* can be attempted.
* TotalTimeout has ultimate control over how long the logic should keep trying the remote call
* until it gives up completely. The higher the total timeout, the more retries can be
* attempted. The default value is {@code Duration.ZERO}.
*/
public abstract Builder setTotalTimeout(Duration totalTimeout);

/**
* The InitialRetryDelay parameter controls the delay before the first retry. Subsequent retries
* will use this value adjusted according to the RetryDelayMultiplier.
* InitialRetryDelay controls the delay before the first retry. Subsequent retries will use this
* value adjusted according to the RetryDelayMultiplier. The default value is {@code
* Duration.ZERO}.
*/
public abstract Builder setInitialRetryDelay(Duration initialDelay);

/**
* The RetryDelayMultiplier controls the change in retry delay. The retry delay of the previous
* call is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next
* call.
* RetryDelayMultiplier controls the change in retry delay. The retry delay of the previous call
* is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next call. The
* default value is {@code 1.0}.
*/
public abstract Builder setRetryDelayMultiplier(double multiplier);

/**
* The MaxRetryDelay puts a limit on the value of the retry delay, so that the
* RetryDelayMultiplier can't increase the retry delay higher than this amount.
* MaxRetryDelay puts a limit on the value of the retry delay, so that the RetryDelayMultiplier
* can't increase the retry delay higher than this amount. The default value is {@code
* Duration.ZERO}.
*/
public abstract Builder setMaxRetryDelay(Duration maxDelay);

/**
* MaxAttempts defines the maximum number of attempts to perform. If number of attempts reaches
* this limit the logic will give up retrying even if the total retry time is still lower than
* TotalTimeout.
* MaxAttempts defines the maximum number of attempts to perform. The default value is {@code
* 0}. If this value is greater than 0, and the number of attempts reaches this limit, the logic
* will give up retrying even if the total retry time is still lower than TotalTimeout.
*/
public abstract Builder setMaxAttempts(int maxAttempts);

/**
* The InitialRpcTimeout parameter controls the timeout for the initial RPC. Subsequent calls
* will use this value adjusted according to the RpcTimeoutMultiplier.
* Jitter determines if the delay time should be randomized. In most cases, if jitter is set to
* {@code true} the actual delay time is calculated in the following way:
*
* <pre>{@code actualDelay = rand_between(0, min(maxRetryDelay, exponentialDelay))}</pre>
*
* The default value is {@code true}.
*/
public abstract Builder setJittered(boolean jittered);

/**
* InitialRpcTimeout controls the timeout for the initial RPC. Subsequent calls will use this
* value adjusted according to the RpcTimeoutMultiplier. The default value is {@code
* Duration.ZERO}.
*/
public abstract Builder setInitialRpcTimeout(Duration initialTimeout);

/**
* See the class documentation of {@link RetrySettings} for a description of what this value
* does.
* does. The default value is {@code 1.0}.
*/
public abstract Builder setRpcTimeoutMultiplier(double multiplier);

/**
* The MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the
* RpcTimeoutMultiplier can't increase the RPC timeout higher than this amount.
* MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the RpcTimeoutMultiplier
* can't increase the RPC timeout higher than this amount. The default value is {@code
* Duration.ZERO}.
*/
public abstract Builder setMaxRpcTimeout(Duration maxTimeout);

/**
* The TotalTimeout parameter has ultimate control over how long the logic should keep trying
* the remote call until it gives up completely. The higher the total timeout, the more retries
* can be attempted.
* TotalTimeout has ultimate control over how long the logic should keep trying the remote call
* until it gives up completely. The higher the total timeout, the more retries can be
* attempted. The default value is {@code Duration.ZERO}.
*/
public abstract Duration getTotalTimeout();

/**
* The InitialRetryDelay parameter controls the delay before the first retry. Subsequent retries
* will use this value adjusted according to the RetryDelayMultiplier.
* InitialRetryDelay controls the delay before the first retry. Subsequent retries will use this
* value adjusted according to the RetryDelayMultiplier. The default value is {@code
* Duration.ZERO}.
*/
public abstract Duration getInitialRetryDelay();

/**
* The RetryDelayMultiplier controls the change in retry delay. The retry delay of the previous
* call is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next
* call.
* RetryDelayMultiplier controls the change in retry delay. The retry delay of the previous call
* is multiplied by the RetryDelayMultiplier to calculate the retry delay for the next call. The
* default value is {@code 1.0}.
*/
public abstract double getRetryDelayMultiplier();

/**
* MaxAttempts defines the maximum number of attempts to perform. If the number of attempts
* reaches this limit, the logic will give up retrying even if the total retry time is still
* lower than TotalTimeout.
* MaxAttempts defines the maximum number of attempts to perform. The default value is {@code
* 0}. If this value is greater than 0, and the number of attempts reaches this limit, the logic
* will give up retrying even if the total retry time is still lower than TotalTimeout.
*/
public abstract int getMaxAttempts();

/**
* The MaxRetryDelay puts a limit on the value of the retry delay, so that the
* RetryDelayMultiplier can't increase the retry delay higher than this amount.
* Jitter determines if the delay time should be randomized. In most cases, if jitter is set to
* {@code true} the actual delay time is calculated in the following way:
*
* <pre>{@code actualDelay = rand_between(0, min(maxRetryDelay, exponentialDelay))}</pre>
*
* The default value is {@code true}.
*/
public abstract boolean isJittered();

/**
* MaxRetryDelay puts a limit on the value of the retry delay, so that the RetryDelayMultiplier
* can't increase the retry delay higher than this amount. The default value is {@code
* Duration.ZERO}.
*/
public abstract Duration getMaxRetryDelay();

/**
* The InitialRpcTimeout parameter controls the timeout for the initial RPC. Subsequent calls
* will use this value adjusted according to the RpcTimeoutMultiplier.
* InitialRpcTimeout controls the timeout for the initial RPC. Subsequent calls will use this
* value adjusted according to the RpcTimeoutMultiplier. The default value is {@code
* Duration.ZERO}.
*/
public abstract Duration getInitialRpcTimeout();

/**
* See the class documentation of {@link RetrySettings} for a description of what this value
* does.
* does. The default value is {@code 1.0}.
*/
public abstract double getRpcTimeoutMultiplier();

/**
* The MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the
* RpcTimeoutMultiplier can't increase the RPC timeout higher than this amount.
* MaxRpcTimeout puts a limit on the value of the RPC timeout, so that the RpcTimeoutMultiplier
* can't increase the RPC timeout higher than this amount.
*/
public abstract Duration getMaxRpcTimeout();

Expand Down Expand Up @@ -285,6 +328,7 @@ public RetrySettings.Builder merge(RetrySettings.Builder newSettings) {
setMaxRetryDelay(newSettings.getMaxRetryDelay());
}
setMaxAttempts(newSettings.getMaxAttempts());
setJittered(newSettings.isJittered());
if (newSettings.getInitialRpcTimeout() != null) {
setInitialRpcTimeout(newSettings.getInitialRpcTimeout());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import com.google.api.core.ApiClock;
import com.google.api.core.ApiFuture;
import com.google.api.gax.retrying.FailingCallable.CustomException;
import java.util.concurrent.CancellationException;
Expand Down Expand Up @@ -331,15 +330,4 @@ void assertFutureCancel(RetryingFuture<?> future)
assertTrue(future.isDone());
assertTrue(future.isCancelled());
}

static class DefiniteExponentialRetryAlgorithm extends ExponentialRetryAlgorithm {
DefiniteExponentialRetryAlgorithm(RetrySettings globalSettings, ApiClock clock) {
super(globalSettings, clock);
}

@Override
protected long nextRandomLong(long bound) {
return bound;
}
}
}
Loading