Skip to content

Commit 49a9502

Browse files
coeuvrecopybara-github
authored andcommitted
Clear all remote metadata if any of them are evicted from remote cache
With TTL based discarding and upcoming 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). This PR changes to remove all remote metadata when the remove cache eviction error happens (which should be rare with the help from TTL based discarding and lease extension) to make sure next incremental build can success. Part of bazelbuild#16660. Closes bazelbuild#17747. PiperOrigin-RevId: 516519657 Change-Id: Ia99770b9d314ca62801b73dc96d09ed8ac2233f6
1 parent dbb09c9 commit 49a9502

File tree

7 files changed

+75
-35
lines changed

7 files changed

+75
-35
lines changed

src/main/java/com/google/devtools/build/lib/actions/cache/ActionCache.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import java.util.List;
4646
import java.util.Map;
4747
import java.util.Optional;
48+
import java.util.function.Predicate;
4849
import javax.annotation.Nullable;
4950

5051
/**
@@ -76,6 +77,9 @@ public interface ActionCache {
7677
*/
7778
void remove(String key);
7879

80+
/** Removes entry from cache that matches the predicate. */
81+
void removeIf(Predicate<ActionCache.Entry> predicate);
82+
7983
/**
8084
* An entry in the ActionCache that contains all action input and output
8185
* artifact paths and their metadata plus action key itself.
@@ -246,7 +250,8 @@ public RemoteFileArtifactValue getOutputFile(Artifact output) {
246250
return outputFileMetadata.get(output.getExecPathString());
247251
}
248252

249-
Map<String, RemoteFileArtifactValue> getOutputFiles() {
253+
/** Gets metadata of all output files */
254+
public Map<String, RemoteFileArtifactValue> getOutputFiles() {
250255
return outputFileMetadata;
251256
}
252257

@@ -273,7 +278,8 @@ public SerializableTreeArtifactValue getOutputTree(SpecialArtifact output) {
273278
return outputTreeMetadata.get(output.getExecPathString());
274279
}
275280

