Only recheck mergeable on reindex with a flag

The mergeability computation is expensive and should be redundant
after it's been run once. Protect this behavior with a flag that
defaults to false, and instruct users to run reindex with the flag in
the release notes.

Change-Id: I3c6140b9a727165b8b43712a0d162c530db048a6
diff --git a/ReleaseNotes/ReleaseNotes-2.9.txt b/ReleaseNotes/ReleaseNotes-2.9.txt
index c0e2fcb..fc764a4 100644
--- a/ReleaseNotes/ReleaseNotes-2.9.txt
+++ b/ReleaseNotes/ReleaseNotes-2.9.txt
@@ -19,6 +19,7 @@
 *WARNING:* This release contains schema changes.  To upgrade:
 ----
   java -jar gerrit.war init -d site_path
+  java -jar gerrit.war reindex --recheck-mergeability -d site_path
 ----
 
 *WARNING:* Upgrading to 2.9.x requires the server be first upgraded to 2.1.7 (or
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
index 723f08d..ecb5371 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Reindex.java
@@ -47,6 +47,7 @@
 import com.google.gerrit.server.cache.CacheRemovalListener;
 import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
 import com.google.gerrit.server.change.ChangeKindCache;
+import com.google.gerrit.server.change.MergeabilityChecker;
 import com.google.gerrit.server.change.MergeabilityChecksExecutor;
 import com.google.gerrit.server.change.PatchSetInserter;
 import com.google.gerrit.server.config.CanonicalWebUrl;
@@ -116,6 +117,9 @@
   @Option(name = "--output", usage = "Prefix for output; path for local disk index, or prefix for remote index")
   private String outputBase;
 
+  @Option(name = "--recheck-mergeable", usage = "Recheck mergeable flag on all changes")
+  private boolean recheckMergeable;
+
   @Option(name = "--verbose", usage = "Output debug information for each change")
   private boolean verbose;
 
@@ -195,58 +199,12 @@
         install(new DefaultCacheFactory.Module());
         factory(ChangeData.Factory.class);
 
-        factory(ProjectState.Factory.class);
-        bind(new TypeLiteral<List<CommentLinkInfo>>() {})
-            .toProvider(CommentLinkProvider.class).in(SINGLETON);
-        bind(IdentifiedUser.class).toProvider(Providers.<IdentifiedUser>of(null));
-        bind(CurrentUser.class).to(IdentifiedUser.class);
-        bind(String.class).annotatedWith(CanonicalWebUrl.class)
-            .toProvider(CanonicalWebUrlProvider.class);
-
-        factory(IncludingGroupMembership.Factory.class);
-        bind(GroupBackend.class).to(UniversalGroupBackend.class).in(SINGLETON);
-        DynamicSet.setOf(binder(), GroupBackend.class);
-        bind(InternalGroupBackend.class).in(SINGLETON);
-        DynamicSet.bind(binder(), GroupBackend.class).to(SystemGroupBackend.class);
-        DynamicSet.bind(binder(), GroupBackend.class).to(InternalGroupBackend.class);
-        factory(InternalUser.Factory.class);
-
-        factory(PatchSetInserter.Factory.class);
-        bind(ChangeHooks.class).to(DisabledChangeHooks.class);
-        bind(ReplacePatchSetSender.Factory.class).toProvider(
-            Providers.<ReplacePatchSetSender.Factory>of(null));
-
-        factory(CapabilityControl.Factory.class);
-        factory(MergeUtil.Factory.class);
-        DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class);
-        DynamicSet.setOf(binder(), CommitValidationListener.class);
-        factory(CommitValidators.Factory.class);
-      }
-    });
-
-    modules.add(AccountCacheImpl.module());
-    modules.add(AccountByEmailCacheImpl.module());
-    modules.add(ChangeKindCache.module());
-    modules.add(GroupCacheImpl.module());
-    modules.add(GroupIncludeCacheImpl.module());
-    modules.add(ProjectCacheImpl.module());
-    modules.add(SectionSortCache.module());
-
-    modules.add(new AccessControlModule());
-    modules.add(new GitModule());
-    modules.add(new NoteDbModule());
-    modules.add(new PrologModule());
-    modules.add(new AbstractModule() {
-      @Override
-      protected void configure() {
-      }
-
-      @Provides
-      @Singleton
-      @MergeabilityChecksExecutor
-      public WorkQueue.Executor createMergeabilityChecksExecutor(
-          WorkQueue queues) {
-        return queues.createQueue(1, "MergeabilityChecks");
+        if (recheckMergeable) {
+          install(new MergeabilityModule());
+        } else {
+          bind(MergeabilityChecker.class)
+              .toProvider(Providers.<MergeabilityChecker> of(null));
+        }
       }
     });
 
