Merge branch 'stable-3.5' into stable-3.6

* stable-3.5:
  Bump up global-refdb to 3.5.4.3

Change-Id: Ie6a68e2f9ed86e3817bfc415f4c7e1bf8a21554b
diff --git a/e2e-tests/test.sh b/e2e-tests/test.sh
index 5491ddd..abdc5c5 100755
--- a/e2e-tests/test.sh
+++ b/e2e-tests/test.sh
@@ -16,7 +16,7 @@
 
 LOCATION="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
 LOCAL_ENV="$( cd "${LOCATION}/../setup_local_env" >/dev/null 2>&1 && pwd )"
-GERRIT_BRANCH=stable-3.5
+GERRIT_BRANCH=master
 GERRIT_CI=https://gerrit-ci.gerritforge.com/view/Plugins-$GERRIT_BRANCH/job
 LAST_BUILD=lastSuccessfulBuild/artifact/bazel-bin/plugins
 DEF_MULTISITE_LOCATION=${LOCATION}/../../../bazel-bin/plugins/multi-site/multi-site.jar
@@ -228,7 +228,7 @@
   { echo >&2 "$MULTISITE_LIB_LOCATION: Not able to copy the file. Aborting"; exit 1; }
 
 echo "Downloading websession-broker plugin $GERRIT_BRANCH"
-wget $GERRIT_CI/plugin-websession-broker-bazel-$GERRIT_BRANCH/$LAST_BUILD/websession-broker/websession-broker.jar \
+wget $GERRIT_CI/plugin-websession-broker-bazel-master-$GERRIT_BRANCH/$LAST_BUILD/websession-broker/websession-broker.jar \
   -O $COMMON_PLUGINS/websession-broker.jar || { echo >&2 "Cannot download websession-broker plugin: Check internet connection. Aborting"; exit 1; }
 
 echo "Downloading healthcheck plugin $GERRIT_BRANCH"
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
index 7992bdb..e989875 100644
--- a/external_plugin_deps.bzl
+++ b/external_plugin_deps.bzl
@@ -3,13 +3,13 @@
 def external_plugin_deps():
     maven_jar(
         name = "global-refdb",
-        artifact = "com.gerritforge:global-refdb:3.5.4.3",
-        sha1 = "1c3f986f1c7b992fd7a7df45c81bf24849171224",
+        artifact = "com.gerritforge:global-refdb:3.6.3.4",
+        sha1 = "1bd96a9f80b35e24a9b17becb0f49a510758a2fa",
     )
 
     maven_jar(
         name = "events-broker",
-        artifact = "com.gerritforge:events-broker:3.5.0.1",
-        sha1 = "af192a8bceaf7ff54d19356f9bfe1f1e83634b40",
+        artifact = "com.gerritforge:events-broker:3.6.3",
+        sha1 = "2a78d4492810d5b4280c6a92e6b8bbdadaffe7d2",
     )
 
diff --git a/setup_local_env/setup.sh b/setup_local_env/setup.sh
index a3dc6b9..9b0d868 100755
--- a/setup_local_env/setup.sh
+++ b/setup_local_env/setup.sh
@@ -16,7 +16,7 @@
 
 
 SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
-GERRIT_BRANCH=stable-3.5
+GERRIT_BRANCH=stable-3.6
 GERRIT_CI=https://gerrit-ci.gerritforge.com/view/Plugins-$GERRIT_BRANCH/job
 LAST_BUILD=lastSuccessfulBuild/artifact/bazel-bin/plugins
 EVENTS_BROKER_VER=`grep 'com.gerritforge:events-broker' $(dirname $0)/../external_plugin_deps.bzl | cut -d '"' -f 2 | cut -d ':' -f 3`
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/cache/CacheModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/cache/CacheModule.java
index 6373f58..5e54951 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/cache/CacheModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/cache/CacheModule.java
@@ -14,18 +14,31 @@
 
 package com.googlesource.gerrit.plugins.multisite.cache;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.gerrit.extensions.events.NewProjectCreatedListener;
 import com.google.gerrit.extensions.events.ProjectDeletedListener;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.server.cache.CacheRemovalListener;
