diff --git a/src/main/java/com/google/devtools/build/lib/remote/GrpcCacheClient.java b/src/main/java/com/google/devtools/build/lib/remote/GrpcCacheClient.java index 29ee7acf7d9d27..9a2e2916db29fd 100644 --- a/src/main/java/com/google/devtools/build/lib/remote/GrpcCacheClient.java +++ b/src/main/java/com/google/devtools/build/lib/remote/GrpcCacheClient.java @@ -366,6 +366,12 @@ public void onNext(ReadResponse readResponse) { @Override public void onError(Throwable t) { + if (offset.get() == digest.getSizeBytes()) { + // If the file was fully downloaded, it doesn't matter if there was an error at + // the end of the stream. + onCompleted(); + return; + } Status status = Status.fromThrowable(t); if (status.getCode() == Status.Code.NOT_FOUND) { future.setException(new CacheNotFoundException(digest)); diff --git a/src/test/java/com/google/devtools/build/lib/remote/GrpcCacheClientTest.java b/src/test/java/com/google/devtools/build/lib/remote/GrpcCacheClientTest.java index 4999dc73105a32..8509d4ad838d68 100644 --- a/src/test/java/com/google/devtools/build/lib/remote/GrpcCacheClientTest.java +++ b/src/test/java/com/google/devtools/build/lib/remote/GrpcCacheClientTest.java @@ -962,6 +962,28 @@ public void read(ReadRequest request, StreamObserver responseObser Mockito.verify(mockBackoff, Mockito.never()).nextDelayMillis(any(Exception.class)); } + @Test + public void downloadBlobDoesNotRetryZeroLengthRequests() + throws IOException, InterruptedException { + Backoff mockBackoff = Mockito.mock(Backoff.class); + final GrpcCacheClient client = + newClient(Options.getDefaults(RemoteOptions.class), () -> mockBackoff); + final Digest digest = DIGEST_UTIL.computeAsUtf8("abcdefg"); + serviceRegistry.addService( + new ByteStreamImplBase() { + @Override + public void read(ReadRequest request, StreamObserver responseObserver) { + assertThat(request.getResourceName().contains(digest.getHash())).isTrue(); + assertThat(request.getReadOffset()).isEqualTo(0); + ByteString data = ByteString.copyFromUtf8("abcdefg"); + responseObserver.onNext(ReadResponse.newBuilder().setData(data).build()); + responseObserver.onError(Status.INTERNAL.asException()); + } + }); + assertThat(new String(downloadBlob(context, client, digest), UTF_8)).isEqualTo("abcdefg"); + Mockito.verify(mockBackoff, Mockito.never()).nextDelayMillis(any(Exception.class)); + } + @Test public void downloadBlobPassesThroughDeadlineExceededWithoutProgress() throws IOException { Backoff mockBackoff = Mockito.mock(Backoff.class);