| // Copyright 2023 The Bazel Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| package com.google.devtools.build.lib.remote; |
| |
| import com.google.devtools.build.lib.actions.ActionInput; |
| import com.google.devtools.build.lib.actions.FileArtifactValue; |
| import com.google.devtools.build.lib.actions.cache.ActionCache; |
| import com.google.devtools.build.lib.skyframe.ActionExecutionValue; |
| import com.google.devtools.build.lib.skyframe.SkyFunctions; |
| import com.google.devtools.build.lib.skyframe.TreeArtifactValue; |
| import com.google.devtools.build.skyframe.MemoizingEvaluator; |
| import java.util.Set; |
| import javax.annotation.Nullable; |
| |
| /** A lease service that manages the lease of remote blobs. */ |
| public class LeaseService { |
| private final MemoizingEvaluator memoizingEvaluator; |
| @Nullable private final ActionCache actionCache; |
| |
| public LeaseService(MemoizingEvaluator memoizingEvaluator, @Nullable ActionCache actionCache) { |
| this.memoizingEvaluator = memoizingEvaluator; |
| this.actionCache = actionCache; |
| } |
| |
| /** Clean up internal state when files are evicted from remote CAS. */ |
| public void handleMissingInputs(Set<ActionInput> missingActionInputs) { |
| if (missingActionInputs.isEmpty()) { |
| return; |
| } |
| |
| // If any outputs are evicted, remove all remote metadata from skyframe and local action cache. |
| // |
| // With TTL based discarding and lease extension, remote cache eviction error won't happen if |
| // remote cache can guarantee the TTL. However, if it happens, it usually means the remote cache |
| // is under high load and it could possibly evict more blobs that Bazel wouldn't aware of. |
| // Following builds could still fail for the same error (caused by different blobs). |
| |
| memoizingEvaluator.delete( |
| key -> { |
| if (key.functionName().equals(SkyFunctions.ACTION_EXECUTION)) { |
| try { |
| var value = memoizingEvaluator.getExistingValue(key); |
| return value instanceof ActionExecutionValue |
| && isRemote((ActionExecutionValue) value); |
| } catch (InterruptedException ignored) { |
| return false; |
| } |
| } |
| return false; |
| }); |
| |
| if (actionCache != null) { |
| actionCache.removeIf( |
| entry -> !entry.getOutputFiles().isEmpty() || !entry.getOutputTrees().isEmpty()); |
| } |
| } |
| |
| private boolean isRemote(ActionExecutionValue value) { |
| return value.getAllFileValues().values().stream().anyMatch(FileArtifactValue::isRemote) |
| || value.getAllTreeArtifactValues().values().stream().anyMatch(this::isRemoteTree); |
| } |
| |
| private boolean isRemoteTree(TreeArtifactValue treeArtifactValue) { |
| return treeArtifactValue.getChildValues().values().stream() |
| .anyMatch(FileArtifactValue::isRemote) |
| || treeArtifactValue |
| .getArchivedRepresentation() |
| .map(ar -> ar.archivedFileValue().isRemote()) |
| .orElse(false); |
| } |
| } |