Skip to content

Commit

Permalink
Permit action rewinding in eligible builds (no incremental state, no …
Browse files Browse the repository at this point in the history
…action cache).

This change open sources a bit more of the action rewinding machinery. However, action rewinding still requires a `LostInputsActionExecutionException` or `LostInputsExecException` to be thrown from a running action in order to be initiated. At the time of this writing, neither of these exceptions are ever thrown in bazel.

PiperOrigin-RevId: 442104054
  • Loading branch information
justinhorvitz authored and copybara-github committed Apr 15, 2022
1 parent bd67975 commit 68ffdd2
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/main/java/com/google/devtools/build/lib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/skyframe:top_level_aspects_value",
"//src/main/java/com/google/devtools/build/lib/skyframe:workspace_info",
"//src/main/java/com/google/devtools/build/lib/skyframe/actiongraph/v2:actiongraph_v2",
"//src/main/java/com/google/devtools/build/lib/skyframe/rewinding",
"//src/main/java/com/google/devtools/build/lib/skyframe/rewinding:action_rewound_event",
"//src/main/java/com/google/devtools/build/lib/unix",
"//src/main/java/com/google/devtools/build/lib/util",
"//src/main/java/com/google/devtools/build/lib/util:TestType",
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/google/devtools/build/lib/skyframe/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,8 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/rules:repository/workspace_file_helper",
"//src/main/java/com/google/devtools/build/lib/skyframe/actiongraph/v2:actiongraph_v2",
"//src/main/java/com/google/devtools/build/lib/skyframe/rewinding",
"//src/main/java/com/google/devtools/build/lib/skyframe/rewinding:action_rewound_event",
"//src/main/java/com/google/devtools/build/lib/skyframe/rewinding:rewindable_graph_inconsistency_receiver",
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec:serialization-constant",
"//src/main/java/com/google/devtools/build/lib/util",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
import com.google.devtools.build.lib.skyframe.FilesystemValueChecker.ImmutableBatchDirtyResult;
import com.google.devtools.build.lib.skyframe.PackageFunction.ActionOnIOExceptionReadingBuildFile;
import com.google.devtools.build.lib.skyframe.PackageLookupFunction.CrossRepositoryLabelViolationStrategy;
import com.google.devtools.build.lib.skyframe.rewinding.RewindableGraphInconsistencyReceiver;
import com.google.devtools.build.lib.util.AbruptExitException;
import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.ExitCode;
Expand Down Expand Up @@ -166,6 +167,7 @@ public final class SequencedSkyframeExecutor extends SkyframeExecutor {
private Duration outputTreeDiffCheckingDuration = Duration.ofSeconds(-1L);

private final WorkspaceInfoFromDiffReceiver workspaceInfoFromDiffReceiver;
private GraphInconsistencyReceiver inconsistencyReceiver = GraphInconsistencyReceiver.THROWING;

private SequencedSkyframeExecutor(
Consumer<SkyframeExecutor> skyframeExecutorConsumerOnInit,
Expand Down Expand Up @@ -230,7 +232,7 @@ protected InMemoryMemoizingEvaluator createEvaluator(
skyFunctions,
recordingDiffer,
progressReceiver,
GraphInconsistencyReceiver.THROWING,
inconsistencyReceiver,
eventFilter,
emittedEventState,
trackIncrementalState);
Expand Down Expand Up @@ -271,6 +273,11 @@ public WorkspaceInfoFromDiff sync(
OptionsProvider options)
throws InterruptedException, AbruptExitException {
if (evaluatorNeedsReset) {
// Rewinding is only supported with no incremental state and no action cache.
inconsistencyReceiver =
trackIncrementalState || useActionCache(options)
? GraphInconsistencyReceiver.THROWING
: new RewindableGraphInconsistencyReceiver();
// Recreate MemoizingEvaluator so that graph is recreated with correct edge-clearing status,
// or if the graph doesn't have edges, so that a fresh graph can be used.
resetEvaluator();
Expand All @@ -293,6 +300,11 @@ public WorkspaceInfoFromDiff sync(
return workspaceInfo;
}

private static boolean useActionCache(OptionsProvider options) {
BuildRequestOptions buildRequestOptions = options.getOptions(BuildRequestOptions.class);
return buildRequestOptions != null && buildRequestOptions.useActionCache;
}

/**
* The value types whose builders have direct access to the package locator, rather than accessing
* it via an explicit Skyframe dependency. They need to be invalidated if the package locator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@ filegroup(
visibility = ["//src:__subpackages__"],
)

java_library(
name = "action_rewound_event",
srcs = ["ActionRewoundEvent.java"],
deps = [
"//src/main/java/com/google/devtools/build/lib/actions",
"//src/main/java/com/google/devtools/build/lib/events",
],
)

java_library(
name = "rewinding",
srcs = [
"ActionRewindStrategy.java",
"ActionRewindingStats.java",
"ActionRewoundEvent.java",
],
deps = [
"//src/main/java/com/google/devtools/build/lib/actions",
Expand All @@ -39,3 +47,34 @@ java_library(
"//third_party:jsr305",
],
)

java_library(
name = "rewindable_graph_inconsistency_receiver",
srcs = ["RewindableGraphInconsistencyReceiver.java"],
deps = [
":rewinding_inconsistency_utils",
"//src/main/java/com/google/devtools/build/skyframe",
"//src/main/java/com/google/devtools/build/skyframe:graph_inconsistency_java_proto",
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
"//third_party:flogger",
"//third_party:guava",
"//third_party:jsr305",
],
)

java_library(
name = "rewinding_inconsistency_utils",
srcs = ["RewindingInconsistencyUtils.java"],
deps = [
"//src/main/java/com/google/devtools/build/lib/actions:action_lookup_data",
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",
"//src/main/java/com/google/devtools/build/lib/skyframe:action_template_expansion_value",
"//src/main/java/com/google/devtools/build/lib/skyframe:artifact_nested_set_key",
"//src/main/java/com/google/devtools/build/lib/skyframe:aspect_completion_value",
"//src/main/java/com/google/devtools/build/lib/skyframe:fileset_entry_key",
"//src/main/java/com/google/devtools/build/lib/skyframe:recursive_filesystem_traversal",
"//src/main/java/com/google/devtools/build/lib/skyframe:target_completion_value",
"//src/main/java/com/google/devtools/build/lib/skyframe:test_completion_value",
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright 2022 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.skyframe.rewinding;

import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.common.collect.ImmutableList;
import com.google.common.flogger.GoogleLogger;
import com.google.devtools.build.skyframe.GraphInconsistencyReceiver;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.proto.GraphInconsistency.Inconsistency;
import java.util.Collection;
import java.util.function.Predicate;
import javax.annotation.Nullable;

/**
* {@link GraphInconsistencyReceiver} for evaluations operating on graphs that support rewinding (no
* reverse dependencies, no action cache).
*
* <p>Action rewinding results in various kinds of inconsistencies which this receiver tolerates.
*/
public final class RewindableGraphInconsistencyReceiver implements GraphInconsistencyReceiver {

private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();

private boolean rewindingInitiated = false;

@Override
public void noteInconsistencyAndMaybeThrow(
SkyKey key, @Nullable Collection<SkyKey> otherKeys, Inconsistency inconsistency) {
String childrenAsString =
otherKeys != null ? GraphInconsistencyReceiver.listChildren(otherKeys) : "null";

// RESET_REQUESTED and PARENT_FORCE_REBUILD_OF_CHILD may be the first inconsistencies seen with
// rewinding. BUILDING_PARENT_FOUND_UNDONE_CHILD may also be seen, but it will not be the first.
switch (inconsistency) {
case RESET_REQUESTED:
checkState(
RewindingInconsistencyUtils.isTypeThatDependsOnRewindableNodes(key),
"Unexpected reset requested for: %s",
key);
logger.atInfo().log("Reset requested for: %s", key);
rewindingInitiated = true;
return;

case PARENT_FORCE_REBUILD_OF_CHILD:
boolean parentMayForceRebuildChildren =
RewindingInconsistencyUtils.mayForceRebuildChildren(key);
ImmutableList<SkyKey> unrewindableRebuildChildren =
otherKeys.stream()
.filter(Predicate.not(RewindingInconsistencyUtils::isRewindable))
.collect(toImmutableList());
checkState(
parentMayForceRebuildChildren && unrewindableRebuildChildren.isEmpty(),
"Unexpected force rebuild, parent = %s, children = %s",
key,
parentMayForceRebuildChildren
? GraphInconsistencyReceiver.listChildren(unrewindableRebuildChildren)
: childrenAsString);
logger.atInfo().log(
"Parent force rebuild of children: parent = %s, children = %s", key, childrenAsString);
rewindingInitiated = true;
return;

case BUILDING_PARENT_FOUND_UNDONE_CHILD:
boolean parentDependsOnRewindableNodes =
RewindingInconsistencyUtils.isTypeThatDependsOnRewindableNodes(key);
ImmutableList<SkyKey> unrewindableUndoneChildren =
otherKeys.stream()
.filter(Predicate.not(RewindingInconsistencyUtils::isRewindable))
.collect(toImmutableList());
checkState(
rewindingInitiated
&& parentDependsOnRewindableNodes
&& unrewindableUndoneChildren.isEmpty(),
"Unexpected undone children: parent = %s, children = %s",
key,
rewindingInitiated && parentDependsOnRewindableNodes
? GraphInconsistencyReceiver.listChildren(unrewindableUndoneChildren)
: childrenAsString);
logger.atInfo().log(
"Building parent found undone children: parent = %s, children = %s",
key, childrenAsString);
return;

case PARENT_FORCE_REBUILD_OF_MISSING_CHILD:
case DIRTY_PARENT_HAD_MISSING_CHILD:
case ALREADY_DECLARED_CHILD_MISSING:
throw new IllegalStateException(
String.format(
"Unexpected inconsistency %s, key = %s, otherKeys = %s ",
inconsistency, key, childrenAsString));
default: // Needed because protobuf creates additional enum values.
throw new IllegalStateException(
String.format(
"Unknown inconsistency %s, key = %s, otherKeys = %s ",
inconsistency, key, childrenAsString));
}
}

@Override
public boolean restartPermitted() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2022 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.skyframe.rewinding;

import com.google.devtools.build.lib.actions.ActionLookupData;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.skyframe.ActionTemplateExpansionValue.ActionTemplateExpansionKey;
import com.google.devtools.build.lib.skyframe.ArtifactNestedSetKey;
import com.google.devtools.build.lib.skyframe.AspectCompletionValue.AspectCompletionKey;
import com.google.devtools.build.lib.skyframe.FilesetEntryKey;
import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue;
import com.google.devtools.build.lib.skyframe.TargetCompletionValue.TargetCompletionKey;
import com.google.devtools.build.lib.skyframe.TestCompletionValue.TestCompletionKey;
import com.google.devtools.build.skyframe.SkyKey;

/**
* Centralizes rewinding-related logic used by {@link
* com.google.devtools.build.skyframe.GraphInconsistencyReceiver} policies.
*/
public final class RewindingInconsistencyUtils {

private RewindingInconsistencyUtils() {}

public static boolean mayForceRebuildChildren(SkyKey key) {
return key instanceof ActionLookupData || key instanceof ArtifactNestedSetKey;
}

/** Returns whether the key specifies a node which may be rewound by a failed action. */
public static boolean isRewindable(SkyKey key) {
return key instanceof ActionLookupData
|| key instanceof ArtifactNestedSetKey
|| key instanceof Artifact
|| key instanceof FilesetEntryKey
|| key instanceof RecursiveFilesystemTraversalValue.TraversalRequest;
}

/**
* Returns whether the key specifies a node which depends on nodes which may be rewound.
*
* <p>Such a node may discover, while in-flight, that a dependency of theirs transitioned from
* done to undone.
*/
public static boolean isTypeThatDependsOnRewindableNodes(SkyKey key) {
return key instanceof ActionLookupData
|| key instanceof ArtifactNestedSetKey
|| key instanceof ActionTemplateExpansionKey
|| key instanceof Artifact
|| key instanceof TargetCompletionKey
|| key instanceof TestCompletionKey
|| key instanceof AspectCompletionKey
|| key instanceof RecursiveFilesystemTraversalValue.TraversalRequest
|| key instanceof FilesetEntryKey;
}
}
Loading

0 comments on commit 68ffdd2

Please sign in to comment.