+import com.googlesource.gerrit.plugins.multisite.ExecutorProvider;
 import java.util.concurrent.Executor;
 
 public class CacheModule extends LifecycleModule {
 
+  private final Class<? extends ExecutorProvider> cacheExecutorProviderClass;
+
+  public CacheModule() {
+    this(CacheExecutorProvider.class);
+  }
+
+  @VisibleForTesting
+  public CacheModule(Class<? extends ExecutorProvider> cacheExecutorProviderClass) {
+    this.cacheExecutorProviderClass = cacheExecutorProviderClass;
+  }
+
   @Override
   protected void configure() {
-    bind(Executor.class).annotatedWith(CacheExecutor.class).toProvider(CacheExecutorProvider.class);
+    bind(Executor.class).annotatedWith(CacheExecutor.class).toProvider(cacheExecutorProviderClass);
     listener().to(CacheExecutorProvider.class);
     DynamicSet.bind(binder(), CacheRemovalListener.class).to(CacheEvictionHandler.class);
     DynamicSet.bind(binder(), NewProjectCreatedListener.class).to(ProjectListUpdateHandler.class);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/AbstractSubcriber.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/AbstractSubcriber.java
index 7107b87..c2b9e9c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/AbstractSubcriber.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/AbstractSubcriber.java
@@ -70,13 +70,9 @@
     if ((Strings.isNullOrEmpty(sourceInstanceId) || instanceId.equals(sourceInstanceId))
         || !shouldConsumeEvent(event)) {
       if (Strings.isNullOrEmpty(sourceInstanceId)) {
-        logger.atWarning().log(
-            String.format(
-                "Dropping event %s because sourceInstanceId cannot be null", event.toString()));
+        logger.atWarning().log("Dropping event %s because sourceInstanceId cannot be null", event);
       } else if (instanceId.equals(sourceInstanceId)) {
-        logger.atFiner().log(
-            String.format(
-                "Dropping event %s produced by our instanceId %s", event.toString(), instanceId));
+        logger.atFiner().log("Dropping event %s produced by our instanceId %s", event, instanceId);
       }
       droppedEventListeners.forEach(l -> l.onEventDropped(event));
     } else {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/router/IndexEventRouter.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/router/IndexEventRouter.java
index 2cf83cd..a647bce 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/router/IndexEventRouter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/router/IndexEventRouter.java
@@ -21,6 +21,10 @@
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritInstanceId;
+import com.google.gerrit.server.events.Event;
+import com.google.gerrit.server.events.EventListener;
+import com.google.gerrit.server.events.RefEvent;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.multisite.forwarder.ForwardedIndexAccountHandler;
 import com.googlesource.gerrit.plugins.multisite.forwarder.ForwardedIndexChangeHandler;
@@ -32,12 +36,12 @@
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.GroupIndexEvent;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.IndexEvent;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.ProjectIndexEvent;
-import com.googlesource.gerrit.plugins.replication.events.RefReplicationDoneEvent;
 import java.io.IOException;
 import java.util.Optional;
 import java.util.Set;
 
-public class IndexEventRouter implements ForwardedEventRouter<IndexEvent>, LifecycleListener {
+public class IndexEventRouter
+    implements ForwardedEventRouter<IndexEvent>, EventListener, LifecycleListener {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   private final ForwardedIndexAccountHandler indexAccountHandler;
@@ -45,6 +49,7 @@
   private final ForwardedIndexGroupHandler indexGroupHandler;
   private final ForwardedIndexProjectHandler indexProjectHandler;
   private final AllUsersName allUsersName;
+  private final String gerritInstanceId;
 
   @Inject
   public IndexEventRouter(
@@ -52,12 +57,14 @@
       ForwardedIndexChangeHandler indexChangeHandler,
       ForwardedIndexGroupHandler indexGroupHandler,
       ForwardedIndexProjectHandler indexProjectHandler,
-      AllUsersName allUsersName) {
+      AllUsersName allUsersName,
+      @GerritInstanceId String gerritInstanceId) {
     this.indexAccountHandler = indexAccountHandler;
     this.indexChangeHandler = indexChangeHandler;
     this.indexGroupHandler = indexGroupHandler;
     this.indexProjectHandler = indexProjectHandler;
     this.allUsersName = allUsersName;
+    this.gerritInstanceId = gerritInstanceId;
   }
 
   @Override
@@ -85,7 +92,7 @@
     }
   }
 
-  public void onRefReplicated(RefReplicationDoneEvent replicationEvent) throws IOException {
+  public void onRefReplicated(RefEvent replicationEvent) throws IOException {
     if (replicationEvent.getProjectNameKey().equals(allUsersName)) {
       Account.Id accountId = Account.Id.fromRef(replicationEvent.getRefName());
       if (accountId != null) {
@@ -97,6 +104,20 @@
   }
 
   @Override
+  public void onEvent(Event event) {
+    if (event instanceof RefEvent
+        && (event.getType().contains("fetch-ref-replicated")
+            || event.getType().contains("fetch-ref-replication-done"))
+        && gerritInstanceId.equals(event.instanceId)) {
+      try {
+        onRefReplicated((RefEvent) event);
+      } catch (IOException e) {
+        logger.atSevere().withCause(e).log("Error while processing event %s", event);
+      }
+    }
+  }
+
+  @Override
   public void start() {}
 
   @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/router/RouterModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/router/RouterModule.java
index bac907e..2193034 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/router/RouterModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/router/RouterModule.java
@@ -14,7 +14,9 @@
 
 package com.googlesource.gerrit.plugins.multisite.forwarder.router;
 
+import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.server.events.EventListener;
 import com.google.inject.Scopes;
 
 public class RouterModule extends LifecycleModule {
@@ -22,6 +24,8 @@
   protected void configure() {
     bind(IndexEventRouter.class).in(Scopes.SINGLETON);
     listener().to(IndexEventRouter.class).in(Scopes.SINGLETON);
+    DynamicSet.bind(binder(), EventListener.class).to(IndexEventRouter.class);
+
     bind(CacheEvictionEventRouter.class).in(Scopes.SINGLETON);
     bind(ProjectListUpdateRouter.class).in(Scopes.SINGLETON);
     bind(StreamEventRouter.class).in(Scopes.SINGLETON);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/index/ChangeCheckerImpl.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/index/ChangeCheckerImpl.java
index 4ef5b7f..7373bcc 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/index/ChangeCheckerImpl.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/index/ChangeCheckerImpl.java
@@ -182,7 +182,7 @@
 
   private long getTsFromChangeAndDraftComments(ChangeNotes notes) {
     Change change = notes.getChange();
-    Timestamp changeTs = change.getLastUpdatedOn();
+    Timestamp changeTs = Timestamp.from(change.getLastUpdatedOn());
     try {
       for (HumanComment comment : commentsUtil.draftByChange(changeNotes.get())) {
         Timestamp commentTs = comment.writtenOn;
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 b751aab..4d349f0 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
@@ -81,7 +81,7 @@
     } catch (IOException ioe) {
       String message = String.format("Error while opening project: '%s'", projectName);
       repLog.error(message);
-      logger.atSevere().withCause(ioe).log(message);
+      logger.atSevere().withCause(ioe).log("%s", message);
       return Collections.emptySet();
     }
   }
@@ -134,13 +134,13 @@
     } catch (GlobalRefDbLockException gle) {
       String message = String.format("%s is locked on shared-refdb", ref);
       repLog.error(message);
-      logger.atSevere().withCause(gle).log(message);
+      logger.atSevere().withCause(gle).log("%s", message);
       return Optional.empty();
     } 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);
+      logger.atSevere().withCause(ioe).log("%s", message);
       return Optional.empty();
     }
   }
@@ -171,7 +171,7 @@
       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);
+      logger.atWarning().withCause(ie).log("%s", message);
     }
   }
 }
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 6f9c2e7..0aba056 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
@@ -102,9 +102,9 @@
           .collect(Collectors.toList());
 
     } catch (IOException ioe) {
-      String message = String.format("Error while opening project: '%s'", projectName);
-      repLog.error(message);
-      logger.atSevere().withCause(ioe).log(message);
+      final String messageFmt = "Error while opening project: '%s'";
+      repLog.error(messageFmt, projectName);
+      logger.atSevere().withCause(ioe).log(messageFmt, projectName);
       return Collections.emptyList();
     }
   }
