|  | // 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); | 
|  | } | 
|  | } |