Merge branch 'stable-3.6' into stable-3.7

* stable-3.6:
  Add newline at EOF as required by HAProxy
  Use GerritForge's archive-ci for downloading artifacts
  Fix issue with disabling ref-database
  Use a bucketed metric for the project-specific replication lag
  Fix pull replication fetch filter for non-existing refs on global-refdb
  Bump global-refdb to v3.4.8.6
  Address review comments of Change 376577
  Remove unused flogger
  Use archive-ci.gerritforge.com for downloading plugins
  Add handling of multi-base local disk repositories

Change-Id: Icbdb0eb3a8e2edbde6269c864bbc7927968ae7b5
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/Configuration.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/Configuration.java
index 3e75cf9..5c443e7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/Configuration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/Configuration.java
@@ -167,6 +167,13 @@
           null,
           SharedRefDbConfiguration.SharedRefDatabase.ENABLE_KEY,
           true);
+      if (cfg instanceof FileBasedConfig) {
+        try {
+          ((FileBasedConfig) cfg).save();
+        } catch (IOException e) {
+          throw new IllegalStateException("Error while enabling global-refdb by default", e);
+        }
+      }
     }
     return cfg;
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/GitModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/GitModule.java
index 38cddd5..96bb4e8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/GitModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/GitModule.java
@@ -15,24 +15,33 @@
 package com.googlesource.gerrit.plugins.multisite;
 
 import com.gerritforge.gerrit.globalrefdb.validation.SharedRefDbConfiguration;
+import com.google.gerrit.server.ModuleImpl;
+import com.google.gerrit.server.config.RepositoryConfig;
+import com.google.gerrit.server.git.GitRepositoryManagerModule;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.multisite.validation.ValidationModule;
 