@@ -132,16 +132,14 @@
           ? Optional.of(refUpdateReloaded)
           : Optional.empty();
     } catch (GlobalRefDbLockException gle) {
-      String message =
-          String.format("%s is locked on shared-refdb and thus will NOT BE replicated", ref);
-      repLog.error(message);
-      logger.atSevere().withCause(gle).log(message);
+      final String messageFmt = "%s is locked on shared-refdb and thus will NOT BE replicated";
+      repLog.error(messageFmt, ref);
+      logger.atSevere().withCause(gle).log(messageFmt, ref);
       return Optional.empty();
     } 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);
+      final String messageFmt = "Error while extracting ref '%s' for project '%s'";
+      repLog.error(messageFmt, ref, projectName);
+      logger.atSevere().withCause(ioe).log(messageFmt, ref, projectName);
       return Optional.empty();
     }
   }
@@ -174,10 +172,9 @@
     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);
+      final String messageFmt = "Error while waiting for next check for '%s', ref '%s'";
+      repLog.error(messageFmt, projectName, ref);
+      logger.atWarning().withCause(ie).log(messageFmt, projectName, ref);
     }
   }
 
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 e9a84ac..10c0d80 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
@@ -71,7 +71,7 @@
 
   @Override
   public void onEvent(Event event) {
-    logger.atFine().log("Processing event type: " + event.type);
+    logger.atFine().log("Processing event type: %s", event.type);
     // Producer of the Event use RefUpdatedEvent to trigger the version update
     if (nodeInstanceId.equals(event.instanceId) && event instanceof RefUpdatedEvent) {
       updateProducerProjectVersionUpdate((RefUpdatedEvent) event);
@@ -114,8 +114,8 @@
       }
     } catch (LocalProjectVersionUpdateException | SharedProjectVersionUpdateException e) {
       logger.atSevere().withCause(e).log(
-          "Issue encountered when updating version for project "
-              + refUpdatedEvent.getProjectNameKey());
+          "Issue encountered when updating version for project %s",
+          refUpdatedEvent.getProjectNameKey());
     }
   }
 