276-
Map<String, SerializableTreeArtifactValue> getOutputTrees() {
281+
/** Gets metadata of all output trees */
282+
public Map<String, SerializableTreeArtifactValue> getOutputTrees() {
277283
return outputTreeMetadata;
278284
}
279285

src/main/java/com/google/devtools/build/lib/actions/cache/CompactPersistentActionCache.java

+10
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import java.util.concurrent.ConcurrentMap;
5656
import java.util.concurrent.TimeUnit;
5757
import java.util.concurrent.atomic.AtomicInteger;
58+
import java.util.function.Predicate;
5859
import javax.annotation.Nullable;
5960

6061
/**
@@ -342,6 +343,10 @@ public ActionCache.Entry get(String key) {
342343
return null;
343344
}
344345
byte[] data = map.get(index);
346+
return get(data);
347+
}
348+
349+
private ActionCache.Entry get(byte[] data) {
345350
try {
346351
return data != null ? decode(indexer, data) : null;
347352
} catch (IOException e) {
@@ -381,6 +386,11 @@ public void remove(String key) {
381386
map.remove(indexer.getIndex(key));
382387
}
383388

389+
@Override
390+
public void removeIf(Predicate<Entry> predicate) {
391+
map.entrySet().removeIf(entry -> predicate.test(get(entry.getValue())));
392+
}
393+
384394
@ThreadSafety.ThreadHostile
385395
@Override
386396
public long save() throws IOException {

src/main/java/com/google/devtools/build/lib/remote/BUILD

+4-1
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,11 @@ java_library(
232232
srcs = ["LeaseService.java"],
233233
deps = [
234234
"//src/main/java/com/google/devtools/build/lib/actions",
235-
"//src/main/java/com/google/devtools/build/lib/actions:action_lookup_data",
236235
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",
236+
"//src/main/java/com/google/devtools/build/lib/actions:file_metadata",
237+
"//src/main/java/com/google/devtools/build/lib/skyframe:action_execution_value",
238+
"//src/main/java/com/google/devtools/build/lib/skyframe:sky_functions",
239+
"//src/main/java/com/google/devtools/build/lib/skyframe:tree_artifact_value",
237240
"//src/main/java/com/google/devtools/build/skyframe",
238241
"//third_party:jsr305",
239242
],

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

+38-32
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,13 @@
1313
// limitations under the License.
1414
package com.google.devtools.build.lib.remote;
1515

16-
import com.google.devtools.build.lib.actions.Action;
17-
import com.google.devtools.build.lib.actions.ActionCacheUtils;
1816
import com.google.devtools.build.lib.actions.ActionInput;
19-
import com.google.devtools.build.lib.actions.ActionLookupData;
20-
import com.google.devtools.build.lib.actions.ActionLookupValue;
21-
import com.google.devtools.build.lib.actions.Artifact;
17+
import com.google.devtools.build.lib.actions.FileArtifactValue;
2218
import com.google.devtools.build.lib.actions.cache.ActionCache;
19+
import com.google.devtools.build.lib.skyframe.ActionExecutionValue;
20+
import com.google.devtools.build.lib.skyframe.SkyFunctions;
21+
import com.google.devtools.build.lib.skyframe.TreeArtifactValue;
2322
import com.google.devtools.build.skyframe.MemoizingEvaluator;
24-
import java.util.HashMap;
2523
import java.util.Set;
2624
import javax.annotation.Nullable;
2725

@@ -41,36 +39,44 @@ public void handleMissingInputs(Set<ActionInput> missingActionInputs) {
4139
return;
4240
}
4341

44-
var actions = new HashMap<ActionLookupData, Action>();
42+
// If any outputs are evicted, remove all remote metadata from skyframe and local action cache.
43+
//
44+
// With TTL based discarding and lease extension, remote cache eviction error won't happen if
45+
// remote cache can guarantee the TTL. However, if it happens, it usually means the remote cache
46+
// is under high load and it could possibly evict more blobs that Bazel wouldn't aware of.
47+
// Following builds could still fail for the same error (caused by different blobs).
4548

46-
try {
47-
for (ActionInput actionInput : missingActionInputs) {
48-
if (actionInput instanceof Artifact.DerivedArtifact) {
49-
Artifact.DerivedArtifact output = (Artifact.DerivedArtifact) actionInput;
50-
ActionLookupData actionLookupData = output.getGeneratingActionKey();
51-
var actionLookupValue =
52-
memoizingEvaluator.getExistingValue(actionLookupData.getActionLookupKey());
53-
if (actionLookupValue instanceof ActionLookupValue) {
54-
Action action =
55-
((ActionLookupValue) actionLookupValue)
56-
.getAction(actionLookupData.getActionIndex());
57-
actions.put(actionLookupData, action);
49+
memoizingEvaluator.delete(
50+
key -> {
51+
if (key.functionName().equals(SkyFunctions.ACTION_EXECUTION)) {
52+
try {
53+
var value = memoizingEvaluator.getExistingValue(key);
54+
return value instanceof ActionExecutionValue
55+
&& isRemote((ActionExecutionValue) value);
56+
} catch (InterruptedException ignored) {
57+
return false;
58+
}
5859
}
59-
}
60-
}
61-
} catch (InterruptedException e) {
62-
Thread.currentThread().interrupt();
60+
return false;
61+
});
62+
63+
if (actionCache != null) {
64+
actionCache.removeIf(
65+
entry -> !entry.getOutputFiles().isEmpty() || !entry.getOutputTrees().isEmpty());
6366
}
67+
}
6468

65-
if (!actions.isEmpty()) {
66-
var actionKeys = actions.keySet();
67-
memoizingEvaluator.delete(key -> key instanceof ActionLookupData && actionKeys.contains(key));
69+
private boolean isRemote(ActionExecutionValue value) {
70+
return value.getAllFileValues().values().stream().anyMatch(FileArtifactValue::isRemote)
71+
|| value.getAllTreeArtifactValues().values().stream().anyMatch(this::isRemoteTree);
72+
}
6873

69-
if (actionCache != null) {
70-
for (var action : actions.values()) {
71-
ActionCacheUtils.removeCacheEntry(actionCache, action);
72-
}
73-
}
74-
}
74+
private boolean isRemoteTree(TreeArtifactValue treeArtifactValue) {
75+
return treeArtifactValue.getChildValues().values().stream()
76+
.anyMatch(FileArtifactValue::isRemote)
77+
|| treeArtifactValue
78+
.getArchivedRepresentation()
79+
.map(ar -> ar.archivedFileValue().isRemote())
80+
.orElse(false);
7581
}
7682
}

src/test/java/com/google/devtools/build/lib/actions/ActionCacheCheckerTest.java

+6
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import java.util.Map;
7474
import java.util.Optional;
7575
import java.util.Set;
76+
import java.util.function.Predicate;
7677
import javax.annotation.Nullable;
7778
import org.junit.After;
7879
import org.junit.Before;
@@ -1255,6 +1256,11 @@ public void remove(String key) {
12551256
delegate.remove(key);
12561257
}
12571258

1259+
@Override
1260+
public void removeIf(Predicate<Entry> predicate) {
1261+
delegate.removeIf(predicate);
1262+
}
1263+
12581264
@Override
12591265
public long save() throws IOException {
12601266
return delegate.save();

src/test/java/com/google/devtools/build/lib/actions/util/ActionCacheTestHelper.java

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.google.devtools.build.lib.actions.cache.Protos.ActionCacheStatistics;
1818
import com.google.devtools.build.lib.actions.cache.Protos.ActionCacheStatistics.MissReason;
1919
import java.io.PrintStream;
20+
import java.util.function.Predicate;
2021

2122
/**
2223
* Utilities for tests that use the action cache.
@@ -38,6 +39,9 @@ public Entry get(String fingerprint) {
3839
@Override
3940
public void remove(String key) {}
4041

42+
@Override
43+
public void removeIf(Predicate<Entry> predicate) {}
44+
4145
@Override
4246
public long save() {
4347
return -1;

src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java

+5
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,11 @@ public synchronized void remove(String key) {
580580
actionCache.remove(key);
581581
}
582582

583+
@Override
584+
public void removeIf(java.util.function.Predicate<Entry> predicate) {
585+
actionCache.values().removeIf(predicate);
586+
}
587+
583588
public synchronized void reset() {
584589
actionCache.clear();
585590
}

0 commit comments

Comments
 (0)