Skip to content

Commit dc74fd9

Browse files
aoeuicopybara-github
authored andcommitted
Make FrontierSerializer additionally serialize nodes in active directories.
We cannot serialize active nodes outside of active directories with frontier based invalidation. This is a step towards granular invalidation. Fixes a couple of bugs. * Nodes that are not done should not be marked ACTIVE. * DerivedArtifact should be marked ACTIVE if owner is ACTIVE. PiperOrigin-RevId: 742703528 Change-Id: I77a4ae35671008842157d9399e20a1f9b018a440
1 parent 2845a0c commit dc74fd9

File tree

6 files changed

+207
-104
lines changed

6 files changed

+207
-104
lines changed

src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionValue.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
3939
import com.google.devtools.build.lib.rules.cpp.IncludeScannable;
4040
import com.google.devtools.build.lib.skyframe.TreeArtifactValue.ArchivedRepresentation;
41+
import com.google.devtools.build.lib.skyframe.serialization.VisibleForSerialization;
4142
import com.google.devtools.build.lib.util.ClassName;
4243
import com.google.devtools.build.lib.util.HashCodes;
4344
import com.google.devtools.build.skyframe.SkyValue;
@@ -356,7 +357,8 @@ public final ImmutableMap<Artifact, FileArtifactValue> getAllFileValues() {
356357
}
357358