@@ -134,6 +134,7 @@
     return newId;
   }
 
+  @SuppressWarnings("FloggerLogString")
   private boolean updateSharedProjectVersion(
       Project.NameKey projectNameKey, ObjectId newObjectId, Long newVersion)
       throws SharedProjectVersionUpdateException {
@@ -156,20 +157,18 @@
     try {
       if (sharedVersion.isPresent() && sharedVersion.get() >= newVersion) {
         logger.atWarning().log(
-            String.format(
-                "NOT Updating project %s version %s (value=%d) in shared ref-db because is more recent than the local one %s (value=%d) ",
-                projectNameKey.get(),
-                newObjectId,
-                newVersion,
-                sharedRef.getObjectId().getName(),
-                sharedVersion.get()));
+            "NOT Updating project %s version %s (value=%d) in shared ref-db because is more recent than the local one %s (value=%d) ",
+            projectNameKey.get(),
+            newObjectId,
+            newVersion,
+            sharedRef.getObjectId().getName(),
+            sharedVersion.get());
         return false;
       }
 
       logger.atFine().log(
-          String.format(
-              "Updating shared project %s version to %s (value=%d)",
-              projectNameKey.get(), newObjectId, newVersion));
+          "Updating shared project %s version to %s (value=%d)",
+          projectNameKey.get(), newObjectId, newVersion);
 
       boolean success = sharedRefDb.compareAndPut(projectNameKey, sharedRef, newObjectId);
       if (!success) {
@@ -256,6 +255,7 @@
     }
   }
 
