Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 562fcf9

Browse files
buchgrdamienmg
authored andcommitted
remote: Don't upload failed action to cache. Fixes bazelbuild#3452
Also, restructure the code for better read- and testability. Change-Id: Ibdd0413f89e4687b836b768a9e7d6315234cb825 PiperOrigin-RevId: 163322658
1 parent eebc5e8 commit 562fcf9

2 files changed

Lines changed: 72 additions & 14 deletions

File tree

src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.google.devtools.build.lib.remote;
1616

17+
import com.google.common.annotations.VisibleForTesting;
1718
import com.google.common.base.Throwables;
1819
import com.google.common.collect.ImmutableMap;
1920
import com.google.devtools.build.lib.actions.ActionInput;
@@ -54,7 +55,7 @@
5455

5556
/** A client for the remote execution service. */
5657
@ThreadSafe
57-
final class RemoteSpawnRunner implements SpawnRunner {
58+
class RemoteSpawnRunner implements SpawnRunner {
5859
private final Path execRoot;
5960
private final RemoteOptions options;
6061
// TODO(olaola): This will be set on a per-action basis instead.
@@ -127,7 +128,8 @@ public SpawnResult exec(Spawn spawn, SpawnExecutionPolicy policy)
127128
}
128129

129130
if (remoteExecutor == null) {
130-
return execLocally(spawn, policy, inputMap, remoteCache, actionKey);
131+
return execLocally(spawn, policy, inputMap, options.remoteUploadLocalResults,
132+
remoteCache, actionKey);
131133
}
132134

133135
// Upload the command and all the inputs into the remote cache.
@@ -142,7 +144,8 @@ public SpawnResult exec(Spawn spawn, SpawnExecutionPolicy policy)
142144
ExecuteResponse reply = remoteExecutor.executeRemotely(request.build());
143145
result = reply.getResult();
144146
if (options.remoteLocalFallback && result.getExitCode() != 0) {
145-
return execLocally(spawn, policy, inputMap, remoteCache, actionKey);
147+
return execLocally(spawn, policy, inputMap, options.remoteUploadLocalResults,
148+
remoteCache, actionKey);
146149
}
147150
remoteCache.download(result, execRoot, policy.getFileOutErr());
148151
return new SpawnResult.Builder()
@@ -151,7 +154,8 @@ public SpawnResult exec(Spawn spawn, SpawnExecutionPolicy policy)
151154
.build();
152155
} catch (IOException e) {
153156
if (options.remoteLocalFallback) {
154-
return execLocally(spawn, policy, inputMap, remoteCache, actionKey);
157+
return execLocally(spawn, policy, inputMap, options.remoteUploadLocalResults,
158+
remoteCache, actionKey);
155159
}
156160

157161
String message = "";
@@ -206,7 +210,7 @@ private Command buildCommand(List<String> arguments, ImmutableMap<String, String
206210
return command.build();
207211
}
208212

209-
Map<Path, Long> getInputCtimes(SortedMap<PathFragment, ActionInput> inputMap) {
213+
private Map<Path, Long> getInputCtimes(SortedMap<PathFragment, ActionInput> inputMap) {
210214
HashMap<Path, Long> ctimes = new HashMap<>();
211215
for (Map.Entry<PathFragment, ActionInput> e : inputMap.entrySet()) {
212216
ActionInput input = e.getValue();
@@ -227,23 +231,36 @@ Map<Path, Long> getInputCtimes(SortedMap<PathFragment, ActionInput> inputMap) {
227231
}
228232

229233
/**
230-
* Fallback: execute the spawn locally. If an ActionKey is provided, try to upload results to
231-
* remote action cache.
234+
* Execute a {@link Spawn} locally, using {@link #fallbackRunner}.
235+
*
236+
* <p>If possible also upload the {@link SpawnResult} to a remote cache.
232237
*/
233238
private SpawnResult execLocally(
234239
Spawn spawn,
235240
SpawnExecutionPolicy policy,
236241
SortedMap<PathFragment, ActionInput> inputMap,
237-
RemoteActionCache remoteCache,
238-
ActionKey actionKey)
239-
throws ExecException, IOException, InterruptedException {
240-
if (!options.remoteUploadLocalResults || !Spawns.mayBeCached(spawn) || remoteCache == null
241-
|| actionKey == null) {
242-
// This is an optimization to not compute the ctimes in case remote upload is disabled.
243-
return fallbackRunner.exec(spawn, policy);
242+
boolean uploadToCache,
243+
@Nullable RemoteActionCache remoteCache,
244+
@Nullable ActionKey actionKey) throws ExecException, IOException, InterruptedException {
245+
if (uploadToCache && Spawns.mayBeCached(spawn) && remoteCache != null && actionKey != null) {
246+
return execLocallyAndUpload(spawn, policy, inputMap, remoteCache, actionKey);
244247
}
248+
return fallbackRunner.exec(spawn, policy);
249+
}
250+
251+
@VisibleForTesting
252+
SpawnResult execLocallyAndUpload(
253+
Spawn spawn,
254+
SpawnExecutionPolicy policy,
255+
SortedMap<PathFragment, ActionInput> inputMap,
256+
RemoteActionCache remoteCache,
257+
ActionKey actionKey) throws ExecException, IOException, InterruptedException {
245258
Map<Path, Long> ctimesBefore = getInputCtimes(inputMap);
246259
SpawnResult result = fallbackRunner.exec(spawn, policy);
260+
if (!Status.SUCCESS.equals(result.status()) || result.exitCode() != 0) {
261+
// Don't upload failed actions.
262+
return result;
263+
}
247264
Map<Path, Long> ctimesAfter = getInputCtimes(inputMap);
248265
for (Map.Entry<Path, Long> e : ctimesBefore.entrySet()) {
249266
// Skip uploading to remote cache, because an input was modified during execution.

src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515

1616
import static com.google.common.truth.Truth.assertThat;
1717
import static org.mockito.Matchers.any;
18+
import static org.mockito.Matchers.eq;
1819
import static org.mockito.Mockito.never;
20+
import static org.mockito.Mockito.spy;
1921
import static org.mockito.Mockito.verify;
2022
import static org.mockito.Mockito.verifyZeroInteractions;
2123
import static org.mockito.Mockito.when;
@@ -30,6 +32,8 @@
3032
import com.google.devtools.build.lib.actions.SimpleSpawn;
3133
import com.google.devtools.build.lib.actions.Spawn;
3234
import com.google.devtools.build.lib.exec.SpawnInputExpander;
35+
import com.google.devtools.build.lib.exec.SpawnResult;
36+
import com.google.devtools.build.lib.exec.SpawnResult.Status;
3337
import com.google.devtools.build.lib.exec.SpawnRunner;
3438
import com.google.devtools.build.lib.exec.SpawnRunner.ProgressStatus;
3539
import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionPolicy;
@@ -54,6 +58,7 @@
5458
import org.junit.runners.JUnit4;
5559
import org.mockito.ArgumentCaptor;
5660
import org.mockito.Mock;
61+
import org.mockito.Mockito;
5762
import org.mockito.MockitoAnnotations;
5863

5964
/** Tests for {@link com.google.devtools.build.lib.remote.RemoteSpawnRunner} */
@@ -172,6 +177,42 @@ public void nonCachableSpawnsShouldNotBeCached_local() throws Exception {
172177
any(FileOutErr.class));
173178
}
174179

180+
@Test
181+
@SuppressWarnings("unchecked")
182+
public void failedActionShouldNotBeUploaded() throws Exception {
183+
// Test that the outputs of a failed locally executed action are not uploaded to a remote
184+
// cache.
185+
186+
RemoteOptions options = Options.getDefaults(RemoteOptions.class);
187+
options.remoteUploadLocalResults = true;
188+
189+
RemoteSpawnRunner runner =
190+
spy(new RemoteSpawnRunner(execRoot, options, localRunner, true, cache, null));
191+
192+
Spawn spawn = new SimpleSpawn(
193+
new FakeOwner("foo", "bar"),
194+
/*arguments=*/ ImmutableList.of(),
195+
/*environment=*/ ImmutableMap.of(),
196+
/*executionInfo=*/ ImmutableMap.of(),
197+
/*inputs=*/ ImmutableList.of(),
198+
/*outputs=*/ ImmutableList.<ActionInput>of(),
199+
ResourceSet.ZERO);
200+
SpawnExecutionPolicy policy = new FakeSpawnExecutionPolicy(spawn);
201+
202+
SpawnResult res = Mockito.mock(SpawnResult.class);
203+
when(res.exitCode()).thenReturn(1);
204+
when(res.status()).thenReturn(Status.EXECUTION_FAILED);
205+
when(localRunner.exec(eq(spawn), eq(policy))).thenReturn(res);
206+
207+
assertThat(runner.exec(spawn, policy)).isSameAs(res);
208+
209+
verify(localRunner).exec(eq(spawn), eq(policy));
210+
verify(runner).execLocallyAndUpload(eq(spawn), eq(policy), any(SortedMap.class), eq(cache),
211+
any(ActionKey.class));
212+
verify(cache, never()).upload(any(ActionKey.class), any(Path.class), any(Collection.class),
213+
any(FileOutErr.class));
214+
}
215+
175216
// TODO(buchgr): Extract a common class to be used for testing.
176217
class FakeSpawnExecutionPolicy implements SpawnExecutionPolicy {
177218

0 commit comments

Comments
 (0)