358359
/** The result of an action that produces rich data. */
359-
private static final class WithRichData extends SingleOutputFile {
360+
@VisibleForSerialization
361+
public static final class WithRichData extends SingleOutputFile {
360362
private final RichArtifactData richArtifactData;
361363

362364
WithRichData(

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

+1
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ java_library(
495495
"//src/main/java/com/google/devtools/build/lib/collect/nestedset",
496496
"//src/main/java/com/google/devtools/build/lib/concurrent",
497497
"//src/main/java/com/google/devtools/build/lib/rules/cpp:cpp_interface",
498+
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization:visible-for-serialization",
498499
"//src/main/java/com/google/devtools/build/lib/util",
499500
"//src/main/java/com/google/devtools/build/lib/util:hash_codes",
500501
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",

src/main/java/com/google/devtools/build/lib/skyframe/serialization/analysis/BUILD

+1-2
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,8 @@ java_library(
8888
"//src/main/java/com/google/devtools/build/lib/cmdline",
8989
"//src/main/java/com/google/devtools/build/lib/concurrent",
9090
"//src/main/java/com/google/devtools/build/lib/events",
91-
"//src/main/java/com/google/devtools/build/lib/skyframe:aspect_key_creator",
91+
"//src/main/java/com/google/devtools/build/lib/skyframe:action_execution_value",
9292
"//src/main/java/com/google/devtools/build/lib/skyframe:filesystem_keys",
93-
"//src/main/java/com/google/devtools/build/lib/skyframe:precomputed_value",
9493
"//src/main/java/com/google/devtools/build/lib/skyframe:skyframe_cluster",
9594
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization",
9695
"//src/main/java/com/google/devtools/build/lib/skyframe/toolchains:registered_execution_platforms_value",

src/main/java/com/google/devtools/build/lib/skyframe/serialization/analysis/FrontierSerializer.java

+59-71
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import com.google.common.base.Stopwatch;
2626
import com.google.common.collect.ImmutableList;
2727
import com.google.common.collect.ImmutableMap;
28-
import com.google.common.collect.Iterables;
2928
import com.google.common.eventbus.EventBus;
3029
import com.google.common.util.concurrent.ListenableFuture;
3130
import com.google.devtools.build.lib.actions.ActionLookupData;
@@ -40,7 +39,7 @@
4039
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
4140
import com.google.devtools.build.lib.server.FailureDetails.RemoteAnalysisCaching;
4241
import com.google.devtools.build.lib.server.FailureDetails.RemoteAnalysisCaching.Code;
43-
import com.google.devtools.build.lib.skyframe.PrecomputedValue;
42+
import com.google.devtools.build.lib.skyframe.ActionExecutionValue.WithRichData;
4443
import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
4544
import com.google.devtools.build.lib.skyframe.serialization.ObjectCodecs;
4645
import com.google.devtools.build.lib.skyframe.serialization.ProfileCollector;
@@ -151,6 +150,7 @@ public static Optional<FailureDetail> serializeAndUploadFrontier(
151150
versionGetter,
152151
codecs,
153152
frontierVersion,
153+
dependenciesProvider::withinActiveDirectories,
154154
selection,
155155
dependenciesProvider.getFingerprintValueService(),
156156
eventBus,
@@ -214,75 +214,44 @@ private static void dumpUploadManifest(PrintStream out, Map<SkyKey, SelectionMar
214214
@VisibleForTesting
215215
enum SelectionMarking {
216216
/**
217-
* The entry is marked as a frontier candidate.
217+
* The entry is a frontier candidate.
218218
*
219219
* <p>If a node is still a frontier candidate at the end of the selection process, it is a
220-
* frontier node and should be serialized.
220+
* frontier node.
221221
*/
222222
FRONTIER_CANDIDATE,
223-
/** The node is in the active set and will not be serialized. */
223+
/** The node is part of the active set. */
224224
ACTIVE
225225
}
226226

227-
/**
228-
* Iterates over the direct analysis deps of a node, and include them into the frontier if they've
229-
* not been seen before.
230-
*/
231-
private static void markAnalysisDirectDepsAsFrontierCandidates(
232-
SkyKey key,
233-
InMemoryGraph graph,
234-
ConcurrentHashMap<SkyKey, SelectionMarking> selection,
235-
Predicate<PackageIdentifier> matcher) {
236-
graph
237-
.getIfPresent(key)
238-
.getDirectDeps()
239-
.forEach(
240-
depKey -> {
241-
if (depKey instanceof ActionLookupKey alk
242-
&& !matcher.test(alk.getLabel().getPackageIdentifier())) {
243-
selection.putIfAbsent(depKey, FRONTIER_CANDIDATE);
244-
}
245-
});
246-
}
247-
248-
private static boolean dependsOnBuildId(InMemoryNodeEntry node) {
249-
// This method only checks direct dependencies because it is used to mark nodes as active in
250-
// case they can't be cached and the upwards transitive closure of such nodes is marked as
251-
// active anyway.
252-
return Iterables.contains(node.getDirectDeps(), PrecomputedValue.BUILD_ID);
253-
}
254-
255227
@VisibleForTesting
256228
static ImmutableMap<SkyKey, SelectionMarking> computeSelection(
257229
InMemoryGraph graph, Predicate<PackageIdentifier> matcher) {
258-
ConcurrentHashMap<SkyKey, SelectionMarking> selection = new ConcurrentHashMap<>();
230+
var selection = new ConcurrentHashMap<SkyKey, SelectionMarking>();
259231
graph.parallelForEach(
260232
node -> {
261233
switch (node.getKey()) {
262-
case ActionLookupKey key when key.getLabel() != null -> {
263-
if (matcher.test(key.getLabel().getPackageIdentifier())) {
234+
case ActionLookupKey key -> {
235+
Label label = key.getLabel();
236+
if (label != null && matcher.test(label.getPackageIdentifier())) {
264237
markActiveAndTraverseEdges(graph, key, selection);
265238
}
266239
}
267240
case ActionLookupData data -> {
268-
if (!dependsOnBuildId(node) && data.getLabel() != null) {
269-
selection.putIfAbsent(data, FRONTIER_CANDIDATE);
270-
} else {
271-
// If this is UnshareableActionLookupData, then its value will never be shared and
272-
// the ActionExecutionFunction will be re-evaluated locally. To evaluate it locally,
273-
// it will need the corresponding full ActionLookupKey's value, so that cannot be
274-
// cached as well. So, mark the ActionLookupKey (and its rdeps) as active,
275-
// so the deserializing build will not incorrectly cache hit on a CT/Aspect
276-
// that owns such actions, which should be evaluated locally then.
277-
markActiveAndTraverseEdges(graph, data.getActionLookupKey(), selection);
241+
if (!data.valueIsShareable() && !(node.getValue() instanceof WithRichData)) {
242+
// `valueIsShareable` is used by a different system that does not serialize
243+
// RunfilesArtifactValue, but the FrontierSerializer should do so. A `WithRichData`
244+
// value type can be used to distinguish this case.
245+
return;
278246
}
247+
selection.putIfAbsent(data, FRONTIER_CANDIDATE);
279248
}
280249
case Artifact artifact -> {
250+
if (!artifact.valueIsShareable()) {
251+
return;
252+
}
281253
switch (artifact) {
282254
case DerivedArtifact derived:
283-
if (derived.isConstantMetadata()) {
284-
return;
285-
}
286255
// Artifact#key is the canonical function to produce the SkyKey that will build
287256
// this artifact. We want to avoid serializing ordinary DerivedArtifacts, which
288257
// are never built by Skyframe directly, and the function will return
@@ -322,41 +291,39 @@ static ImmutableMap<SkyKey, SelectionMarking> computeSelection(
322291
// dependencies here. Those SkyValues are entirely derived from the build configuration
323292
// fragments, and the values themselves look relatively straightforward to serialize.
324293
case RegisteredExecutionPlatformsValue.Key key ->
325-
markAnalysisDirectDepsAsFrontierCandidates(key, graph, selection, matcher);
294+
markAnalysisDirectDepsAsFrontierCandidates(key, graph, selection);
326295
case RegisteredToolchainsValue.Key key ->
327-
markAnalysisDirectDepsAsFrontierCandidates(key, graph, selection, matcher);
296+
markAnalysisDirectDepsAsFrontierCandidates(key, graph, selection);
328297
case ToolchainContextKey key ->
329-
markAnalysisDirectDepsAsFrontierCandidates(key, graph, selection, matcher);
298+
markAnalysisDirectDepsAsFrontierCandidates(key, graph, selection);
330299
default -> {}
331300
}
332301
});
333302

334-
// Filter for ActionExecutionValues owned by active analysis nodes and skip them, because
335-
// they should be evaluated locally.
303+
// Marks ActionExecutionValues owned by active analysis nodes ACTIVE.
336304
return selection.entrySet().parallelStream()
337-
.map(
338-
entry -> {
339-
if (!(entry.getKey() instanceof ActionLookupData ald)) {
340-
return entry;
341-
}
342-
if (entry.getValue() == FRONTIER_CANDIDATE
343-
&& selection.get(ald.getActionLookupKey()) == ACTIVE) {
344-
return Map.entry(entry.getKey(), ACTIVE);
345-
}
346-
return entry;
347-
})
348-
.collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
305+
.collect(
306+
toImmutableMap(
307+
Map.Entry::getKey,
308+
entry ->
309+
switch (entry.getKey()) {
310+
case ActionLookupData lookupData ->
311+
selection.get(lookupData.getActionLookupKey()) == ACTIVE
312+
? ACTIVE
313+
: entry.getValue();
314+
case DerivedArtifact artifact ->
315+
selection.get(artifact.getArtifactOwner()) == ACTIVE
316+
? ACTIVE
317+
: entry.getValue();
318+
default -> entry.getValue();
319+
}));
349320
}
350321

351322
private static void markActiveAndTraverseEdges(
352323
InMemoryGraph graph,
353324
ActionLookupKey root,
354325
ConcurrentHashMap<SkyKey, SelectionMarking> selection) {
355-
Label label = root.getLabel();
356-
if (label == null) {
357-
return;
358-
}
359-
if (selection.put(root, ACTIVE) == ACTIVE) {
326+
if (root.getLabel() == null) {
360327
return;
361328
}
362329

@@ -372,6 +339,10 @@ private static void markActiveAndTraverseEdges(
372339
return;
373340
}
374341

342+
if (selection.put(root, ACTIVE) == ACTIVE) {
343+
return;
344+
}
345+
375346
for (SkyKey dep : node.getDirectDeps()) {
376347
if (!(dep instanceof ActionLookupKey actionLookupKey)) {
377348
continue;
@@ -402,6 +373,23 @@ private static void markActiveAndTraverseEdges(
402373
}
403374
}
404375

376+
/**
377+
* Iterates over the direct analysis deps of a node, and include them into the frontier if they've
378+
* not been seen before.
379+
*/
380+
private static void markAnalysisDirectDepsAsFrontierCandidates(
381+
SkyKey key, InMemoryGraph graph, ConcurrentHashMap<SkyKey, SelectionMarking> selection) {
382+
graph
383+
.getIfPresent(key)
384+
.getDirectDeps()
385+
.forEach(
386+
depKey -> {
387+
if (depKey instanceof ActionLookupKey) {
388+
selection.putIfAbsent(depKey, FRONTIER_CANDIDATE);
389+
}
390+
});
391+
}
392+
405393
/** Stopwatch that resets upon reporting the time via {@link #toString}. */
406394
private record ResettingStopwatch(Stopwatch stopwatch) {
407395
@Override

0 commit comments

Comments
 (0)