Merge "Do not await async ChangeIndex updates in Batch Update, subject to experiment"
diff --git a/java/com/google/gerrit/server/experiments/ExperimentFeaturesConstants.java b/java/com/google/gerrit/server/experiments/ExperimentFeaturesConstants.java
index 32ec401..e294d55 100644
--- a/java/com/google/gerrit/server/experiments/ExperimentFeaturesConstants.java
+++ b/java/com/google/gerrit/server/experiments/ExperimentFeaturesConstants.java
@@ -25,4 +25,8 @@
 
   /** Features, enabled by default in the current release. */
   public static final ImmutableSet<String> DEFAULT_ENABLED_FEATURES = ImmutableSet.of();
+
+  /** On BatchUpdate, do not await index completion before returning to the user */
+  public static String GERRIT_BACKEND_FEATURE_DO_NOT_AWAIT_CHANGE_INDEXING =
+      "GerritBackendFeature__do_not_await_change_indexing";
 }
diff --git a/java/com/google/gerrit/server/update/BatchUpdate.java b/java/com/google/gerrit/server/update/BatchUpdate.java
index 3f5fa97..9250513 100644
--- a/java/com/google/gerrit/server/update/BatchUpdate.java
+++ b/java/com/google/gerrit/server/update/BatchUpdate.java
@@ -58,6 +58,8 @@
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.change.NotifyResolver;
+import com.google.gerrit.server.experiments.ExperimentFeatures;
+import com.google.gerrit.server.experiments.ExperimentFeaturesConstants;
 import com.google.gerrit.server.extensions.events.AttentionSetObserver;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -423,6 +425,7 @@
   private final Map<Change.Id, Change> newChanges = new HashMap<>();
   private final List<OpData<RepoOnlyOp>> repoOnlyOps = new ArrayList<>();
   private final Map<Change.Id, NotifyHandling> perChangeNotifyHandling = new HashMap<>();
+  private final ExperimentFeatures experimentFeatures;
 
   private RepoView repoView;
   private BatchRefUpdate batchRefUpdate;
@@ -449,6 +452,7 @@
       GitReferenceUpdated gitRefUpdated,
       RefLogIdentityProvider refLogIdentityProvider,
       AttentionSetObserver attentionSetObserver,
+      ExperimentFeatures experimentFeatures,
       @Assisted Project.NameKey project,
       @Assisted CurrentUser user,
       @Assisted Instant when) {
@@ -462,6 +466,7 @@
     this.gitRefUpdated = gitRefUpdated;
     this.refLogIdentityProvider = refLogIdentityProvider;
     this.attentionSetObserver = attentionSetObserver;
+    this.experimentFeatures = experimentFeatures;
     this.project = project;
     this.user = user;
     this.when = when;
@@ -661,6 +666,11 @@
     }
   }
 
+  private boolean indexAsync() {
+    return experimentFeatures.isFeatureEnabled(
+        ExperimentFeaturesConstants.GERRIT_BACKEND_FEATURE_DO_NOT_AWAIT_CHANGE_INDEXING);
+  }
+
   private void fireRefChangeEvent() {
     if (batchRefUpdate != null) {
       gitRefUpdated.fire(project, batchRefUpdate, getAccount().orElse(null));
@@ -683,11 +693,13 @@
     private final NoteDbUpdateManager manager;
     private final boolean dryrun;
     private final Map<Change.Id, ChangeResult> results;
+    private final boolean indexAsync;
 
-    ChangesHandle(NoteDbUpdateManager manager, boolean dryrun) {
+    ChangesHandle(NoteDbUpdateManager manager, boolean dryrun, boolean indexAsync) {
       this.manager = manager;
       this.dryrun = dryrun;
       results = new HashMap<>();
+      this.indexAsync = indexAsync;
     }
 
     @Override
@@ -738,6 +750,17 @@
             throw new IllegalStateException("unexpected result: " + e.getValue());
         }
       }
+      if (indexAsync) {
+        // We want to index asynchronously. However, the callers will await all
+        // index futures. This allows us to - even in synchronous case -
+        // parallelize indexing changes.
+        // Returning immediate futures for newly-created change data objects
+        // while letting the actual futures go will make actual indexing
+        // asynchronous.
+        return results.keySet().stream()
+            .map(cId -> Futures.immediateFuture(changeDataFactory.create(project, cId)))
+            .collect(toImmutableList());
+      }
       return indexFutures.build();
     }
   }
@@ -759,7 +782,8 @@
                 .setBatchUpdateListeners(batchUpdateListeners)
                 .setChangeRepo(
                     repo, repoView.getRevWalk(), repoView.getInserter(), repoView.getCommands()),
-            dryrun);
+            dryrun,
+            indexAsync());
     getRefLogIdent().ifPresent(handle.manager::setRefLogIdent);
     handle.manager.setRefLogMessage(refLogMessage);
     handle.manager.setPushCertificate(pushCert);