Skip to content

Commit 24dab1f

Browse files
aoeuicopybara-github
authored andcommitted
Update counters in FileDependencySerializer's NodeDependencyHandler on error.
This nodesWaitingForDeps counter is incremented during setup, but there are a couple of cases where we are missing a decrement. * If any dependency fails. Uses QuiescingFutureTask's doneWithError to decrement in this case. * NodeDependencyHandler.call has a couple of short-circuit branches that don't decrement correctly. Adds the appropriate decrements for these. PiperOrigin-RevId: 980735799 Change-Id: Ia5ee7ee7c5753d0857c01b807347092c24344c1a
1 parent aac8677 commit 24dab1f

4 files changed

Lines changed: 263 additions & 72 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ java_library(
232232
"//src/main/java/com/google/devtools/build/lib/actions:file_metadata",
233233
"//src/main/java/com/google/devtools/build/lib/analysis:analysis_cluster",
234234
"//src/main/java/com/google/devtools/build/lib/compress:compression_service",
235+
"//src/main/java/com/google/devtools/build/lib/concurrent",
235236
"//src/main/java/com/google/devtools/build/lib/concurrent/safeexecutor:safe_executor",
236237
"//src/main/java/com/google/devtools/build/lib/profiler",
237238
"//src/main/java/com/google/devtools/build/lib/profiler:counter_series_task_impl",

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

Lines changed: 90 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.google.devtools.build.lib.actions.FileValue;
4545
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.BundledFileSystem;
4646
import com.google.devtools.build.lib.compress.CompressionService;
47+
import com.google.devtools.build.lib.concurrent.QuiescingFutureTask;
4748
import com.google.devtools.build.lib.concurrent.safeexecutor.SafeExecutor;
4849
import com.google.devtools.build.lib.concurrent.safeexecutor.SafeFutures;
4950
import com.google.devtools.build.lib.profiler.CounterSeriesCollector;
@@ -88,6 +89,7 @@
8889
import com.google.devtools.build.lib.vfs.RootedPath;
8990
import com.google.devtools.build.skyframe.InMemoryGraph;
9091
import com.google.devtools.build.skyframe.InMemoryNodeEntry;
92+
import com.google.errorprone.annotations.DoNotCall;
9193
import com.google.protobuf.CodedOutputStream;
9294
import java.io.ByteArrayOutputStream;
9395
import java.io.IOException;
@@ -793,50 +795,9 @@ public ListenableFuture<ListingDataInfo> apply(FileDataInfo info) {
793795
NodeDataInfoOrFuture populateFutureNodeDataInfo(FutureNodeDataInfo future) {
794796
counters.nodesWaitingForDeps.incrementAndGet();
795797

796-
AbstractNestedFileOpNodes node = future.key();
797-
var dependencyHandler = new NodeDependencyHandler();
798-
799-
// Loops through all node dependencies, registering them with the dependencyHandler. The
800-
// dependencyHandler triggers recursive registration, keeping track of immediate results and
801-
// any futures.
802-
for (int i = 0; i < node.analysisDependenciesCount(); i++) {
803-
switch (node.getAnalysisDependency(i)) {
804-
case FileKey fileKey:
805-
dependencyHandler.addFileKey(fileKey);
806-
break;
807-
case DirectoryListingKey listingKey:
808-
dependencyHandler.addListingKey(listingKey);
809-
break;
810-
case AbstractNestedFileOpNodes nestedKeys:
811-
dependencyHandler.addNodeKey(nestedKeys);
812-
break;
813-
case RemoteFileOpNode remoteNode:
814-
dependencyHandler.addRemoteNode(remoteNode);
815-
break;
816-
}
817-
}
818-
819-
switch (node) {
820-
case NestedFileOpNodes plainNodes:
821-
break;
822-
case NestedFileOpNodesWithSource withSource:
823-
dependencyHandler.setSourceFile(withSource.source());
824-
break;
825-
}
826-
827-
var allFutures = dependencyHandler.getCombinedFutures();
828-
if (allFutures.isEmpty()) {
829-
NodeDataInfo result;
830-
try {
831-
result = dependencyHandler.call();
832-
} catch (ExecutionException | IOException e) {
833-
// Only thrown when calling Future.get, but none should be present if this is reached.
834-
throw new IllegalStateException("unexpected failure", e);
835-
}
836-
return future.completeWith(result);
837-
}
838-
return future.completeWith(
839-
SafeFutures.call(Futures.whenAllSucceed(allFutures), dependencyHandler, executor));
798+
var task = new NodeDependencyHandler(future.key());
799+
task.run();
800+
return future.completeWith(task);
840801
}
841802

842803
OutputStream getCompressedOutputStream(OutputStream outputStream) throws IOException {
@@ -849,12 +810,15 @@ OutputStream getCompressedOutputStream(OutputStream outputStream) throws IOExcep
849810

850811
/**
851812
* Accepts all the dependencies associated with a node, registers their serialization and waits
852-
* for processing to complete, signalled through the {@link #call} callback.
813+
* for processing to complete.
853814
*
854815
* <p>Once processing is complete and all keys are known, uploads the node value. {@link
855816
* #computeNodeBytes} defines the wire format of nodes.
856817
*/
857-
class NodeDependencyHandler implements Callable<NodeDataInfo> {
818+
private final class NodeDependencyHandler extends QuiescingFutureTask<NodeDataInfo>
819+
implements FutureCallback<Object> {
820+
private final AbstractNestedFileOpNodes node;
821+
858822
private final ArrayList<String> fileKeys = new ArrayList<>();
859823
private final ArrayList<String> listingKeys = new ArrayList<>();
860824
private final ArrayList<NodeInvalidationDataInfo> nodeDependencies = new ArrayList<>();
@@ -866,21 +830,54 @@ class NodeDependencyHandler implements Callable<NodeDataInfo> {
866830
private final ArrayList<FutureListingDataInfo> futureListingDataInfo = new ArrayList<>();
867831
private final ArrayList<FutureNodeDataInfo> futureNodeDataInfo = new ArrayList<>();
868832

833+
private NodeDependencyHandler(AbstractNestedFileOpNodes node) {
834+
super(executor);
835+
this.node = node;
836+
}
837+
869838
@Override
870-
public NodeDataInfo call() throws ExecutionException, IOException {
871-
for (FutureFileDataInfo futureInfo : futureFileDataInfo) {
872-
addFileInfo(Futures.getDone(futureInfo));
839+
protected void arrangeSubtasks() {
840+
// Loops through all node dependencies, registering them with this handler. This triggers
841+
// recursive registration, keeping track of immediate results and any futures.
842+
for (int i = 0; i < node.analysisDependenciesCount(); i++) {
843+
switch (node.getAnalysisDependency(i)) {
844+
case FileKey fileKey -> addFileKey(fileKey);
845+
case DirectoryListingKey listingKey -> addListingKey(listingKey);
846+
case AbstractNestedFileOpNodes nestedKeys -> addNodeKey(nestedKeys);
847+
case RemoteFileOpNode remoteNode -> addRemoteNode(remoteNode);
848+
}
873849
}
874-
for (FutureListingDataInfo futureInfo : futureListingDataInfo) {
875-
addListingInfo(Futures.getDone(futureInfo));
850+
851+
switch (node) {
852+
case NestedFileOpNodes plainNodes -> {}
853+
case NestedFileOpNodesWithSource withSource -> setSourceFile(withSource.source());
876854
}
877-
for (FutureNodeDataInfo futureInfo : futureNodeDataInfo) {
878-
addNodeInfo(Futures.getDone(futureInfo));
855+
}
856+
857+
@Override
858+
protected NodeDataInfo getValue() {
859+
@Nullable String sourceFileKey;
860+
try {
861+
for (FutureFileDataInfo futureInfo : futureFileDataInfo) {
862+
addFileInfo(Futures.getDone(futureInfo));
863+
}
864+
for (FutureListingDataInfo futureInfo : futureListingDataInfo) {
865+
addListingInfo(Futures.getDone(futureInfo));
866+
}
867+
for (FutureNodeDataInfo futureInfo : futureNodeDataInfo) {
868+
addNodeInfo(Futures.getDone(futureInfo));
869+
}
870+
sourceFileKey = getSourceFileKey();
871+
} catch (ExecutionException e) {
872+
// The QuiescingFutureTask setup guarantees that ExecutionException will not be thrown if
873+
// getValue is called.
874+
throw new AssertionError("unexpected failure", e);
879875
}
880-
@Nullable String sourceFileKey = getSourceFileKey();
881876

882877
if (fileKeys.isEmpty() && listingKeys.isEmpty() && sourceFileKey == null) {
878+
// TODO(b/558805781): investigate whether this is always dead code in practice
883879
if (nodeDependencies.isEmpty()) {
880+
counters.nodesWaitingForDeps.decrementAndGet();
884881
return CONSTANT_NODE; // None of the dependencies are relevant to invalidation.
885882
}
886883
// There are multiple ways that result could become unary here, even if `node` always has at
@@ -893,6 +890,7 @@ public NodeDataInfo call() throws ExecutionException, IOException {
893890
//
894891
// TODO: b/364831651 - consider additional special casing for unary file or listing
895892
// dependencies.
893+
counters.nodesWaitingForDeps.decrementAndGet();
896894
return nodeDependencies.get(0);
897895
}
898896
}
@@ -956,6 +954,13 @@ public NodeDataInfo call() throws ExecutionException, IOException {
956954
return new NodeInvalidationDataInfo(key, writeStatusBuilder.build());
957955
}
958956

957+
@Override
958+
protected void doneWithError(
959+
@Nullable Throwable primaryCause, ImmutableList<Throwable> secondaryCauses) {
960+
counters.nodesWaitingForDeps.decrementAndGet();
961+
counters.nodesWithProcessingErrors.incrementAndGet();
962+
}
963+
959964
private void addRemoteNode(RemoteFileOpNode remoteNode) {
960965
addNodeInfo(registerDependency(remoteNode));
961966
}
@@ -967,6 +972,7 @@ private void addFileKey(FileKey fileKey) {
967972
break;
968973
case FutureFileDataInfo futureInfo:
969974
futureFileDataInfo.add(futureInfo);
975+
trackFuture(futureInfo);
970976
break;
971977
}
972978
}
@@ -989,6 +995,7 @@ private void addListingKey(DirectoryListingKey listingKey) {
989995
break;
990996
case FutureListingDataInfo futureInfo:
991997
futureListingDataInfo.add(futureInfo);
998+
trackFuture(futureInfo);
992999
break;
9931000
}
9941001
}
@@ -1011,6 +1018,7 @@ private void addNodeKey(AbstractNestedFileOpNodes nestedKeys) {
10111018
break;
10121019
case FutureNodeDataInfo futureInfo:
10131020
futureNodeDataInfo.add(futureInfo);
1021+
trackFuture(futureInfo);
10141022
break;
10151023
}
10161024
}
@@ -1033,28 +1041,39 @@ private void setSourceFile(FileKey sourceFile) {
10331041
sourceFile,
10341042
sourceFileOrFuture);
10351043
this.sourceFileOrFuture = registerDependency(sourceFile);
1036-
}
1037-
1038-
private ImmutableList<ListenableFuture<?>> getCombinedFutures() {
1039-
var combined =
1040-
ImmutableList.<ListenableFuture<?>>builder()
1041-
.addAll(futureFileDataInfo)
1042-
.addAll(futureListingDataInfo)
1043-
.addAll(futureNodeDataInfo);
10441044
switch (sourceFileOrFuture) {
1045-
case null -> {}
1046-
case FileDataInfo unusedSource -> {}
1047-
case FutureFileDataInfo futureSource -> combined.add(futureSource);
1045+
case FileDataInfo immediateSource -> {}
1046+
case FutureFileDataInfo futureSource -> trackFuture(futureSource);
10481047
}
1049-
return combined.build();
10501048
}
10511049

1052-
private byte[] compressBytes(byte[] nodeBytes) throws IOException {
1050+
private void trackFuture(ListenableFuture<?> future) {
1051+
increment();
1052+
Futures.addCallback(future, this, directExecutor());
1053+
}
1054+
1055+
@Override
1056+
@DoNotCall("Only called via trackFuture")
1057+
public void onSuccess(Object ignored) {
1058+
decrement();
1059+
}
1060+
1061+
@Override
1062+
@DoNotCall("Only called via trackFuture")
1063+
public void onFailure(Throwable t) {
1064+
notifyException(t);
1065+
}
1066+
1067+
private byte[] compressBytes(byte[] nodeBytes) {
10531068
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
1054-
MagicBytes.writeMagicBytes(outputStream);
1055-
try (OutputStream compressedBytesStream =
1056-
compressionService.newZstdOutputStream(outputStream)) {
1057-
compressedBytesStream.write(nodeBytes);
1069+
try {
1070+
MagicBytes.writeMagicBytes(outputStream);
1071+
try (OutputStream compressedBytesStream =
1072+
compressionService.newZstdOutputStream(outputStream)) {
1073+
compressedBytesStream.write(nodeBytes);
1074+
}
1075+
} catch (IOException e) {
1076+
throw new AssertionError("Unexpected IOException during in-memory compression", e);
10581077
}
10591078
return outputStream.toByteArray();
10601079
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ java_test(
244244
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
245245
"//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs",
246246
"//src/main/java/com/google/devtools/build/skyframe",
247+
"//third_party/java/guava:collect",
247248
"//third_party/java/junit",
248249
"//third_party/java/mockito",
249250
"//third_party/java/truth",

0 commit comments

Comments
 (0)