+  @SuppressWarnings("FloggerLogString")
   private Optional<RefUpdate> updateLocalProjectVersion(
       Project.NameKey projectNameKey, long newVersionNumber)
       throws LocalProjectVersionUpdateException {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/cache/CacheEvictionHandlerTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/cache/CacheEvictionHandlerTest.java
index ce222d0..ed51e1b 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/cache/CacheEvictionHandlerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/cache/CacheEvictionHandlerTest.java
@@ -14,7 +14,7 @@
 
 package com.googlesource.gerrit.plugins.multisite.cache;
 
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
 
 import com.google.common.cache.RemovalCause;
 import com.google.common.cache.RemovalNotification;
@@ -44,6 +44,6 @@
     handler.onRemoval(
         "test", "accounts", RemovalNotification.create("test", "accounts", RemovalCause.EXPLICIT));
 
-    verifyZeroInteractions(executorMock);
+    verifyNoInteractions(executorMock);
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/cache/ProjectListUpdateHandlerTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/cache/ProjectListUpdateHandlerTest.java
index 69968b5..3a4242c 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/cache/ProjectListUpdateHandlerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/cache/ProjectListUpdateHandlerTest.java
@@ -20,7 +20,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 
 import com.google.common.util.concurrent.MoreExecutors;
@@ -90,7 +90,7 @@
     handler.onNewProjectCreated(mock(NewProjectCreatedListener.Event.class));
     handler.onProjectDeleted(mock(ProjectDeletedListener.Event.class));
     Context.unsetForwardedEvent();
-    verifyZeroInteractions(forwarder);
+    verifyNoInteractions(forwarder);
   }
 
   @Test
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/IndexEventSubscriberTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/IndexEventSubscriberTest.java
index 0e63528..fd4a0aa 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/IndexEventSubscriberTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/IndexEventSubscriberTest.java
@@ -128,6 +128,6 @@
         Change.id(CHANGE_ID),
         Account.id(9999),
         BranchNameKey.create(Project.nameKey(PROJECT_NAME), "refs/heads/master"),
-        TimeUtil.nowTs());
+        TimeUtil.now());
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetricsTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetricsTest.java
index 6d660bb..8d0ac81 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetricsTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetricsTest.java
@@ -16,7 +16,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 
 import com.google.common.base.Suppliers;
@@ -139,7 +139,7 @@
 
     metrics.updateReplicationStatusMetrics(eventMessage);
 
-    verifyZeroInteractions(verLogger);
+    verifyNoInteractions(verLogger);
   }
 
   @Test
@@ -151,7 +151,7 @@
 
     metrics.updateReplicationStatusMetrics(eventMessage);
 
-    verifyZeroInteractions(verLogger);
+    verifyNoInteractions(verLogger);
   }
 
   @Test
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/event/IndexEventRouterTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/event/IndexEventRouterTest.java
index 8efa2ed..4ffdbbc 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/event/IndexEventRouterTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/event/IndexEventRouterTest.java
@@ -16,7 +16,7 @@
 
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
 
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.server.config.AllUsersName;
@@ -61,7 +61,8 @@
             indexChangeHandler,
             indexGroupHandler,
             indexProjectHandler,
-            allUsersName);
+            allUsersName,
+            INSTANCE_ID);
   }
 
   @Test
@@ -72,7 +73,7 @@
     verify(indexAccountHandler)
         .indexAsync(Account.id(event.accountId), ForwardedIndexingHandler.Operation.INDEX);
 
