Drain executor of index change requests before closing index

When Gerrit needs to exit because of a SIG-HUP, we need to make
sure to drain all pending threads that have outstanding indexing
operations not yet flushed to disk.

This does not have to be confused with the index close which may
happen during on-line reindexing and needs to preserve the current
indexing executor.

This is a rework of I6ec90eb73 and I7bda13058 with an alternative
approach that keeps both index consistency across restarts and on-line
reindexing.

Bug: Issue 5200
Bug: Issue 4618
Change-Id: Id332ec0215de4777fabef6ca310c510ba90c9760
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/ChangeSubIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/ChangeSubIndex.java
index 6f0df0f..9bec978 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/ChangeSubIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/ChangeSubIndex.java
@@ -97,4 +97,8 @@
     }
     super.add(doc, values);
   }
+
+  @Override
+  public void stop() {
+  }
 }
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneAccountIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneAccountIndex.java
index 78c0185..59980e7 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneAccountIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneAccountIndex.java
@@ -214,4 +214,8 @@
     // to reindex when those change.
     return accountCache.get(id);
   }
+
+  @Override
+  public void stop() {
+  }
 }
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index b5b391e..530566c 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -30,12 +30,11 @@
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.Futures;
-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.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
@@ -96,6 +95,7 @@
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Secondary index implementation using Apache Lucene.
@@ -187,21 +187,18 @@
   }
 
   @Override
+  public void stop() {
+    MoreExecutors.shutdownAndAwaitTermination(
+        executor, Long.MAX_VALUE, TimeUnit.SECONDS);
+  }
+
+  @Override
   public void close() {
-    List<ListenableFuture<?>> closeFutures = Lists.newArrayListWithCapacity(2);
-    closeFutures.add(executor.submit(new Runnable() {
-      @Override
-      public void run() {
-        openIndex.close();
-      }
-    }));
-    closeFutures.add(executor.submit(new Runnable() {
-      @Override
-      public void run() {
-        closedIndex.close();
-      }
-    }));
-    Futures.getUnchecked(Futures.allAsList(closeFutures));
+    try {
+      openIndex.close();
+    } finally {
+      closedIndex.close();
+    }
   }
 
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/Index.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/Index.java
index 533e57c..d12de44 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/Index.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/Index.java
@@ -34,6 +34,9 @@
   /** @return the schema version used by this index. */
   Schema<V> getSchema();
 
+  /** Stop and await termination of all executor threads */
+  void stop();
+
   /** Close this index. */
   void close();
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexCollection.java
index ca0fab2..61c4675 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexCollection.java
@@ -98,6 +98,7 @@
     }
     for (I write : writeIndexes) {
       if (write != read) {
+        write.stop();
         write.close();
       }
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/DummyChangeIndex.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/DummyChangeIndex.java
index ff68106..78c463c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/DummyChangeIndex.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/DummyChangeIndex.java
@@ -57,4 +57,8 @@
   public int getMaxLimit() {
     return Integer.MAX_VALUE;
   }
+
+  @Override
+  public void stop() {
+  }
 }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/index/change/FakeChangeIndex.java b/gerrit-server/src/test/java/com/google/gerrit/server/index/change/FakeChangeIndex.java
index 43039f8..ea13ec4 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/index/change/FakeChangeIndex.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/index/change/FakeChangeIndex.java
@@ -108,4 +108,8 @@
   public void markReady(boolean ready) {
     throw new UnsupportedOperationException();
   }
+
+  @Override
+  public void stop() {
+  }
 }