+@ModuleImpl(name = GitRepositoryManagerModule.MANAGER_MODULE)
 public class GitModule extends AbstractModule {
   private final Configuration config;
+  private final RepositoryConfig repoConfig;
 
   @Inject
-  public GitModule(Configuration config) {
+  public GitModule(Configuration config, RepositoryConfig repoConfig) {
     this.config = config;
+    this.repoConfig = repoConfig;
   }
 
   @Override
   protected void configure() {
-    bind(SharedRefDbConfiguration.class).toInstance(config.getSharedRefDbConfiguration());
+    SharedRefDbConfiguration sharedRefDbConfiguration = config.getSharedRefDbConfiguration();
+    bind(SharedRefDbConfiguration.class).toInstance(sharedRefDbConfiguration);
     bind(ProjectVersionLogger.class).to(Log4jProjectVersionLogger.class);
-    if (config.getSharedRefDbConfiguration().getSharedRefDb().isEnabled()) {
-      install(new ValidationModule(config));
+    if (sharedRefDbConfiguration.getSharedRefDb().isEnabled()) {
+      install(new ValidationModule(config, repoConfig));
+    } else {
+      install(new GitRepositoryManagerModule(repoConfig));
     }
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/ReplicationStatus.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/ReplicationStatus.java
index d677df8..0253588 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/ReplicationStatus.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/ReplicationStatus.java
@@ -20,7 +20,7 @@
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.extensions.events.ProjectDeletedListener;
-import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.CallbackMetric1;
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.server.cache.CacheModule;
 import com.google.gerrit.server.cache.serialize.JavaCacheSerializer;
@@ -161,26 +161,27 @@
     }
   }
 
-  void incrementLagMetric(Project.NameKey projectName) {
-    logger.atFine().log("Creating replication lag metric for project %s", projectName);
-    String sanitizedProjectName = SubscriberMetrics.sanitizeProjectName(projectName.get());
-    metricMaker.newCallbackMetric(
-        String.format("%s_%s", SubscriberMetrics.REPLICATION_LAG_SEC, sanitizedProjectName),
-        Long.class,
-        new Description("Replication lag for project (sec)")
-            .setGauge()
-            .setUnit(Description.Units.SECONDS),
-        () -> getReplicationStatus(projectName.get()));
+  @VisibleForTesting
+  Runnable replicationLagMetricPerProject(CallbackMetric1<String, Long> metricCallback) {
+    return () -> {
+      if (replicationStatusPerProject.isEmpty()) {
+        metricCallback.forceCreate("");
+      } else {
+        replicationStatusPerProject.entrySet().stream()
+            .filter(e -> e.getValue() > 0)
+            .forEach(
+                e ->
+                    metricCallback.set(
+                        SubscriberMetrics.sanitizeProjectName(e.getKey()), e.getValue()));
+        metricCallback.prune();
+      }
+    };
   }
 
   @VisibleForTesting
   public void doUpdateLag(Project.NameKey projectName, Long lag) {
     cache.put(projectName.get(), lag);
     replicationStatusPerProject.put(projectName.get(), lag);
-
-    if (lag > 0) {
-      incrementLagMetric(projectName);
-    }
   }
 
   @VisibleForTesting
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetrics.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetrics.java
index 8c4efba..bb971ff 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetrics.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetrics.java
@@ -14,13 +14,15 @@
 
 package com.googlesource.gerrit.plugins.multisite.consumer;
 
-import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.metrics.CallbackMetric1;
 import com.google.gerrit.metrics.Counter1;
 import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.Field;
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.server.events.Event;
 import com.google.gerrit.server.events.ProjectEvent;
 import com.google.gerrit.server.events.RefUpdatedEvent;
+import com.google.gerrit.server.logging.Metadata;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.multisite.MultiSiteMetrics;
@@ -33,7 +35,6 @@
 
 @Singleton
 public class SubscriberMetrics extends MultiSiteMetrics {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
   private static final String SUBSCRIBER_SUCCESS_COUNTER = "subscriber_msg_consumer_counter";
   private static final String SUBSCRIBER_FAILURE_COUNTER =
       "subscriber_msg_consumer_failure_counter";
@@ -41,11 +42,15 @@
       "multi_site/subscriber/subscriber_replication_status/sec_behind";
   private static final String REPLICATION_LAG_MSEC =
       "multi_site/subscriber/subscriber_replication_status/msec_behind";
+  private static final String REPLICATION_LAG_MSEC_PROJECT =
+      "multi_site/subscriber/subscriber_replication_status/msec_behind/per_project";
 
   private final Counter1<String> subscriberSuccessCounter;
   private final Counter1<String> subscriberFailureCounter;
   private final ReplicationStatus replicationStatus;
   private static final Pattern isValidMetricNamePattern = Pattern.compile("[a-zA-Z0-9_-]");
+  private static final Field<String> PROJECT_NAME =
+      Field.ofString("project_name", Metadata.Builder::cacheName).build();
 
   @Inject
   public SubscriberMetrics(MetricMaker metricMaker, ReplicationStatus replicationStatus) {
@@ -77,6 +82,16 @@
             .setGauge()
             .setUnit(Description.Units.MILLISECONDS),
         replicationStatus::getMaxLagMillis);
+
+    CallbackMetric1<String, Long> metrics =
+        metricMaker.newCallbackMetric(
+            SubscriberMetrics.REPLICATION_LAG_MSEC_PROJECT,
+            Long.class,
+            new Description("Per-project replication lag (msec)")
+                .setGauge()
+                .setUnit(Description.Units.MILLISECONDS),
+            PROJECT_NAME);
+    metricMaker.newTrigger(metrics, replicationStatus.replicationLagMetricPerProject(metrics));
   }
 
   /**
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/MultisiteReplicationFetchFilter.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/MultisiteReplicationFetchFilter.java
index 4d349f0..98d1320 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/MultisiteReplicationFetchFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/MultisiteReplicationFetchFilter.java
@@ -65,7 +65,8 @@
           .filter(
               ref -> {
                 Optional<ObjectId> localRefOid =
-                    getSha1IfUpToDateWithGlobalRefDb(repository, projectName, refDb, ref, true);
+                    getLocalSha1IfEqualsToExistingGlobalRefDb(
+                        repository, projectName, refDb, ref, true);
                 localRefOid.ifPresent(
                     oid ->
                         repLog.info(
@@ -111,7 +112,7 @@
         .orElse(false);
   }
 
-  private Optional<ObjectId> getSha1IfUpToDateWithGlobalRefDb(
+  private Optional<ObjectId> getLocalSha1IfEqualsToExistingGlobalRefDb(
       Repository repository,
       String projectName,
       RefDatabase refDb,
@@ -120,14 +121,19 @@
     try {
       Optional<ObjectId> localRefObjectId =
           Optional.ofNullable(refDb.exactRef(ref))
-              .filter(r -> sharedRefDb.isUpToDate(Project.nameKey(projectName), r))
+              .filter(
+                  r ->
+                      sharedRefDb
+                          .get(Project.nameKey(projectName), r.getName(), String.class)
+                          .map(sharedRefObjId -> r.getObjectId().getName().equals(sharedRefObjId))
+                          .orElse(false))
               .map(Ref::getObjectId);
 
       if (localRefObjectId.isEmpty() && retryWithRandomSleep) {
         randomSleepForMitigatingConditionWhereLocalRefHaveJustBeenChanged(
             projectName, localRefObjectId, ref);
         localRefObjectId =
-            getSha1IfUpToDateWithGlobalRefDb(repository, projectName, refDb, ref, false);
+            getLocalSha1IfEqualsToExistingGlobalRefDb(repository, projectName, refDb, ref, false);
       }
 
       return localRefObjectId;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateImpl.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateImpl.java
index 336a950..64048ab 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateImpl.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateImpl.java
@@ -40,7 +40,6 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdRef;
 import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
@@ -232,15 +231,13 @@
     return Optional.empty();
   }
 
-  private Optional<Long> longBlobParse(Repository repo, String ref) throws IOException {
-    return Optional.ofNullable(repo.exactRef(ref))
+  private Optional<Long> longBlobParse(Repository repo, String refName) throws IOException {
+    return Optional.ofNullable(repo.exactRef(refName))
         .map(
-            (r) -> {
-              ObjectLoader loader;
+            (ref) -> {
               try {
-                loader = repo.open(r.getObjectId());
-                String boutString = new String(loader.getBytes(), StandardCharsets.UTF_8);
-                return Long.parseLong(boutString);
+                return Long.parseLong(
+                    new String(repo.open(ref.getObjectId()).getBytes(), StandardCharsets.UTF_8));
               } catch (IOException e) {
                 logger.atSevere().withCause(e).log(
                     "Unable to extract long BLOB from %s:%s", repo.getDirectory(), ref);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/RepositoryManagerModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/RepositoryManagerModule.java
new file mode 100644
index 0000000..b71ca67
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/RepositoryManagerModule.java
@@ -0,0 +1,41 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.multisite.validation;
+
+import com.gerritforge.gerrit.globalrefdb.validation.SharedRefDbGitRepositoryManager;
+import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.server.config.RepositoryConfig;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.LocalDiskRepositoryManager;
+import com.google.gerrit.server.git.MultiBaseLocalDiskRepositoryManager;
+
+class RepositoryManagerModule extends LifecycleModule {
+  private final RepositoryConfig cfg;
+
+  RepositoryManagerModule(RepositoryConfig cfg) {
+    this.cfg = cfg;
+  }
+
+  @Override
+  protected void configure() {
+    bind(GitRepositoryManager.class).to(SharedRefDbGitRepositoryManager.class);
+
+    // part responsible for physical repositories handling
+    listener().to(LocalDiskRepositoryManager.Lifecycle.class);
+    if (!cfg.getAllBasePaths().isEmpty()) {
+      bind(LocalDiskRepositoryManager.class).to(MultiBaseLocalDiskRepositoryManager.class);
+    }
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ValidationModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ValidationModule.java
index e9d3e1a..fc4d505 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ValidationModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ValidationModule.java
@@ -30,7 +30,7 @@
 import com.gerritforge.gerrit.globalrefdb.validation.dfsrefdb.SharedRefEnforcement;
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.config.RepositoryConfig;
 import com.google.inject.Scopes;
 import com.google.inject.TypeLiteral;
 import com.google.inject.name.Names;
@@ -38,9 +38,11 @@
 
 public class ValidationModule extends FactoryModule {
   private final Configuration cfg;
+  private final RepositoryConfig repoConfig;
 
-  public ValidationModule(Configuration cfg) {
+  public ValidationModule(Configuration cfg, RepositoryConfig repoConfig) {
     this.cfg = cfg;
+    this.repoConfig = repoConfig;
   }
 
   @Override
@@ -62,7 +64,7 @@
             ImmutableSet.of(
                 ProjectVersionRefUpdate.MULTI_SITE_VERSIONING_REF,
                 ProjectVersionRefUpdate.MULTI_SITE_VERSIONING_VALUE_REF));
-    bind(GitRepositoryManager.class).to(SharedRefDbGitRepositoryManager.class);
+    install(new RepositoryManagerModule(repoConfig));
 
     if (cfg.getSharedRefDbConfiguration().getSharedRefDb().getEnforcementRules().isEmpty()) {
       bind(SharedRefEnforcement.class).to(DefaultSharedRefEnforcement.class).in(Scopes.SINGLETON);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/GitModuleTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/GitModuleTest.java
new file mode 100644
index 0000000..7faccae
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/GitModuleTest.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.multisite;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.gerritforge.gerrit.globalrefdb.validation.SharedRefDbGitRepositoryManager;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.UseLocalDisk;
+import com.google.gerrit.acceptance.config.GerritConfig;
+import com.google.gerrit.acceptance.config.GlobalPluginConfig;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.LocalDiskRepositoryManager;
+import com.google.gerrit.server.git.MultiBaseLocalDiskRepositoryManager;
+import com.google.inject.Inject;
+import org.junit.Test;
+
+@UseLocalDisk
+@NoHttpd
+public class GitModuleTest extends AbstractDaemonTest {
+  @Inject private GitRepositoryManager gitRepoManager;
+  @Inject private LocalDiskRepositoryManager wrapped;
+
+  @Test
+  @GerritConfig(
+      name = "gerrit.installDbModule",
+      value = "com.googlesource.gerrit.plugins.multisite.GitModule")
+  public void shouldUseLocalDiskRepositoryManagerByDefault() {
+    assertThat(gitRepoManager).isInstanceOf(SharedRefDbGitRepositoryManager.class);
+    assertThat(wrapped).isNotInstanceOf(MultiBaseLocalDiskRepositoryManager.class);
+  }
+
+  @Test
+  @GerritConfig(
+      name = "gerrit.installDbModule",
+      value = "com.googlesource.gerrit.plugins.multisite.GitModule")
+  @GerritConfig(name = "repository.r1.basePath", value = "/tmp/git1")
+  public void shouldUseMultiBaseLocalDiskRepositoryManagerWhenItIsConfigured() {
+    assertThat(gitRepoManager).isInstanceOf(SharedRefDbGitRepositoryManager.class);
+    assertThat(wrapped).isInstanceOf(MultiBaseLocalDiskRepositoryManager.class);
+  }
+
+  @Test
+  @GerritConfig(
+      name = "gerrit.installDbModule",
+      value = "com.googlesource.gerrit.plugins.multisite.GitModule")
+  @GlobalPluginConfig(
+      pluginName = Configuration.PLUGIN_NAME,
+      name = "ref-database.enabled",
+      value = "false")
+  public void shouldInstallDefaultGerritGitManagerWhenRefDbIsDisabled() {
+    assertThat(gitRepoManager).isInstanceOf(LocalDiskRepositoryManager.class);
+  }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/CallbackMetricMaker.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/CallbackMetricMaker.java
index daf1cfd..a197c91 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/CallbackMetricMaker.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/CallbackMetricMaker.java
@@ -1,9 +1,9 @@
 package com.googlesource.gerrit.plugins.multisite.consumer;
 
-import com.google.common.base.Supplier;
-import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.metrics.Counter1;
 import com.google.gerrit.metrics.Description;
 import com.google.gerrit.metrics.DisabledMetricMaker;
+import com.google.gerrit.metrics.Field;
 import org.junit.Ignore;
 
 @Ignore
@@ -15,14 +15,9 @@
   }
 
   @Override
-  public <V> RegistrationHandle newCallbackMetric(
-      String name, Class<V> valueClass, Description desc, Supplier<V> trigger) {
+  public <F1> Counter1<F1> newCounter(String name, Description desc, Field<F1> field1) {
     callbackMetricCounter += 1;
-    return new RegistrationHandle() {
-
-      @Override
-      public void remove() {}
-    };
+    return super.newCounter(name, desc, field1);
   }
 
   public void resetCallbackMetricCounter() {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/ReplicationStatusTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/ReplicationStatusTest.java
index d49108c..7828062 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/ReplicationStatusTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/ReplicationStatusTest.java
@@ -15,8 +15,11 @@
 package com.googlesource.gerrit.plugins.multisite.consumer;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import com.google.common.cache.Cache;
@@ -24,6 +27,8 @@
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.events.ProjectDeletedListener;
+import com.google.gerrit.metrics.CallbackMetric1;
+import com.google.gerrit.metrics.DisabledMetricMaker;
 import com.google.gerrit.server.project.ProjectCache;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
 import com.googlesource.gerrit.plugins.multisite.ProjectVersionLogger;
@@ -43,9 +48,9 @@
   @Mock private ProjectVersionLogger verLogger;
   @Mock private ProjectCache projectCache;
   @Mock private ProjectVersionRefUpdate projectVersionRefUpdate;
+  @Mock private CallbackMetric1<String, Long> perProjectReplicationLagMetricCallback;
   private ReplicationStatus objectUnderTest;
   private Cache<String, Long> replicationStatusCache;
-  private CallbackMetricMaker callbackMetricMaker = new CallbackMetricMaker();
 
   @Before
   public void setup() throws Exception {
@@ -53,7 +58,6 @@
         .thenReturn(
             ImmutableSortedSet.of(Project.nameKey("projectA"), Project.nameKey("projectB")));
     replicationStatusCache = CacheBuilder.newBuilder().build();
-    callbackMetricMaker.resetCallbackMetricCounter();
     objectUnderTest =
         new ReplicationStatus(
             replicationStatusCache,
@@ -62,7 +66,7 @@
             projectCache,
             Executors.newScheduledThreadPool(1),
             new Configuration(new Config(), new Config()),
-            callbackMetricMaker);
+            new DisabledMetricMaker());
   }
 
   @Test
@@ -174,10 +178,12 @@
         .thenReturn(Optional.of(projectRemoteVersion));
 
     objectUnderTest.updateReplicationLag(Project.nameKey(projectName));
+    objectUnderTest.replicationLagMetricPerProject(perProjectReplicationLagMetricCallback).run();
 
     assertThat(replicationStatusCache.getIfPresent(projectName))
         .isEqualTo(projectRemoteVersion - projectLocalVersion);
-    assertThat(callbackMetricMaker.getCallbackMetricCounter()).isEqualTo(1);
+    verify(perProjectReplicationLagMetricCallback)
+        .set(eq(projectName), eq(projectRemoteVersion - projectLocalVersion));
   }
 
   @Test
@@ -189,7 +195,8 @@
 
     objectUnderTest.updateReplicationLag(Project.nameKey(projectName));
 
-    assertThat(callbackMetricMaker.getCallbackMetricCounter()).isEqualTo(0);
+    assertThat(replicationStatusCache.getIfPresent(projectName)).isNull();
+    verify(perProjectReplicationLagMetricCallback, never()).set(any(), any());
   }
 
   @SuppressWarnings("unchecked")
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/MultisiteReplicationFetchFilterTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/MultisiteReplicationFetchFilterTest.java
index 3e80b51..9a7a3b3 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/MultisiteReplicationFetchFilterTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/MultisiteReplicationFetchFilterTest.java
@@ -34,6 +34,7 @@
 import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -68,10 +69,18 @@
 
   @Test
   public void shouldReturnEmptyRefsWhenAllUpToDate() throws Exception {
-    newRef("refs/heads/foo");
-    newRef("refs/heads/bar");
-    Set<String> refs = Set.of("refs/heads/foo", "refs/heads/bar");
-    doReturn(true).when(sharedRefDatabaseMock).isUpToDate(eq(projectName), any());
+    String fooRefName = "refs/heads/foo";
+    ObjectId fooObjectId = newRef(fooRefName).getId();
+    String barRefName = "refs/heads/bar";
+    ObjectId barObjectId = newRef(barRefName).getId();
+    Set<String> refs = Set.of(fooRefName, barRefName);
+
+    doReturn(Optional.of(fooObjectId.getName()))
+        .when(sharedRefDatabaseMock)
+        .get(eq(projectName), eq(fooRefName), eq(String.class));
+    doReturn(Optional.of(barObjectId.getName()))
+        .when(sharedRefDatabaseMock)
+        .get(eq(projectName), eq(barRefName), eq(String.class));
 
     MultisiteReplicationFetchFilter fetchFilter =
         new MultisiteReplicationFetchFilter(sharedRefDatabaseMock, gitRepositoryManager, config);
@@ -84,13 +93,19 @@
   public void shouldFilterOutOneUpToDateRef() throws Exception {
     String refUpToDate = "refs/heads/uptodate";
     String outdatedRef = "refs/heads/outdated";
-    newRef(refUpToDate);
-    newRef(outdatedRef);
+    ObjectId upToDateObjectId = newRef(refUpToDate).getId();
+    newRef(outdatedRef).getId();
     Set<String> refsToFetch = Set.of(refUpToDate, outdatedRef);
-    SharedRefDatabaseWrapper sharedRefDatabase = new FakeSharedRefDatabaseWrapper(outdatedRef);
+
+    doReturn(Optional.of(upToDateObjectId.getName()))
+        .when(sharedRefDatabaseMock)
+        .get(eq(projectName), eq(refUpToDate), eq(String.class));
+    doReturn(Optional.of(AN_OUTDATED_OBJECT_ID.getName()))
+        .when(sharedRefDatabaseMock)
+        .get(eq(projectName), eq(outdatedRef), eq(String.class));
 
     MultisiteReplicationFetchFilter fetchFilter =
-        new MultisiteReplicationFetchFilter(sharedRefDatabase, gitRepositoryManager, config);
+        new MultisiteReplicationFetchFilter(sharedRefDatabaseMock, gitRepositoryManager, config);
     Set<String> filteredRefsToFetch = fetchFilter.filter(project, refsToFetch);
 
     assertThat(filteredRefsToFetch).containsExactly(outdatedRef);
@@ -99,10 +114,13 @@
   @Test
   public void shouldLoadLocalVersionAndFilterOut() throws Exception {
     String temporaryOutdated = "refs/heads/temporaryOutdated";
-    newRef(temporaryOutdated);
+    RevCommit localRef = newRef(temporaryOutdated);
 
     Set<String> refsToFetch = Set.of(temporaryOutdated);
-    doReturn(false).doReturn(true).when(sharedRefDatabaseMock).isUpToDate(eq(projectName), any());
+    doReturn(Optional.empty())
+        .doReturn(Optional.of(localRef.getId().getName()))
+        .when(sharedRefDatabaseMock)
+        .get(eq(projectName), eq(temporaryOutdated), eq(String.class));
 
     MultisiteReplicationFetchFilter fetchFilter =
         new MultisiteReplicationFetchFilter(sharedRefDatabaseMock, gitRepositoryManager, config);
@@ -110,7 +128,7 @@
 
     assertThat(filteredRefsToFetch).isEmpty();
 
-    verify(sharedRefDatabaseMock, times(2)).isUpToDate(any(), any());
+    verify(sharedRefDatabaseMock, times(2)).get(any(), any(), any());
   }
 
   @Test
@@ -119,14 +137,18 @@
     newRef(temporaryOutdated);
 
     Set<String> refsToFetch = Set.of(temporaryOutdated);
-    doReturn(false).doReturn(false).when(sharedRefDatabaseMock).isUpToDate(eq(projectName), any());
+    doReturn(Optional.of(AN_OUTDATED_OBJECT_ID.getName()))
+        .doReturn(Optional.of(AN_OUTDATED_OBJECT_ID.getName()))
+        .when(sharedRefDatabaseMock)
+        .get(eq(projectName), eq(temporaryOutdated), eq(String.class));
 
     MultisiteReplicationFetchFilter fetchFilter =
         new MultisiteReplicationFetchFilter(sharedRefDatabaseMock, gitRepositoryManager, config);
     Set<String> filteredRefsToFetch = fetchFilter.filter(project, refsToFetch);
 
     assertThat(filteredRefsToFetch).hasSize(1);
-    verify(sharedRefDatabaseMock, times(2)).isUpToDate(any(), any());
+    verify(sharedRefDatabaseMock, times(3))
+        .get(eq(projectName), eq(temporaryOutdated), eq(String.class));
   }
 
   @Test
@@ -134,6 +156,10 @@
     String temporaryOutdated = "refs/heads/temporaryOutdated";
     newRef(temporaryOutdated);
 
+    doReturn(Optional.empty())
+        .when(sharedRefDatabaseMock)
+        .get(eq(projectName), eq(temporaryOutdated), eq(String.class));
+
     Set<String> refsToFetch = Set.of(temporaryOutdated);
 
     MultisiteReplicationFetchFilter fetchFilter =
@@ -151,7 +177,7 @@
     Set<String> refsToFetch = Set.of(temporaryOutdated);
     doReturn(Optional.of(ObjectId.zeroId().getName()))
         .when(sharedRefDatabaseMock)
-        .get(eq(projectName), any(), any());
+        .get(eq(projectName), eq(temporaryOutdated), eq(String.class));
 
     MultisiteReplicationFetchFilter fetchFilter =
         new MultisiteReplicationFetchFilter(sharedRefDatabaseMock, gitRepositoryManager, config);
@@ -208,7 +234,7 @@
     assertThat(filteredRefsToFetch).hasSize(0);
   }
 
-  private void newRef(String refName) throws Exception {
-    repo.branch(refName).commit().create();
+  private RevCommit newRef(String refName) throws Exception {
+    return repo.branch(refName).commit().create();
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/RefFixture.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/RefFixture.java
index baba94b..1885d27 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/RefFixture.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/RefFixture.java
@@ -33,6 +33,8 @@
   static final ObjectId AN_OBJECT_ID_1 = new ObjectId(1, 2, 3, 4, 5);
   static final ObjectId AN_OBJECT_ID_2 = new ObjectId(1, 2, 3, 4, 6);
   static final ObjectId AN_OBJECT_ID_3 = new ObjectId(1, 2, 3, 4, 7);
+  static final ObjectId AN_OUTDATED_OBJECT_ID =
+      ObjectId.fromString("da37cb098dd7df4c8662ae8afc1acae5f7567775");
   static final String A_TEST_REF_NAME = "refs/heads/master";
   static final String A_REF_NAME_OF_A_PATCHSET = "refs/changes/01/1/1";