ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 1 | // 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. |
| 14 | package com.google.devtools.build.lib.remote; |
| 15 | |
| 16 | import com.google.devtools.build.lib.actions.ActionInput; |
| 17 | import com.google.devtools.build.lib.actions.ExecException; |
| 18 | import com.google.devtools.build.lib.actions.ExecutionStrategy; |
| 19 | import com.google.devtools.build.lib.actions.Spawn; |
ruperts | da40fbf | 2017-09-22 05:59:42 +0200 | [diff] [blame] | 20 | import com.google.devtools.build.lib.actions.SpawnResult; |
| 21 | import com.google.devtools.build.lib.actions.SpawnResult.Status; |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 22 | import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 23 | import com.google.devtools.build.lib.events.Event; |
| 24 | import com.google.devtools.build.lib.events.Reporter; |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 25 | import com.google.devtools.build.lib.exec.SpawnCache; |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 26 | import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionPolicy; |
| 27 | import com.google.devtools.build.lib.remote.Digests.ActionKey; |
| 28 | import com.google.devtools.build.lib.remote.TreeNodeRepository.TreeNode; |
| 29 | import com.google.devtools.build.lib.vfs.Path; |
| 30 | import com.google.devtools.build.lib.vfs.PathFragment; |
| 31 | import com.google.devtools.remoteexecution.v1test.Action; |
| 32 | import com.google.devtools.remoteexecution.v1test.ActionResult; |
| 33 | import com.google.devtools.remoteexecution.v1test.Command; |
| 34 | import com.google.devtools.remoteexecution.v1test.Platform; |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 35 | import io.grpc.Context; |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 36 | import java.io.IOException; |
| 37 | import java.util.Collection; |
| 38 | import java.util.NoSuchElementException; |
| 39 | import java.util.SortedMap; |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 40 | import java.util.concurrent.atomic.AtomicBoolean; |
| 41 | import javax.annotation.Nullable; |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 42 | |
| 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 | ) |
| 51 | final 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; |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 58 | private final String buildRequestId; |
| 59 | private final String commandId; |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 60 | private final boolean verboseFailures; |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 61 | |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 62 | @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 | |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 67 | RemoteSpawnCache( |
| 68 | Path execRoot, |
| 69 | RemoteOptions options, |
| 70 | RemoteActionCache remoteCache, |
| 71 | String buildRequestId, |
| 72 | String commandId, |
| 73 | boolean verboseFailures, |
| 74 | @Nullable Reporter cmdlineReporter) { |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 75 | this.execRoot = execRoot; |
| 76 | this.options = options; |
| 77 | this.platform = options.parseRemotePlatformOverride(); |
| 78 | this.remoteCache = remoteCache; |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 79 | this.verboseFailures = verboseFailures; |
| 80 | this.cmdlineReporter = cmdlineReporter; |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 81 | this.buildRequestId = buildRequestId; |
| 82 | this.commandId = commandId; |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 83 | } |
| 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); |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 105 | 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 | } |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 128 | } |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 129 | if (options.remoteUploadLocalResults) { |
| 130 | return new CacheHandle() { |
| 131 | @Override |
| 132 | public boolean hasResult() { |
| 133 | return false; |
| 134 | } |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 135 | |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 136 | @Override |
| 137 | public SpawnResult getResult() { |
| 138 | throw new NoSuchElementException(); |
| 139 | } |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 140 | |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 141 | @Override |
| 142 | public boolean willStore() { |
| 143 | return true; |
| 144 | } |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 145 | |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 146 | @Override |
| 147 | public void store(SpawnResult result, Collection<Path> files) |
| 148 | throws InterruptedException, IOException { |
olaola | 7744b86 | 2017-09-18 23:04:33 +0200 | [diff] [blame] | 149 | boolean uploadAction = Status.SUCCESS.equals(result.status()) && result.exitCode() == 0; |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 150 | 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 Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 158 | } |
| 159 | } |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 160 | |
olaola | 6f32d5a | 2017-09-20 17:12:19 +0200 | [diff] [blame] | 161 | @Override |
| 162 | public void close() {} |
| 163 | }; |
| 164 | } else { |
| 165 | return SpawnCache.NO_RESULT_NO_STORE; |
| 166 | } |
| 167 | } finally { |
| 168 | withMetadata.detach(previous); |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 169 | } |
| 170 | } |
Benjamin Peterson | 3ff87f7 | 2017-08-21 18:41:45 +0200 | [diff] [blame] | 171 | |
| 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 | } |
ulfjack | 9274cba | 2017-08-11 23:19:48 +0200 | [diff] [blame] | 183 | } |