-    verifyZeroInteractions(indexChangeHandler, indexGroupHandler, indexProjectHandler);
+    verifyNoInteractions(indexChangeHandler, indexGroupHandler, indexProjectHandler);
   }
 
   @Test
@@ -86,7 +87,7 @@
     verify(indexAccountHandler)
         .indexAsync(Account.id(event.accountId), ForwardedIndexingHandler.Operation.INDEX);
 
-    verifyZeroInteractions(indexChangeHandler, indexGroupHandler, indexProjectHandler);
+    verifyNoInteractions(indexChangeHandler, indexGroupHandler, indexProjectHandler);
 
     streamEventRouter.route(new RefReplicationDoneEvent(allUsersName.get(), "refs/any", 1));
 
@@ -102,7 +103,7 @@
     verify(indexGroupHandler)
         .index(groupId, ForwardedIndexingHandler.Operation.INDEX, Optional.of(event));
 
-    verifyZeroInteractions(indexAccountHandler, indexChangeHandler, indexProjectHandler);
+    verifyNoInteractions(indexAccountHandler, indexChangeHandler, indexProjectHandler);
   }
 
   @Test
@@ -114,7 +115,7 @@
     verify(indexProjectHandler)
         .index(projectName, ForwardedIndexingHandler.Operation.INDEX, Optional.of(event));
 
-    verifyZeroInteractions(indexAccountHandler, indexChangeHandler, indexGroupHandler);
+    verifyNoInteractions(indexAccountHandler, indexChangeHandler, indexGroupHandler);
   }
 
   @Test
@@ -128,7 +129,7 @@
             ForwardedIndexingHandler.Operation.INDEX,
             Optional.of(event));
 
-    verifyZeroInteractions(indexAccountHandler, indexGroupHandler, indexProjectHandler);
+    verifyNoInteractions(indexAccountHandler, indexGroupHandler, indexProjectHandler);
   }
 
   @Test
@@ -142,7 +143,7 @@
             ForwardedIndexingHandler.Operation.DELETE,
             Optional.of(event));
 
-    verifyZeroInteractions(indexAccountHandler, indexGroupHandler, indexProjectHandler);
+    verifyNoInteractions(indexAccountHandler, indexGroupHandler, indexProjectHandler);
   }
 
   @Test
@@ -150,7 +151,7 @@
     final IndexEvent newEventType = new IndexEvent("new-type", INSTANCE_ID) {};
 
     assertThrows(UnsupportedOperationException.class, () -> router.route(newEventType));
-    verifyZeroInteractions(
+    verifyNoInteractions(
         indexAccountHandler, indexChangeHandler, indexGroupHandler, indexProjectHandler);
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/event/StreamEventRouterTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/event/StreamEventRouterTest.java
index 3b4bec5..ac90436 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/event/StreamEventRouterTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/event/StreamEventRouterTest.java
@@ -55,6 +55,6 @@
         Change.id(1),
         Account.id(1),
         BranchNameKey.create("proj", "refs/heads/master"),
-        TimeUtil.nowTs());
+        TimeUtil.now());
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/BrokerForwarderTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/BrokerForwarderTest.java
index 28bf56d..c583455 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/BrokerForwarderTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/BrokerForwarderTest.java
@@ -16,7 +16,7 @@
 
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
 
 import com.googlesource.gerrit.plugins.multisite.Configuration;
 import com.googlesource.gerrit.plugins.multisite.broker.BrokerApiWrapper;
