Wrap StatusRuntimeExceptions from GrpcRemoteCache
Exceptions that occur during remote interactions are expected to be
wrapped in IOException for observation by the RemoteSpawn{Runner,Cache}
layers.
Fixes #7856
Closes #7860.
PiperOrigin-RevId: 240793745
diff --git a/src/main/java/com/google/devtools/build/lib/remote/GrpcRemoteCache.java b/src/main/java/com/google/devtools/build/lib/remote/GrpcRemoteCache.java
index 8e896bd..8a4f439 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/GrpcRemoteCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/GrpcRemoteCache.java
@@ -160,7 +160,11 @@
private ListenableFuture<FindMissingBlobsResponse> getMissingDigests(
FindMissingBlobsRequest request) throws IOException, InterruptedException {
Context ctx = Context.current();
- return retrier.executeAsync(() -> ctx.call(() -> casFutureStub().findMissingBlobs(request)));
+ try {
+ return retrier.executeAsync(() -> ctx.call(() -> casFutureStub().findMissingBlobs(request)));
+ } catch (StatusRuntimeException e) {
+ throw new IOException(e);
+ }
}
private ImmutableSet<Digest> getMissingDigests(Iterable<Digest> digests)
@@ -274,6 +278,9 @@
@Override
public void onFailure(Throwable t) {
+ if (t instanceof StatusRuntimeException) {
+ t = new IOException(t);
+ }
outerF.setException(t);
}
},
@@ -289,12 +296,17 @@
Context ctx = Context.current();
AtomicLong offset = new AtomicLong(0);
ProgressiveBackoff progressiveBackoff = new ProgressiveBackoff(retrier::newBackoff);
- return retrier.executeAsync(
- () ->
- ctx.call(
- () ->
- requestRead(
- resourceName, offset, progressiveBackoff, digest, out, hashSupplier)));
+ return Futures.catchingAsync(
+ retrier.executeAsync(
+ () ->
+ ctx.call(
+ () ->
+ requestRead(
+ resourceName, offset, progressiveBackoff, digest, out, hashSupplier)),
+ progressiveBackoff),
+ StatusRuntimeException.class,
+ (e) -> Futures.immediateFailedFuture(new IOException(e)),
+ MoreExecutors.directExecutor());
}
static class ProgressiveBackoff implements Backoff {
diff --git a/src/main/java/com/google/devtools/build/lib/remote/Retrier.java b/src/main/java/com/google/devtools/build/lib/remote/Retrier.java
index b3ccedb..949050d 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/Retrier.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/Retrier.java
@@ -264,7 +264,7 @@
* Executes an {@link AsyncCallable}, retrying execution in case of failure with the given
* backoff.
*/
- private <T> ListenableFuture<T> executeAsync(AsyncCallable<T> call, Backoff backoff) {
+ public <T> ListenableFuture<T> executeAsync(AsyncCallable<T> call, Backoff backoff) {
try {
return Futures.catchingAsync(
call.call(),
diff --git a/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteCacheTest.java b/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteCacheTest.java
index 8676fb0..239109a 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteCacheTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteCacheTest.java
@@ -1008,16 +1008,12 @@
responseObserver.onError(Status.DEADLINE_EXCEEDED.asException());
}
});
- boolean passedThroughDeadlineExceeded = false;
try {
getFromFuture(client.downloadBlob(digest));
- } catch (RuntimeException e) {
+ fail("Should have thrown an exception.");
+ } catch (IOException e) {
Status st = Status.fromThrowable(e);
- if (st.getCode() != Status.Code.DEADLINE_EXCEEDED) {
- throw e;
- }
- passedThroughDeadlineExceeded = true;
+ assertThat(st.getCode()).isEqualTo(Status.Code.DEADLINE_EXCEEDED);
}
- assertThat(passedThroughDeadlineExceeded).isTrue();
}
}