// Copyright 2017 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.remote; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.ListeningScheduledExecutorService; import io.grpc.Status; import io.grpc.StatusException; import io.grpc.StatusRuntimeException; import java.time.Duration; import java.util.concurrent.Callable; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Predicate; import java.util.function.Supplier; /** * Specific retry logic for remote execution/caching. * *
A call can disable retries by throwing a {@link PassThroughException}.
* RemoteRetrier r = ...;
* try {
* r.execute(() -> {
* // Not retried.
* throw PassThroughException(new IOException("fail"));
* }
* } catch (RetryException e) {
* // e.getCause() is the IOException
* System.out.println(e.getCause());
* }
*
*/
public class RemoteRetrier extends Retrier {
/**
* Wraps around an {@link Exception} to make it pass through a single layer of retries.
*/
public static class PassThroughException extends Exception {
public PassThroughException(Exception e) {
super(e);
}
}
public static final Predicate super Exception> RETRIABLE_GRPC_ERRORS =
e -> {
if (!(e instanceof StatusException) && !(e instanceof StatusRuntimeException)) {
return false;
}
Status s = Status.fromThrowable(e);
switch (s.getCode()) {
case CANCELLED:
return !Thread.currentThread().isInterrupted();
case UNKNOWN:
case DEADLINE_EXCEEDED:
case ABORTED:
case INTERNAL:
case UNAVAILABLE:
case UNAUTHENTICATED:
case RESOURCE_EXHAUSTED:
return true;
default:
return false;
}
};
public RemoteRetrier(
RemoteOptions options,
Predicate super Exception> shouldRetry,
ListeningScheduledExecutorService retryScheduler,
CircuitBreaker circuitBreaker) {
this(
options.experimentalRemoteRetry
? () -> new ExponentialBackoff(options)
: () -> RETRIES_DISABLED,
shouldRetry,
retryScheduler,
circuitBreaker);
}
public RemoteRetrier(
Supplier