diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java index 0657f2bc24729f..72c356b73f6146 100644 --- a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java @@ -126,8 +126,9 @@ public SpawnResult exec(Spawn spawn, SpawnExecutionPolicy policy) try { return downloadRemoteResults(cachedResult, policy.getFileOutErr()); } catch (CacheNotFoundException e) { - // Intentionally left empty. No cache hit, so we fall through to local or - // remote execution. + // No cache hit, so we fall through to local or remote execution. + // We set acceptCachedResult to false in order to force the action re-execution. + acceptCachedResult = false; } } } catch (IOException e) { diff --git a/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteExecutionClientTest.java b/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteExecutionClientTest.java index 0e3e9585f48f1d..daed01ac5a3846 100644 --- a/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteExecutionClientTest.java +++ b/src/test/java/com/google/devtools/build/lib/remote/GrpcRemoteExecutionClientTest.java @@ -728,4 +728,82 @@ public void read(ReadRequest request, StreamObserver responseObser assertThat(((CacheNotFoundException) t).getMissingDigest()).isEqualTo(stdOutDigest); } } + + @Test + public void remotelyReExecuteOrphanedCachedActions() throws Exception { + final Digest stdOutDigest = Digests.computeDigestUtf8("stdout"); + final ActionResult actionResult = + ActionResult.newBuilder().setStdoutDigest(stdOutDigest).build(); + serviceRegistry.addService( + new ActionCacheImplBase() { + @Override + public void getActionResult( + GetActionResultRequest request, StreamObserver responseObserver) { + responseObserver.onNext(actionResult); + responseObserver.onCompleted(); + } + }); + serviceRegistry.addService( + new ByteStreamImplBase() { + @Override + public void read(ReadRequest request, StreamObserver responseObserver) { + // All reads are a cache miss. + responseObserver.onError(Status.NOT_FOUND.asRuntimeException()); + } + + @Override + public StreamObserver write( + StreamObserver responseObserver) { + return new StreamObserver() { + @Override + public void onNext(WriteRequest request) {} + + @Override + public void onCompleted() { + responseObserver.onCompleted(); + } + + @Override + public void onError(Throwable t) { + fail("An error occurred: " + t); + } + }; + } + }); + serviceRegistry.addService( + new ExecutionImplBase() { + @Override + public void execute(ExecuteRequest request, StreamObserver responseObserver) { + assertThat(request.getSkipCacheLookup()).isTrue(); // Action will be re-executed. + responseObserver.onNext( + Operation.newBuilder() + .setDone(true) + .setResponse( + Any.pack(ExecuteResponse.newBuilder().setResult(actionResult).build())) + .build()); + responseObserver.onCompleted(); + } + }); + serviceRegistry.addService( + new ContentAddressableStorageImplBase() { + @Override + public void findMissingBlobs( + FindMissingBlobsRequest request, + StreamObserver responseObserver) { + // Nothing is missing. + responseObserver.onNext(FindMissingBlobsResponse.getDefaultInstance()); + responseObserver.onCompleted(); + } + }); + + try { + client.exec(simpleSpawn, simplePolicy); + fail("Expected an exception"); + } catch (EnvironmentalExecException expected) { + assertThat(expected).hasMessageThat().contains("Failed to download from remote cache"); + Throwable t = expected.getCause(); + assertThat(t).isInstanceOf(CacheNotFoundException.class); + assertThat(((CacheNotFoundException) t).getMissingDigest()).isEqualTo(stdOutDigest); + } + } }