remote: implement --experimental_remote_download_outputs=toplevel
This implements the second milestone of "Remote builds without the
Bytes" and introduces the "toplevel" option for the
--experimental_remote_download_outputs flag. This mode will only
download outputs of top level targets but not outputs of intermediate
actions.
On a build of Bazel against RBE I am still seeing a 50% speed up
compared to the "all" mode.
Progress towards #6862.
Closes #7984.
PiperOrigin-RevId: 243029119
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java
index 3b1732d..4bd91ae 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java
@@ -16,13 +16,18 @@
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.devtools.build.lib.remote.util.Utils.createSpawnResult;
import static com.google.devtools.build.lib.remote.util.Utils.getInMemoryOutputPath;
+import static com.google.devtools.build.lib.remote.util.Utils.hasTopLevelOutputs;
+import static com.google.devtools.build.lib.remote.util.Utils.shouldDownloadAllSpawnOutputs;
import build.bazel.remote.execution.v2.Action;
import build.bazel.remote.execution.v2.ActionResult;
import build.bazel.remote.execution.v2.Command;
import build.bazel.remote.execution.v2.Digest;
import build.bazel.remote.execution.v2.Platform;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.ActionInput;
+import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.actions.ExecutionStrategy;
import com.google.devtools.build.lib.actions.FileArtifactValue;
@@ -64,6 +69,7 @@
name = {"remote-cache"},
contextType = SpawnCache.class)
final class RemoteSpawnCache implements SpawnCache {
+
private final Path execRoot;
private final RemoteOptions options;
@@ -77,6 +83,14 @@
private final DigestUtil digestUtil;
+ /**
+ * Set of artifacts that are top level outputs
+ *
+ * <p>This set is empty unless {@link RemoteOutputsMode#TOPLEVEL} is specified. If so, this set is
+ * used to decide whether to download an output.
+ */
+ private final ImmutableSet<Artifact> topLevelOutputs;
+
RemoteSpawnCache(
Path execRoot,
RemoteOptions options,
@@ -84,7 +98,8 @@
String buildRequestId,
String commandId,
@Nullable Reporter cmdlineReporter,
- DigestUtil digestUtil) {
+ DigestUtil digestUtil,
+ ImmutableSet<Artifact> topLevelOutputs) {
this.execRoot = execRoot;
this.options = options;
this.remoteCache = remoteCache;
@@ -92,8 +107,10 @@
this.buildRequestId = buildRequestId;
this.commandId = commandId;
this.digestUtil = digestUtil;
+ this.topLevelOutputs = Preconditions.checkNotNull(topLevelOutputs, "topLevelOutputs");
}
+
@Override
public CacheHandle lookup(Spawn spawn, SpawnExecutionContext context)
throws InterruptedException, IOException, ExecException {
@@ -138,31 +155,34 @@
try (SilentCloseable c = prof.profile(ProfilerTask.REMOTE_CACHE_CHECK, "check cache hit")) {
result = remoteCache.getCachedActionResult(actionKey);
}
+ // In case the remote cache returned a failed action (exit code != 0) we treat it as a
+ // cache miss
if (result != null && result.getExitCode() == 0) {
- // In case if failed action returned (exit code != 0) we treat it as a cache miss
- // Otherwise, we know that result exists.
- PathFragment inMemoryOutputPath = getInMemoryOutputPath(spawn);
InMemoryOutput inMemoryOutput = null;
- switch (remoteOutputsMode) {
- case MINIMAL:
- try (SilentCloseable c =
- prof.profile(ProfilerTask.REMOTE_DOWNLOAD, "download outputs minimal")) {
- inMemoryOutput =
- remoteCache.downloadMinimal(
- result,
- spawn.getOutputFiles(),
- inMemoryOutputPath,
- context.getFileOutErr(),
- execRoot,
- context.getMetadataInjector());
- }
- break;
- case ALL:
- try (SilentCloseable c =
- prof.profile(ProfilerTask.REMOTE_DOWNLOAD, "download outputs")) {
- remoteCache.download(result, execRoot, context.getFileOutErr());
- }
- break;
+ boolean downloadOutputs =
+ shouldDownloadAllSpawnOutputs(
+ remoteOutputsMode,
+ /* exitCode = */ 0,
+ hasTopLevelOutputs(spawn.getOutputFiles(), topLevelOutputs));
+ if (downloadOutputs) {
+ try (SilentCloseable c =
+ prof.profile(ProfilerTask.REMOTE_DOWNLOAD, "download outputs")) {
+ remoteCache.download(result, execRoot, context.getFileOutErr());
+ }
+ } else {
+ PathFragment inMemoryOutputPath = getInMemoryOutputPath(spawn);
+ // inject output metadata
+ try (SilentCloseable c =
+ prof.profile(ProfilerTask.REMOTE_DOWNLOAD, "download outputs minimal")) {
+ inMemoryOutput =
+ remoteCache.downloadMinimal(
+ result,
+ spawn.getOutputFiles(),
+ inMemoryOutputPath,
+ context.getFileOutErr(),
+ execRoot,
+ context.getMetadataInjector());
+ }
}
SpawnResult spawnResult =
createSpawnResult(
@@ -174,10 +194,10 @@
} catch (IOException e) {
String errorMsg = e.getMessage();
if (isNullOrEmpty(errorMsg)) {
- errorMsg = e.getClass().getSimpleName();
+ errorMsg = e.getClass().getSimpleName();
}
- errorMsg = "Reading from Remote Cache:\n" + errorMsg;
- report(Event.warn(errorMsg));
+ errorMsg = "Reading from Remote Cache:\n" + errorMsg;
+ report(Event.warn(errorMsg));
} finally {
withMetadata.detach(previous);
}