Improve readability of the refs filtering during replication

Refactor the filtering of refs during replication so that becomes clearer
to read and less prone to bugs in the future.

Change-Id: I9d4b1e66e9628a6f3aef6975c60bf8617013629c
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/MultisiteReplicationPushFilter.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/MultisiteReplicationPushFilter.java
index e4bd2e3..b85ec89 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/MultisiteReplicationPushFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/MultisiteReplicationPushFilter.java
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.plugins.multisite.validation;
 
+import com.google.common.base.Preconditions;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -27,12 +28,10 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Optional;
 import java.util.Random;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdRef;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
@@ -68,57 +67,16 @@
           remoteUpdatesList.stream()
               .filter(
                   refUpdate -> {
-                    String ref = refUpdate.getSrcRef();
-                    try {
-                      if (sharedRefDb.isUpToDate(
-                          projectName, SharedRefDatabase.newRef(ref, refUpdate.getNewObjectId()))) {
-                        return true;
-                      }
-                      // The ref coming from the event might be old compared to the local version.
-                      // Valid refs won't be replicated because of this misalignment.
-                      // Reading the local ref and re-trying the comparison, after a short sleep,
-                      // could mitigate the issue.
-                      int waitBeforeReloadLocalVersionMs =
-                          MIN_WAIT_BEFORE_RELOAD_LOCAL_VERSION_MS
-                              + new Random().nextInt(RANDOM_WAIT_BEFORE_RELOAD_LOCAL_VERSION_MS);
-                      repLog.debug(
-                          String.format(
-                              "'%s' is not up-to-date for project '%s' [local='%s']. Reload local ref in '%d ms' and re-check",
-                              ref,
-                              projectName,
-                              refUpdate.getNewObjectId(),
-                              waitBeforeReloadLocalVersionMs));
-                      Thread.sleep(waitBeforeReloadLocalVersionMs);
-                      Optional<ObjectId> objectIdVersion =
-                          getProjectLocalObjectIdVersion(repository, projectName, ref);
-                      if (objectIdVersion.isPresent()
-                          && sharedRefDb.isUpToDate(
-                              projectName,
-                              new ObjectIdRef.Unpeeled(
-                                  Ref.Storage.NETWORK, ref, objectIdVersion.get()))) {
-                        repLog.debug("{} is up-to-date after retrying", objectIdVersion);
-                        return true;
-                      }
+                    boolean refUpToDate = isUpToDateWithRetry(projectName, repository, refUpdate);
+                    if (!refUpToDate) {
                       repLog.warn(
                           "{} is not up-to-date with the shared-refdb and thus will NOT BE replicated",
                           refUpdate);
-                    } catch (SharedLockException e) {
-                      repLog.warn(
-                          "{} is locked on shared-refdb and thus will NOT BE replicated",
-                          refUpdate);
-                    } catch (InterruptedException ie) {
-                      String message =
-                          String.format(
-                              "Error while waiting for next check for '%s', ref '%s'",
-                              projectName, ref);
-                      repLog.error(message);
-                      logger.atSevere().withCause(ie).log(message);
-                      return false;
+                      if (refUpdate.getSrcRef().endsWith(REF_META_SUFFIX)) {
+                        outdatedChanges.add(getRootChangeRefPrefix(refUpdate.getSrcRef()));
+                      }
                     }
-                    if (ref.endsWith(REF_META_SUFFIX)) {
-                      outdatedChanges.add(getRootChangeRefPrefix(ref));
-                    }
-                    return false;
+                    return refUpToDate;
                   })
               .collect(Collectors.toList());
 
@@ -143,6 +101,54 @@
     }
   }
 
+  private boolean isUpToDateWithRetry(
+      String projectName, Repository repository, RemoteRefUpdate refUpdate) {
+    String ref = refUpdate.getSrcRef();
+    try {
+      if (sharedRefDb.isUpToDate(
+          projectName, SharedRefDatabase.newRef(ref, refUpdate.getNewObjectId()))) {
+        return true;
+      }
+
+      randomSleepForMitigatingConditionWhereLocalRefHaveJustBeenChanged(
+          projectName, refUpdate, ref);
+
+      return sharedRefDb.isUpToDate(
+          projectName, SharedRefDatabase.newRef(ref, getNotNullExactRef(repository, ref)));
+    } catch (SharedLockException sle) {
+      String message =
+          String.format("%s is locked on shared-refdb and thus will NOT BE replicated", ref);
+      repLog.error(message);
+      logger.atSevere().withCause(sle).log(message);
+      return false;
+    } catch (IOException ioe) {
+      String message =
+          String.format("Error while extracting ref '%s' for project '%s'", ref, projectName);
+      repLog.error(message);
+      logger.atSevere().withCause(ioe).log(message);
+      return false;
+    }
+  }
+
+  private void randomSleepForMitigatingConditionWhereLocalRefHaveJustBeenChanged(
+      String projectName, RemoteRefUpdate refUpdate, String ref) {
+    int randomSleepTimeMsec =
+        MIN_WAIT_BEFORE_RELOAD_LOCAL_VERSION_MS
+            + new Random().nextInt(RANDOM_WAIT_BEFORE_RELOAD_LOCAL_VERSION_MS);
+    repLog.debug(
+        String.format(
+            "'%s' is not up-to-date for project '%s' [local='%s']. Reload local ref in '%d ms' and re-check",
+            ref, projectName, refUpdate.getNewObjectId(), randomSleepTimeMsec));
+    try {
+      Thread.sleep(randomSleepTimeMsec);
+    } catch (InterruptedException ie) {
+      String message =
+          String.format("Error while waiting for next check for '%s', ref '%s'", projectName, ref);
+      repLog.error(message);
+      logger.atWarning().withCause(ie).log(message);
+    }
+  }
+
   private String changePrefix(String changeRef) {
     if (changeRef == null || !changeRef.startsWith("refs/changes")) {
       return changeRef;
@@ -163,17 +169,9 @@
     return changeMetaRef;
   }
 
-  private Optional<ObjectId> getProjectLocalObjectIdVersion(
-      Repository repository, String projectName, String ref) {
-    try {
-      return Optional.ofNullable(repository.exactRef(ref).getObjectId());
-
-    } catch (IOException ioe) {
-      String message =
-          String.format("Error while extracting ref '%s' for project '%s'", ref, projectName);
-      repLog.error(message);
-      logger.atSevere().withCause(ioe).log(message);
-    }
-    return Optional.empty();
+  private ObjectId getNotNullExactRef(Repository repository, String refName) throws IOException {
+    Ref ref = repository.exactRef(refName);
+    Preconditions.checkNotNull(ref);
+    return ref.getObjectId();
   }
 }