Merge "Speed up reviewer suggestion"
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
index 7b4777e..08f879f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
@@ -53,7 +53,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -65,6 +64,7 @@
     final Timer0 recommendAccountsLatency;
     final Timer0 loadAccountsLatency;
     final Timer0 queryGroupsLatency;
+    final Timer0 filterVisibility;
 
     @Inject
     Metrics(MetricMaker metricMaker) {
@@ -92,12 +92,18 @@
               new Description("Latency for querying groups for reviewer suggestion")
                   .setCumulative()
                   .setUnit(Units.MILLISECONDS));
+      filterVisibility =
+          metricMaker.newTimer(
+              "reviewer_suggestion/filter_visibility",
+              new Description("Latency for removing users that can't see the change")
+                  .setCumulative()
+                  .setUnit(Units.MILLISECONDS));
     }
   }
 
   // Generate a candidate list at 3x the size of what the user wants to see to
   // give the ranking algorithm a good set of candidates it can work with
-  private static final int CANDIDATE_LIST_MULTIPLIER = 3;
+  private static final int CANDIDATE_LIST_MULTIPLIER = 2;
 
   private final AccountLoader accountLoader;
   private final AccountQueryBuilder accountQueryBuilder;
@@ -150,13 +156,26 @@
 
     List<Account.Id> candidateList = new ArrayList<>();
     if (!Strings.isNullOrEmpty(query)) {
-      candidateList = suggestAccounts(suggestReviewers, visibilityControl);
+      candidateList = suggestAccounts(suggestReviewers);
     }
 
     List<Account.Id> sortedRecommendations =
         recommendAccounts(changeNotes, suggestReviewers, projectControl, candidateList);
-    List<SuggestedReviewerInfo> suggestedReviewer = loadAccounts(sortedRecommendations);
 
+    // Filter accounts by visibility and enforce limit
+    List<Account.Id> filteredRecommendations = new ArrayList<>();
+    try (Timer0.Context ctx = metrics.filterVisibility.start()) {
+      for (Account.Id reviewer : sortedRecommendations) {
+        if (filteredRecommendations.size() >= limit) {
+          break;
+        }
+        if (visibilityControl.isVisibleTo(reviewer)) {
+          filteredRecommendations.add(reviewer);
+        }
+      }
+    }
+
+    List<SuggestedReviewerInfo> suggestedReviewer = loadAccounts(filteredRecommendations);
     if (!excludeGroups && suggestedReviewer.size() < limit && !Strings.isNullOrEmpty(query)) {
       // Add groups at the end as individual accounts are usually more
       // important.
@@ -174,22 +193,14 @@
     return suggestedReviewer.subList(0, limit);
   }
 
-  private List<Account.Id> suggestAccounts(
-      SuggestReviewers suggestReviewers, VisibilityControl visibilityControl) throws OrmException {
+  private List<Account.Id> suggestAccounts(SuggestReviewers suggestReviewers) throws OrmException {
     try (Timer0.Context ctx = metrics.queryAccountsLatency.start()) {
       try {
-        Set<Account.Id> matches = new HashSet<>();
         QueryResult<AccountState> result =
             accountQueryProcessor
                 .setLimit(suggestReviewers.getLimit() * CANDIDATE_LIST_MULTIPLIER)
                 .query(accountQueryBuilder.defaultQuery(suggestReviewers.getQuery()));
-        for (AccountState accountState : result.entities()) {
-          Account.Id id = accountState.getAccount().getId();
-          if (visibilityControl.isVisibleTo(id)) {
-            matches.add(id);
-          }
-        }
-        return new ArrayList<>(matches);
+        return result.entities().stream().map(a -> a.getAccount().getId()).collect(toList());
       } catch (QueryParseException e) {
         return ImmutableList.of();
       }