Listen to OnlineUpgradeListener.onSuccess event

Issue:
During the Gerrit upgrade sometimes there is also an index upgrade
therefore Gerrit starts with the old index version set as ready and
issues an on-line reindex. It finally switches to the new version and
marks it as ready (marking the previous one as not-ready).

Solution:
Upon the Gerrit start detect the index version as it was before (IOW
monitor the old version's lock files). But in addition listen to the
`OnlineUpgradeListener.onSuccess` event and in case when `changes`
on-line reindex gets finished and its version changed switch to monitor
the new changes index version lock files.

The following scenarios were tested:
* gerrit 3.8.3 site was initiated and ~200 changes were created
* gerrit 3.9.1 init was performed and healthcheck plugin was dropped to
  plugins
* gerrit 3.9.1 was started and `healthcheck~status` endpoint was called
  a few times while reindex of changes was performed - all `passed`
* switch to newer changes index version locks was confirmed with the
  following log entry:

    QueryChangesHealthCheck : Changes index healthcheck switched from index version 82 to 84

  but also by removing `write.lock` from old version (healtcheck passed)
  and from new version (healtheck failed) as expected
* calling manual reindex on the same version was ignored by healtcheck
  - again as expected

Bug: Issue 40015289
Change-Id: I6c2f8feebadafeb89f4ac8c17bce4b96ab8a5685
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
index d88c445..bff5c13 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
@@ -16,6 +16,7 @@
 
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.server.index.OnlineUpgradeListener;
 import com.googlesource.gerrit.plugins.healthcheck.check.ActiveWorkersCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.AuthHealthCheck;
 import com.googlesource.gerrit.plugins.healthcheck.check.BlockedThreadsCheck;
@@ -39,6 +40,8 @@
     bindChecker(BlockedThreadsCheck.class);
     bindChecker(ChangesIndexHealthCheck.class);
 
+    DynamicSet.bind(binder(), OnlineUpgradeListener.class).to(ChangesIndexHealthCheck.class);
+
     install(BlockedThreadsCheck.SUB_CHECKS);
   }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ChangesIndexHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ChangesIndexHealthCheck.java
index 176cea9..bd16b7b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ChangesIndexHealthCheck.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ChangesIndexHealthCheck.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.index.OnlineUpgradeListener;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig;
@@ -28,6 +29,7 @@
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
@@ -36,12 +38,13 @@
 import org.slf4j.LoggerFactory;
 
 @Singleton
-public class ChangesIndexHealthCheck extends AbstractHealthCheck {
+public class ChangesIndexHealthCheck extends AbstractHealthCheck implements OnlineUpgradeListener {
   private static final Logger log = LoggerFactory.getLogger(QueryChangesHealthCheck.class);
   private static final String lockFilename = "write.lock";
 
+  private final SitePaths sitePaths;
   private final boolean isLuceneIndex;
-  private final Optional<ChangesIndexLockFiles> changes;
+  private final AtomicReference<Optional<ChangesIndexLockFiles>> changes;
 
   @Inject
   ChangesIndexHealthCheck(
@@ -51,17 +54,46 @@
       MetricMaker metricMaker,
       SitePaths sitePaths) {
     super(executor, config, CHANGES_INDEX, metricMaker);
+    this.sitePaths = sitePaths;
     this.isLuceneIndex = isIndexTypeLucene(cfg);
-    this.changes = getChangesIndexLockFiles(sitePaths.index_dir);
+    this.changes = new AtomicReference<>(getChangesIndexLockFiles(sitePaths.index_dir));
   }
 
   @Override
   protected Result doCheck() throws Exception {
     return isLuceneIndex
-        ? changes.map(locks -> locks.valid() ? Result.PASSED : Result.FAILED).orElse(Result.FAILED)
+        ? changes
+            .get()
+            .map(locks -> locks.valid() ? Result.PASSED : Result.FAILED)
+            .orElse(Result.FAILED)
         : Result.DISABLED;
   }
 
+  @Override
+  public void onStart(String name, int oldVersion, int newVersion) {}
+
+  @Override
+  public void onSuccess(String name, int oldVersion, int newVersion) {
+    if (!name.equals("changes") || oldVersion == newVersion) {
+      return;
+    }
+
+    Optional<ChangesIndexLockFiles> newLockFiles =
+        Optional.of(
+            getChangesLockFiles(sitePaths.index_dir, String.format("changes_%04d", newVersion)));
+    if (!changes.compareAndSet(changes.get(), newLockFiles)) {
+      log.info(
+          "New version {} of changes index healthcheck lock files was set already by another thread",
+          newVersion);
+    } else {
+      log.info(
+          "Changes index healthcheck switched from index version {} to {}", oldVersion, newVersion);
+    }
+  }
+
+  @Override
+  public void onFailure(String name, int oldVersion, int newVersion) {}
+
   private static boolean isIndexTypeLucene(Config cfg) {
     IndexType indexType = new IndexType(cfg.getString("index", null, "type"));
     boolean isLucene = indexType.isLucene();
@@ -73,7 +105,7 @@
     return isLucene;
   }
 
-  private static Optional<ChangesIndexLockFiles> getChangesIndexLockFiles(Path indexDir) {
+  private Optional<ChangesIndexLockFiles> getChangesIndexLockFiles(Path indexDir) {
     FileBasedConfig cfg =
         new FileBasedConfig(indexDir.resolve("gerrit_index.config").toFile(), FS.detect());
     try {
@@ -96,7 +128,7 @@
         .findAny();
   }
 
-  private static ChangesIndexLockFiles getChangesLockFiles(Path indexDir, String indexVersion) {
+  private ChangesIndexLockFiles getChangesLockFiles(Path indexDir, String indexVersion) {
     Path versionDir = indexDir.resolve(indexVersion);
     return new ChangesIndexLockFiles(
         versionDir.resolve("open").resolve(lockFilename).toFile(),