Resolve target patterns on the fly in SkyQueryEnvironment. Cache only the label sets that are precomputed in the graph.

This is the fourth step in a series to allow processing large sets of targets in query target patterns via streaming batches rather than all at once. This may make SkyQueryEnvironment slower when evaluating queries with repeated target patterns, or many target patterns that would benefit from graph lookups that were batched across all patterns. But that is not currently a bottleneck we're concerned about.

--
MOS_MIGRATED_REVID=111626483
diff --git a/src/main/java/com/google/devtools/build/lib/query2/BlazeQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/BlazeQueryEnvironment.java
index 2f889ae..cde183c 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/BlazeQueryEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/BlazeQueryEnvironment.java
@@ -50,6 +50,7 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
@@ -64,6 +65,7 @@
 public class BlazeQueryEnvironment extends AbstractBlazeQueryEnvironment<Target> {
 
   private static final int MAX_DEPTH_FULL_SCAN_LIMIT = 20;
+  private final Map<String, Set<Target>> resolvedTargetPatterns = new HashMap<>();
   private final TargetPatternEvaluator targetPatternEvaluator;
   private final TransitivePackageLoader transitivePackageLoader;
   private final TargetProvider targetProvider;
@@ -112,6 +114,7 @@
     // errors here.
     eventHandler.resetErrors();
     final AtomicBoolean empty = new AtomicBoolean(true);
+    resolvedTargetPatterns.clear();
     QueryEvalResult queryEvalResult = super.evaluateQuery(expr, new Callback<Target>() {
       @Override
       public void process(Iterable<Target> partialResult)
@@ -414,17 +417,20 @@
       };
 
   @Override
-  protected Map<String, Set<Target>> preloadOrThrow(
-      QueryExpression caller, Collection<String> patterns) throws TargetParsingException {
-    try {
-      // Note that this may throw a RuntimeException if deps are missing in Skyframe and this is
-      // being called from within a SkyFunction.
-      return Maps.transformValues(
-          targetPatternEvaluator.preloadTargetPatterns(eventHandler, patterns, keepGoing),
-          RESOLVED_TARGETS_TO_TARGETS);
-    } catch (InterruptedException e) {
-      // TODO(bazel-team): Propagate the InterruptedException from here [skyframe-loading].
-      throw new TargetParsingException("interrupted");
+  protected void preloadOrThrow(QueryExpression caller, Collection<String> patterns)
+      throws TargetParsingException {
+    if (!resolvedTargetPatterns.keySet().containsAll(patterns)) {
+      try {
+        // Note that this may throw a RuntimeException if deps are missing in Skyframe and this is
+        // being called from within a SkyFunction.
+        resolvedTargetPatterns.putAll(
+            Maps.transformValues(
+                targetPatternEvaluator.preloadTargetPatterns(eventHandler, patterns, keepGoing),
+                RESOLVED_TARGETS_TO_TARGETS));
+      } catch (InterruptedException e) {
+        // TODO(bazel-team): Propagate the InterruptedException from here [skyframe-loading].
+        throw new TargetParsingException("interrupted");
+      }
     }
   }