@@ -99,21 +99,21 @@
   @Test
   public void shouldSkipEventFromHighAvailabilityPluginThread() {
     brokerForwarder.send(newForwarderTask(HIGH_AVAILABILITY_PLUGIN), testTopic, testEvent);
-    verifyZeroInteractions(brokerMock);
+    verifyNoInteractions(brokerMock);
   }
 
   @Test
   public void shouldSkipEventFromHighAvailabilityPluginForwardedThread() {
     brokerForwarder.send(newForwarderTask(HIGH_AVAILABILITY_FORWARDED), testTopic, testEvent);
 
-    verifyZeroInteractions(brokerMock);
+    verifyNoInteractions(brokerMock);
   }
 
   @Test
   public void shouldSkipEventFromHighAvailabilityPluginBatchForwardedThread() {
     brokerForwarder.send(newForwarderTask(HIGH_AVAILABILITY_BATCH_FORWARDED), testTopic, testEvent);
 
-    verifyZeroInteractions(brokerMock);
+    verifyNoInteractions(brokerMock);
   }
 
   private ForwarderTask newForwarderTask(String threadName) {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedCacheEvictionHandlerIT.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedCacheEvictionHandlerIT.java
index 75596ed..6ca1a7a 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedCacheEvictionHandlerIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedCacheEvictionHandlerIT.java
@@ -29,10 +29,13 @@
 import com.google.gerrit.extensions.registration.RegistrationHandle;
 import com.google.gerrit.server.cache.CacheRemovalListener;
 import com.google.gerrit.server.events.EventGson;
+import com.google.gerrit.server.git.WorkQueue;
 import com.google.gerrit.server.project.ProjectCacheImpl;
 import com.google.gson.Gson;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.multisite.ExecutorProvider;
 import com.googlesource.gerrit.plugins.multisite.cache.CacheModule;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.CacheEvictionEvent;
 import com.googlesource.gerrit.plugins.multisite.forwarder.router.CacheEvictionEventRouter;
@@ -43,7 +46,10 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 import org.eclipse.jgit.lib.Config;
 import org.junit.After;
 import org.junit.Before;
@@ -69,7 +75,7 @@
     @Override
     protected void configure() {
       install(new ForwarderModule());
-      install(new CacheModule());
+      install(new CacheModule(TestForwardingExecutorProvider.class));
       install(new RouterModule());
       install(new IndexModule());
       SharedRefDbConfiguration sharedRefDbConfig =
@@ -78,11 +84,44 @@
     }
   }
 
+  @Singleton
+  public static class TestForwardingExecutorProvider extends ExecutorProvider {
+    private final ScheduledThreadPoolExecutor executor;
+    private final AtomicInteger executionsCounter;
+
+    @Inject
+    protected TestForwardingExecutorProvider(WorkQueue workQueue) {
+      super(workQueue, 1, "test");
+      executionsCounter = new AtomicInteger();
+      executor =
+          new ScheduledThreadPoolExecutor(1) {
+
+            @Override
+            public void execute(Runnable command) {
+              @SuppressWarnings("unused")
+              int ignored = executionsCounter.incrementAndGet();
+              super.execute(command);
+            }
+          };
+    }
+
+    @Override
+    public ScheduledExecutorService get() {
+      return executor;
+    }
+
+    public int executions() {
+      return executionsCounter.get();
+    }
+  }
+
   public static class CacheEvictionsTracker<K, V> implements CacheRemovalListener<K, V> {
     private final Map<String, Set<Object>> trackedEvictions;
     private final CountDownLatch allExpectedEvictionsArrived;
+    private final String trackedCacheName;
 
-    public CacheEvictionsTracker(int numExpectedEvictions) {
+    public CacheEvictionsTracker(String cacheName, int numExpectedEvictions) {
+      this.trackedCacheName = cacheName;
       allExpectedEvictionsArrived = new CountDownLatch(numExpectedEvictions);
       trackedEvictions = Maps.newHashMap();
     }
@@ -99,22 +138,24 @@
     @Override
     public void onRemoval(
         String pluginName, String cacheName, RemovalNotification<K, V> notification) {
-      trackedEvictions.compute(
-          cacheName,
-          (k, v) -> {
-            if (v == null) {
-              return Sets.newHashSet(notification.getKey());
-            }
-            v.add(notification.getKey());
-            return v;
-          });
-      allExpectedEvictionsArrived.countDown();
+      if (cacheName.equals(trackedCacheName)) {
+        trackedEvictions.compute(
+            cacheName,
+            (k, v) -> {
+              if (v == null) {
+                return Sets.newHashSet(notification.getKey());
+              }
+              v.add(notification.getKey());
+              return v;
+            });
+        allExpectedEvictionsArrived.countDown();
+      }
     }
   }
 
   @Before
   public void startTrackingCacheEvictions() {
-    evictionsCacheTracker = new CacheEvictionsTracker<>(1);
+    evictionsCacheTracker = new CacheEvictionsTracker<>(ProjectCacheImpl.CACHE_NAME, 1);
     cacheEvictionRegistrationHandle = cacheRemovalListeners.add("gerrit", evictionsCacheTracker);
   }
 
@@ -135,6 +176,36 @@
   }
 
   @Test