@@ -294,6 +252,59 @@
     }
   }
 
+  private static class MergeabilityModule extends FactoryModule {
+    @Override
+    public void configure() {
+      factory(ProjectState.Factory.class);
+      bind(new TypeLiteral<List<CommentLinkInfo>>() {})
+          .toProvider(CommentLinkProvider.class).in(SINGLETON);
+      bind(IdentifiedUser.class).toProvider(Providers.<IdentifiedUser>of(null));
+      bind(CurrentUser.class).to(IdentifiedUser.class);
+      bind(String.class).annotatedWith(CanonicalWebUrl.class)
+          .toProvider(CanonicalWebUrlProvider.class);
+
+      factory(IncludingGroupMembership.Factory.class);
+      bind(GroupBackend.class).to(UniversalGroupBackend.class).in(SINGLETON);
+      DynamicSet.setOf(binder(), GroupBackend.class);
+      bind(InternalGroupBackend.class).in(SINGLETON);
+      DynamicSet.bind(binder(), GroupBackend.class).to(SystemGroupBackend.class);
+      DynamicSet.bind(binder(), GroupBackend.class).to(InternalGroupBackend.class);
+      factory(InternalUser.Factory.class);
+
+      factory(PatchSetInserter.Factory.class);
+      bind(ChangeHooks.class).to(DisabledChangeHooks.class);
+      bind(ReplacePatchSetSender.Factory.class).toProvider(
+          Providers.<ReplacePatchSetSender.Factory>of(null));
+
+      factory(CapabilityControl.Factory.class);
+      factory(MergeUtil.Factory.class);
+      DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class);
+      DynamicSet.setOf(binder(), CommitValidationListener.class);
+      factory(CommitValidators.Factory.class);
+
+      install(AccountCacheImpl.module());
+      install(AccountByEmailCacheImpl.module());
+      install(ChangeKindCache.module());
+      install(GroupCacheImpl.module());
+      install(GroupIncludeCacheImpl.module());
+      install(ProjectCacheImpl.module());
+      install(SectionSortCache.module());
+
+      install(new AccessControlModule());
+      install(new GitModule());
+      install(new NoteDbModule());
+      install(new PrologModule());
+    }
+
+    @Provides
+    @Singleton
+    @MergeabilityChecksExecutor
+    public WorkQueue.Executor createMergeabilityChecksExecutor(
+        WorkQueue queues) {
+      return queues.createQueue(1, "MergeabilityChecks");
+    }
+  }
+
   private int indexAll() throws Exception {
     ReviewDb db = sysInjector.getInstance(ReviewDb.class);
     ProgressMonitor pm = new TextProgressMonitor();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java
index 0334715..a1075fa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeBatchIndexer.java
@@ -27,6 +27,7 @@
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
+import com.google.gerrit.common.Nullable;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -118,7 +119,7 @@
       GitRepositoryManager repoManager,
       @IndexExecutor ListeningExecutorService executor,
       ChangeIndexer.Factory indexerFactory,
-      MergeabilityChecker mergeabilityChecker) {
+      @Nullable MergeabilityChecker mergeabilityChecker) {
     this.schemaFactory = schemaFactory;
     this.changeDataFactory = changeDataFactory;
     this.repoManager = repoManager;
@@ -149,10 +150,7 @@
     final AtomicBoolean ok = new AtomicBoolean(true);
 
     for (final Project.NameKey project : projects) {
-      try {
-        mergeabilityChecker.update(project);
-      } catch (IOException e) {
-        log.error("Error in mergeability checker", e);
+      if (!updateMergeable(project)) {
         ok.set(false);
       }
       final ListenableFuture<?> future = executor.submit(reindexProject(
@@ -210,6 +208,18 @@
     return new Result(sw, ok.get(), doneTask.getCount(), failedTask.getCount());
   }
 
+  private boolean updateMergeable(Project.NameKey project) {
+    if (mergeabilityChecker != null) {
+      try {
+        mergeabilityChecker.update(project);
+      } catch (IOException e) {
+        log.error("Error in mergeability checker", e);
+        return false;
+      }
+    }
+    return true;
+  }
+
   private Callable<Void> reindexProject(final ChangeIndexer indexer,
       final Project.NameKey project, final Task done, final Task failed,
       final PrintWriter verboseWriter) {