blob: 3a8d0a386842aa189ac0dd7541bccc34ebfaa1db [file] [log] [blame]
ulfjack9274cba2017-08-11 23:19:48 +02001// Copyright 2017 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.remote;
15
16import com.google.devtools.build.lib.actions.ActionInput;
17import com.google.devtools.build.lib.actions.ExecException;
18import com.google.devtools.build.lib.actions.ExecutionStrategy;
19import com.google.devtools.build.lib.actions.Spawn;
rupertsda40fbf2017-09-22 05:59:42 +020020import com.google.devtools.build.lib.actions.SpawnResult;
21import com.google.devtools.build.lib.actions.SpawnResult.Status;
ulfjack9274cba2017-08-11 23:19:48 +020022import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
Benjamin Peterson3ff87f72017-08-21 18:41:45 +020023import com.google.devtools.build.lib.events.Event;
24import com.google.devtools.build.lib.events.Reporter;
ulfjack9274cba2017-08-11 23:19:48 +020025import com.google.devtools.build.lib.exec.SpawnCache;
ulfjack9274cba2017-08-11 23:19:48 +020026import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionPolicy;
27import com.google.devtools.build.lib.remote.Digests.ActionKey;
28import com.google.devtools.build.lib.remote.TreeNodeRepository.TreeNode;
29import com.google.devtools.build.lib.vfs.Path;
30import com.google.devtools.build.lib.vfs.PathFragment;
31import com.google.devtools.remoteexecution.v1test.Action;
32import com.google.devtools.remoteexecution.v1test.ActionResult;
33import com.google.devtools.remoteexecution.v1test.Command;
34import com.google.devtools.remoteexecution.v1test.Platform;
olaola6f32d5a2017-09-20 17:12:19 +020035import io.grpc.Context;
ulfjack9274cba2017-08-11 23:19:48 +020036import java.io.IOException;
37import java.util.Collection;
38import java.util.NoSuchElementException;
39import java.util.SortedMap;
Benjamin Peterson3ff87f72017-08-21 18:41:45 +020040import java.util.concurrent.atomic.AtomicBoolean;
41import javax.annotation.Nullable;
ulfjack9274cba2017-08-11 23:19:48 +020042
43/**
44 * A remote {@link SpawnCache} implementation.
45 */
46@ThreadSafe // If the RemoteActionCache implementation is thread-safe.
47@ExecutionStrategy(
48 name = {"remote-cache"},
49 contextType = SpawnCache.class
50)
51final class RemoteSpawnCache implements SpawnCache {
52 private final Path execRoot;
53 private final RemoteOptions options;
54 // TODO(olaola): This will be set on a per-action basis instead.
55 private final Platform platform;
56
57 private final RemoteActionCache remoteCache;
olaola6f32d5a2017-09-20 17:12:19 +020058 private final String buildRequestId;
59 private final String commandId;
Benjamin Peterson3ff87f72017-08-21 18:41:45 +020060 private final boolean verboseFailures;
ulfjack9274cba2017-08-11 23:19:48 +020061
Benjamin Peterson3ff87f72017-08-21 18:41:45 +020062 @Nullable private final Reporter cmdlineReporter;
63
64 // Used to ensure that a warning is reported only once.
65 private final AtomicBoolean warningReported = new AtomicBoolean();
66
olaola6f32d5a2017-09-20 17:12:19 +020067 RemoteSpawnCache(
68 Path execRoot,
69 RemoteOptions options,
70 RemoteActionCache remoteCache,
71 String buildRequestId,
72 String commandId,
73 boolean verboseFailures,
74 @Nullable Reporter cmdlineReporter) {
ulfjack9274cba2017-08-11 23:19:48 +020075 this.execRoot = execRoot;
76 this.options = options;
77 this.platform = options.parseRemotePlatformOverride();
78 this.remoteCache = remoteCache;
Benjamin Peterson3ff87f72017-08-21 18:41:45 +020079 this.verboseFailures = verboseFailures;
80 this.cmdlineReporter = cmdlineReporter;
olaola6f32d5a2017-09-20 17:12:19 +020081 this.buildRequestId = buildRequestId;
82 this.commandId = commandId;
ulfjack9274cba2017-08-11 23:19:48 +020083 }
84
85 @Override
86 public CacheHandle lookup(Spawn spawn, SpawnExecutionPolicy policy)
87 throws InterruptedException, IOException, ExecException {
88 // Temporary hack: the TreeNodeRepository should be created and maintained upstream!
89 TreeNodeRepository repository =
90 new TreeNodeRepository(execRoot, policy.getActionInputFileCache());
91 SortedMap<PathFragment, ActionInput> inputMap = policy.getInputMapping();
92 TreeNode inputRoot = repository.buildFromActionInputs(inputMap);
93 repository.computeMerkleDigests(inputRoot);
94 Command command = RemoteSpawnRunner.buildCommand(spawn.getArguments(), spawn.getEnvironment());
95 Action action =
96 RemoteSpawnRunner.buildAction(
97 spawn.getOutputFiles(),
98 Digests.computeDigest(command),
99 repository.getMerkleDigest(inputRoot),
100 platform,
101 policy.getTimeout());
102
103 // Look up action cache, and reuse the action output if it is found.
104 final ActionKey actionKey = Digests.computeActionKey(action);
olaola6f32d5a2017-09-20 17:12:19 +0200105 Context withMetadata =
106 TracingMetadataUtils.contextWithMetadata(buildRequestId, commandId, actionKey);
107 // Metadata will be available in context.current() until we detach.
108 // This is done via a thread-local variable.
109 Context previous = withMetadata.attach();
110 try {
111 ActionResult result =
112 this.options.remoteAcceptCached ? remoteCache.getCachedActionResult(actionKey) : null;
113 if (result != null) {
114 // We don't cache failed actions, so we know the outputs exist.
115 // For now, download all outputs locally; in the future, we can reuse the digests to
116 // just update the TreeNodeRepository and continue the build.
117 try {
118 remoteCache.download(result, execRoot, policy.getFileOutErr());
119 SpawnResult spawnResult =
120 new SpawnResult.Builder()
121 .setStatus(Status.SUCCESS)
122 .setExitCode(result.getExitCode())
123 .build();
124 return SpawnCache.success(spawnResult);
125 } catch (CacheNotFoundException e) {
126 // There's a cache miss. Fall back to local execution.
127 }
ulfjack9274cba2017-08-11 23:19:48 +0200128 }
olaola6f32d5a2017-09-20 17:12:19 +0200129 if (options.remoteUploadLocalResults) {
130 return new CacheHandle() {
131 @Override
132 public boolean hasResult() {
133 return false;
134 }
ulfjack9274cba2017-08-11 23:19:48 +0200135
olaola6f32d5a2017-09-20 17:12:19 +0200136 @Override
137 public SpawnResult getResult() {
138 throw new NoSuchElementException();
139 }
ulfjack9274cba2017-08-11 23:19:48 +0200140
olaola6f32d5a2017-09-20 17:12:19 +0200141 @Override
142 public boolean willStore() {
143 return true;
144 }
ulfjack9274cba2017-08-11 23:19:48 +0200145
olaola6f32d5a2017-09-20 17:12:19 +0200146 @Override
147 public void store(SpawnResult result, Collection<Path> files)
148 throws InterruptedException, IOException {
olaola7744b862017-09-18 23:04:33 +0200149 boolean uploadAction = Status.SUCCESS.equals(result.status()) && result.exitCode() == 0;
olaola6f32d5a2017-09-20 17:12:19 +0200150 try {
151 remoteCache.upload(actionKey, execRoot, files, policy.getFileOutErr(), uploadAction);
152 } catch (IOException e) {
153 if (verboseFailures) {
154 report(Event.debug("Upload to remote cache failed: " + e.getMessage()));
155 } else {
156 reportOnce(Event.warn("Some artifacts failed be uploaded to the remote cache."));
157 }
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200158 }
159 }
ulfjack9274cba2017-08-11 23:19:48 +0200160
olaola6f32d5a2017-09-20 17:12:19 +0200161 @Override
162 public void close() {}
163 };
164 } else {
165 return SpawnCache.NO_RESULT_NO_STORE;
166 }
167 } finally {
168 withMetadata.detach(previous);
ulfjack9274cba2017-08-11 23:19:48 +0200169 }
170 }
Benjamin Peterson3ff87f72017-08-21 18:41:45 +0200171
172 private void reportOnce(Event evt) {
173 if (warningReported.compareAndSet(false, true)) {
174 report(evt);
175 }
176 }
177
178 private void report(Event evt) {
179 if (cmdlineReporter != null) {
180 cmdlineReporter.handle(evt);
181 }
182 }
ulfjack9274cba2017-08-11 23:19:48 +0200183}