+  @GerritConfig(name = "gerrit.instanceId", value = "testInstanceId")
+  @GerritConfig(name = "cache.threads", value = "0")
+  public void shouldNotForwardProjectCacheEvictionsWhenEventIsForwarded() throws Exception {
+    TestForwardingExecutorProvider cacheForwarder =
+        plugin.getSysInjector().getInstance(TestForwardingExecutorProvider.class);
+    Context.setForwardedEvent(true);
+    projectCache.evict(allProjects);
+
+    evictionsCacheTracker.waitForExpectedEvictions();
+    assertThat(evictionsCacheTracker.trackedEvictionsFor(ProjectCacheImpl.CACHE_NAME))
+        .contains(allProjects);
+
+    assertThat(cacheForwarder.executions()).isEqualTo(0);
+  }
+
+  @Test
+  @GerritConfig(name = "gerrit.instanceId", value = "testInstanceId")
+  public void shouldForwardProjectCacheEvictions() throws Exception {
+    TestForwardingExecutorProvider cacheForwarder =
+        plugin.getSysInjector().getInstance(TestForwardingExecutorProvider.class);
+    projectCache.evict(allProjects);
+
+    evictionsCacheTracker.waitForExpectedEvictions();
+    assertThat(evictionsCacheTracker.trackedEvictionsFor(ProjectCacheImpl.CACHE_NAME))
+        .contains(allProjects);
+
+    assertThat(cacheForwarder.executions()).isEqualTo(1);
+  }
+
+  @Test
   @GerritConfig(name = "gerrit.instanceId", value = "instance-id")
   public void shouldEvictProjectCacheWithSlash() throws Exception {
     ProjectInput in = new ProjectInput();
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedIndexChangeHandlerTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedIndexChangeHandlerTest.java
index b827cdf..2c4b157 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedIndexChangeHandlerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedIndexChangeHandlerTest.java
@@ -87,7 +87,7 @@
   public void setUp() throws Exception {
     when(ctxMock.open()).thenReturn(manualRequestContextMock);
     id = Change.id(TEST_CHANGE_NUMBER);
-    change = new Change(null, id, null, null, TimeUtil.nowTs());
+    change = new Change(null, id, null, null, TimeUtil.now());
     when(changeNotes.getChange()).thenReturn(change);
     when(changeCheckerFactoryMock.create(any())).thenReturn(changeCheckerAbsentMock);
     when(configurationMock.index()).thenReturn(index);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java
index cce996b..92015ce 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java
@@ -21,7 +21,7 @@
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 
 import com.gerritforge.gerrit.globalrefdb.validation.SharedRefDatabaseWrapper;
@@ -231,7 +231,7 @@
     Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
     assertThat(ref).isNull();
 
-    verifyZeroInteractions(verLogger);
+    verifyNoInteractions(verLogger);
   }
 
   @Test
@@ -246,7 +246,7 @@
     Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
     assertThat(ref).isNull();
 
-    verifyZeroInteractions(verLogger);
+    verifyNoInteractions(verLogger);
   }
 
   @Test
@@ -261,7 +261,7 @@
     Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
     assertThat(ref).isNull();
 
-    verifyZeroInteractions(verLogger);
+    verifyNoInteractions(verLogger);
   }
 
   @Test