Merge branch 'stable-3.1' into stable-3.2

* stable-3.1:
  Inject EventDispatcher as DynamicItem

Change-Id: I04bacde57ee3fa219662a9d4bb4a2b93fb185789
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
index 9cbb332..fee8aca 100644
--- a/external_plugin_deps.bzl
+++ b/external_plugin_deps.bzl
@@ -9,6 +9,6 @@
 
     maven_jar(
         name = "events-broker",
-        artifact = "com.gerritforge:events-broker:3.1.4",
-        sha1 = "5672908dde0bd02cabc95efe34a8d8507d44b6ac",
+        artifact = "com.gerritforge:events-broker:3.2.0-rc4",
+        sha1 = "53e3f862ac2c2196dba716756ac9586f4b63af47",
     )
diff --git a/setup_local_env/setup.sh b/setup_local_env/setup.sh
index 7615ca0..84037ad 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.1
+GERRIT_BRANCH=stable-3.2
 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`
@@ -341,7 +341,7 @@
 fi
 if [ $DOWNLOAD_WEBSESSION_PLUGIN = "true" ];then
   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 $DEPLOYMENT_LOCATION/websession-broker.jar || { echo >&2 "Cannot download websession-broker plugin: Check internet connection. Abort\
 ing"; exit 1; }
   wget $GERRIT_CI/plugin-healthcheck-bazel-$GERRIT_BRANCH/$LAST_BUILD/healthcheck/healthcheck.jar \
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/SharedRefDatabaseWrapper.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/SharedRefDatabaseWrapper.java
index b56f2ea..ddf9d84 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/SharedRefDatabaseWrapper.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/SharedRefDatabaseWrapper.java
@@ -21,11 +21,13 @@
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.NoopSharedRefDatabase;
 import java.util.Optional;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
 
 public class SharedRefDatabaseWrapper implements GlobalRefDatabase {
+  private static final GlobalRefDatabase NOOP_REFDB = new NoopSharedRefDatabase();
 
   @Inject(optional = true)
   private DynamicItem<GlobalRefDatabase> sharedRefDbDynamicItem;
@@ -95,6 +97,6 @@
   }
 
   private GlobalRefDatabase sharedRefDb() {
-    return sharedRefDbDynamicItem.get();
+    return Optional.ofNullable(sharedRefDbDynamicItem).map(di -> di.get()).orElse(NOOP_REFDB);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/cache/CacheEvictionHandler.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/cache/CacheEvictionHandler.java
index 77c19c1..2990264 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/cache/CacheEvictionHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/cache/CacheEvictionHandler.java
@@ -20,6 +20,7 @@
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.multisite.forwarder.CacheEvictionForwarder;
 import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
+import com.googlesource.gerrit.plugins.multisite.forwarder.ForwarderTask;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.CacheEvictionEvent;
 import java.util.concurrent.Executor;
 
@@ -45,7 +46,7 @@
     }
   }
 
-  class CacheEvictionTask implements Runnable {
+  class CacheEvictionTask extends ForwarderTask {
     CacheEvictionEvent cacheEvictionEvent;
 
     CacheEvictionTask(CacheEvictionEvent cacheEvictionEvent) {
@@ -54,7 +55,7 @@
 
     @Override
     public void run() {
-      forwarders.forEach(f -> f.evict(cacheEvictionEvent));
+      forwarders.forEach(f -> f.evict(this, cacheEvictionEvent));
     }
 
     @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/cache/ProjectListUpdateHandler.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/cache/ProjectListUpdateHandler.java
index 5723fbe..1060977 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/cache/ProjectListUpdateHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/cache/ProjectListUpdateHandler.java
@@ -21,6 +21,7 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
+import com.googlesource.gerrit.plugins.multisite.forwarder.ForwarderTask;
 import com.googlesource.gerrit.plugins.multisite.forwarder.ProjectListUpdateForwarder;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.ProjectListUpdateEvent;
 import java.util.concurrent.Executor;
@@ -57,7 +58,7 @@
     }
   }
 
-  class ProjectListUpdateTask implements Runnable {
+  class ProjectListUpdateTask extends ForwarderTask {
     private final ProjectListUpdateEvent projectListUpdateEvent;
 
     ProjectListUpdateTask(ProjectListUpdateEvent projectListUpdateEvent) {
@@ -66,7 +67,7 @@
 
     @Override
     public void run() {
-      forwarders.forEach(f -> f.updateProjectList(projectListUpdateEvent));
+      forwarders.forEach(f -> f.updateProjectList(this, projectListUpdateEvent));
     }
 
     @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/CacheEvictionForwarder.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/CacheEvictionForwarder.java
index 68921e6..2a18759 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/CacheEvictionForwarder.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/CacheEvictionForwarder.java
@@ -20,8 +20,9 @@
   /**
    * Forward a cache eviction event to the other master.
    *
+   * @param task that triggered the forwarding of the cache event.
    * @param cacheEvictionEvent the details of the cache eviction event.
    * @return true if successful, otherwise false.
    */
-  boolean evict(CacheEvictionEvent cacheEvictionEvent);
+  boolean evict(ForwarderTask task, CacheEvictionEvent cacheEvictionEvent);
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwarderTask.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwarderTask.java
new file mode 100644
index 0000000..329b5cb
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwarderTask.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 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.forwarder;
+
+public abstract class ForwarderTask implements Runnable {
+  private final Thread callerThread = Thread.currentThread();
+
+  public Thread getCallerThread() {
+    return callerThread;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/IndexEventForwarder.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/IndexEventForwarder.java
index 5c1f444..73cd9e8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/IndexEventForwarder.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/IndexEventForwarder.java
@@ -21,16 +21,18 @@
   /**
    * Publish an indexing event to the broker using interactive topic.
    *
+   * @param task that triggered the forwarding of the index event.
    * @param event the details of the index event.
    * @return true if successful, otherwise false.
    */
-  boolean index(IndexEvent event);
+  boolean index(ForwarderTask task, IndexEvent event);
 
   /**
    * Publish an indexing event to the broker using batch topic.
    *
+   * @param task that triggered the forwarding of the index event.
    * @param event the details of the index event.
    * @return true if successful, otherwise false.
    */
-  boolean batchIndex(IndexEvent event);
+  boolean batchIndex(ForwarderTask task, IndexEvent event);
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ProjectListUpdateForwarder.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ProjectListUpdateForwarder.java
index e40ff3f..b370b26 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ProjectListUpdateForwarder.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ProjectListUpdateForwarder.java
@@ -21,8 +21,9 @@
   /**
    * Forward an update the project list cache event to the other master.
    *
+   * @param task that triggered the forwarding of the project list event.
    * @param projectListUpdateEvent the content of project list update event
    * @return true if successful, otherwise false.
    */
-  boolean updateProjectList(ProjectListUpdateEvent projectListUpdateEvent);
+  boolean updateProjectList(ForwarderTask task, ProjectListUpdateEvent projectListUpdateEvent);
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerCacheEvictionForwarder.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerCacheEvictionForwarder.java
index b32e5ae..1e2a48f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerCacheEvictionForwarder.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerCacheEvictionForwarder.java
@@ -19,22 +19,21 @@
 import com.googlesource.gerrit.plugins.multisite.Configuration;
 import com.googlesource.gerrit.plugins.multisite.broker.BrokerApiWrapper;
 import com.googlesource.gerrit.plugins.multisite.forwarder.CacheEvictionForwarder;
+import com.googlesource.gerrit.plugins.multisite.forwarder.ForwarderTask;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.CacheEvictionEvent;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.EventTopic;
 
 @Singleton
-public class BrokerCacheEvictionForwarder implements CacheEvictionForwarder {
-  private final BrokerApiWrapper broker;
-  private final Configuration cfg;
+public class BrokerCacheEvictionForwarder extends BrokerForwarder
+    implements CacheEvictionForwarder {
 
   @Inject
   BrokerCacheEvictionForwarder(BrokerApiWrapper broker, Configuration cfg) {
-    this.broker = broker;
-    this.cfg = cfg;
+    super(broker, cfg);
   }
 
   @Override
-  public boolean evict(CacheEvictionEvent event) {
-    return broker.send(EventTopic.CACHE_TOPIC.topic(cfg), event);
+  public boolean evict(ForwarderTask task, CacheEvictionEvent event) {
+    return send(task, EventTopic.CACHE_TOPIC, event);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerForwarder.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerForwarder.java
new file mode 100644
index 0000000..da19fed
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerForwarder.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2020 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.forwarder.broker;
+
+import com.googlesource.gerrit.plugins.multisite.Configuration;
+import com.googlesource.gerrit.plugins.multisite.broker.BrokerApiWrapper;
+import com.googlesource.gerrit.plugins.multisite.forwarder.ForwarderTask;
+import com.googlesource.gerrit.plugins.multisite.forwarder.events.EventTopic;
+import com.googlesource.gerrit.plugins.multisite.forwarder.events.MultiSiteEvent;
+
+public abstract class BrokerForwarder {
+  private static final CharSequence HIGH_AVAILABILITY_PLUGIN = "/plugins/high-availability/";
+  private static final CharSequence HIGH_AVAILABILITY_FORWARDER = "Forwarded-Index-Event";
+
+  private final BrokerApiWrapper broker;
+  private final Configuration cfg;
+
+  protected BrokerForwarder(BrokerApiWrapper broker, Configuration cfg) {
+    this.broker = broker;
+    this.cfg = cfg;
+  }
+
+  protected boolean currentThreadBelongsToHighAvailabilityPlugin(ForwarderTask task) {
+    String currentThreadName = task.getCallerThread().getName();
+
+    return currentThreadName.contains(HIGH_AVAILABILITY_PLUGIN)
+        || currentThreadName.contains(HIGH_AVAILABILITY_FORWARDER);
+  }
+
+  protected boolean send(ForwarderTask task, EventTopic eventTopic, MultiSiteEvent event) {
+    // Events generated by the high-availability plugin should be
+    // discarded. Sending them around would cause infinite loops.
+    if (currentThreadBelongsToHighAvailabilityPlugin(task)) {
+      return true;
+    }
+
+    return broker.send(eventTopic.topic(cfg), event);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerIndexEventForwarder.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerIndexEventForwarder.java
index a86c62e..8321b94 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerIndexEventForwarder.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerIndexEventForwarder.java
@@ -17,27 +17,25 @@
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
 import com.googlesource.gerrit.plugins.multisite.broker.BrokerApiWrapper;
+import com.googlesource.gerrit.plugins.multisite.forwarder.ForwarderTask;
 import com.googlesource.gerrit.plugins.multisite.forwarder.IndexEventForwarder;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.EventTopic;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.IndexEvent;
 
-public class BrokerIndexEventForwarder implements IndexEventForwarder {
-  private final BrokerApiWrapper broker;
-  private final Configuration cfg;
+public class BrokerIndexEventForwarder extends BrokerForwarder implements IndexEventForwarder {
 
   @Inject
   BrokerIndexEventForwarder(BrokerApiWrapper broker, Configuration cfg) {
-    this.broker = broker;
-    this.cfg = cfg;
+    super(broker, cfg);
   }
 
   @Override
-  public boolean index(IndexEvent event) {
-    return broker.send(EventTopic.INDEX_TOPIC.topic(cfg), event);
+  public boolean index(ForwarderTask task, IndexEvent event) {
+    return send(task, EventTopic.INDEX_TOPIC, event);
   }
 
   @Override
-  public boolean batchIndex(IndexEvent event) {
-    return broker.send(EventTopic.BATCH_INDEX_TOPIC.topic(cfg), event);
+  public boolean batchIndex(ForwarderTask task, IndexEvent event) {
+    return send(task, EventTopic.BATCH_INDEX_TOPIC, event);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerProjectListUpdateForwarder.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerProjectListUpdateForwarder.java
index 34e0300..dc8139d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerProjectListUpdateForwarder.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerProjectListUpdateForwarder.java
@@ -20,22 +20,21 @@
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
 import com.googlesource.gerrit.plugins.multisite.broker.BrokerApiWrapper;
+import com.googlesource.gerrit.plugins.multisite.forwarder.ForwarderTask;
 import com.googlesource.gerrit.plugins.multisite.forwarder.ProjectListUpdateForwarder;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.ProjectListUpdateEvent;
 
 @Singleton
-public class BrokerProjectListUpdateForwarder implements ProjectListUpdateForwarder {
-  private final BrokerApiWrapper broker;
-  private final Configuration cfg;
+public class BrokerProjectListUpdateForwarder extends BrokerForwarder
+    implements ProjectListUpdateForwarder {
 
   @Inject
   BrokerProjectListUpdateForwarder(BrokerApiWrapper broker, Configuration cfg) {
-    this.broker = broker;
-    this.cfg = cfg;
+    super(broker, cfg);
   }
 
   @Override
-  public boolean updateProjectList(ProjectListUpdateEvent event) {
-    return broker.send(PROJECT_LIST_TOPIC.topic(cfg), event);
+  public boolean updateProjectList(ForwarderTask task, ProjectListUpdateEvent event) {
+    return send(task, PROJECT_LIST_TOPIC, event);
   }
 }
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 32b8af3..15b1e09 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
@@ -79,7 +79,7 @@
   @Override
   public Optional<ChangeNotes> getChangeNotes() {
     try (ManualRequestContext ctx = oneOffReqCtx.open()) {
-      this.changeNotes = Optional.ofNullable(changeFinder.findOne(changeId));
+      this.changeNotes = changeFinder.findOne(changeId);
       return changeNotes;
     }
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/index/IndexEventHandler.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/index/IndexEventHandler.java
index ed33efa..62359cb 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/index/IndexEventHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/index/IndexEventHandler.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
+import com.googlesource.gerrit.plugins.multisite.forwarder.ForwarderTask;
 import com.googlesource.gerrit.plugins.multisite.forwarder.IndexEventForwarder;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.AccountIndexEvent;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.ChangeIndexEvent;
@@ -130,7 +131,8 @@
     }
   }
 
-  abstract class IndexTask implements Runnable {
+  abstract class IndexTask extends ForwarderTask {
+
     @Override
     public void run() {
       queuedTasks.remove(this);
@@ -149,7 +151,7 @@
 
     @Override
     public void execute() {
-      forwarders.forEach(f -> f.index(changeIndexEvent));
+      forwarders.forEach(f -> f.index(this, changeIndexEvent));
     }
 
     @Override
@@ -180,7 +182,7 @@
 
     @Override
     public void execute() {
-      forwarders.forEach(f -> f.batchIndex(changeIndexEvent));
+      forwarders.forEach(f -> f.batchIndex(this, changeIndexEvent));
     }
 
     @Override
@@ -211,7 +213,7 @@
 
     @Override
     public void execute() {
-      forwarders.forEach(f -> f.index(accountIndexEvent));
+      forwarders.forEach(f -> f.index(this, accountIndexEvent));
     }
 
     @Override
@@ -242,7 +244,7 @@
 
     @Override
     public void execute() {
-      forwarders.forEach(f -> f.index(groupIndexEvent));
+      forwarders.forEach(f -> f.index(this, groupIndexEvent));
     }
 
     @Override
@@ -273,7 +275,7 @@
 
     @Override
     public void execute() {
-      forwarders.forEach(f -> f.index(projectIndexEvent));
+      forwarders.forEach(f -> f.index(this, projectIndexEvent));
     }
 
     @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/index/ProjectCheckerImpl.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/index/ProjectCheckerImpl.java
index 548ccc0..d9851f3 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/index/ProjectCheckerImpl.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/index/ProjectCheckerImpl.java
@@ -28,6 +28,6 @@
 
   @Override
   public boolean isProjectUpToDate(Project.NameKey projectName) {
-    return projectCache.get(projectName) != null;
+    return projectCache.get(projectName).isPresent();
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/RefUpdateValidator.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/RefUpdateValidator.java
index dcc9030..89c6792 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/RefUpdateValidator.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/RefUpdateValidator.java
@@ -244,7 +244,7 @@
   }
 
   protected Ref getCurrentRef(String refName) throws IOException {
-    return MoreObjects.firstNonNull(refDb.getRef(refName), nullRef(refName));
+    return MoreObjects.firstNonNull(refDb.findRef(refName), nullRef(refName));
   }
 
   public static class CloseableSet<T extends AutoCloseable> implements AutoCloseable {
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 481d288..4a34b7a 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
@@ -14,7 +14,6 @@
 
 package com.googlesource.gerrit.plugins.multisite.validation;
 
-import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -33,8 +32,6 @@
 import com.googlesource.gerrit.plugins.replication.ReplicationPushFilter;
 
 public class ValidationModule extends FactoryModule {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
   private final Configuration cfg;
 
   public ValidationModule(Configuration cfg) {
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 bf4b4cf..b86f8af 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
@@ -15,6 +15,8 @@
 package com.googlesource.gerrit.plugins.multisite.cache;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
@@ -59,7 +61,9 @@
     NewProjectCreatedListener.Event event = mock(NewProjectCreatedListener.Event.class);
     when(event.getProjectName()).thenReturn(projectName);
     handler.onNewProjectCreated(event);
-    verify(forwarder).updateProjectList(new ProjectListUpdateEvent(projectName, false));
+    verify(forwarder)
+        .updateProjectList(
+            any(ProjectListUpdateTask.class), eq(new ProjectListUpdateEvent(projectName, false)));
   }
 
   @Test
@@ -68,7 +72,9 @@
     ProjectDeletedListener.Event event = mock(ProjectDeletedListener.Event.class);
     when(event.getProjectName()).thenReturn(projectName);
     handler.onProjectDeleted(event);
-    verify(forwarder).updateProjectList(new ProjectListUpdateEvent(projectName, true));
+    verify(forwarder)
+        .updateProjectList(
+            any(ProjectListUpdateTask.class), eq(new ProjectListUpdateEvent(projectName, true)));
   }
 
   @Test
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
new file mode 100644
index 0000000..5e2fc05
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/BrokerForwarderTest.java
@@ -0,0 +1,132 @@
+// Copyright (C) 2020 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.forwarder;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import com.googlesource.gerrit.plugins.multisite.Configuration;
+import com.googlesource.gerrit.plugins.multisite.broker.BrokerApiWrapper;
+import com.googlesource.gerrit.plugins.multisite.forwarder.broker.BrokerForwarder;
+import com.googlesource.gerrit.plugins.multisite.forwarder.events.EventTopic;
+import com.googlesource.gerrit.plugins.multisite.forwarder.events.MultiSiteEvent;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.eclipse.jgit.lib.Config;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class BrokerForwarderTest {
+  private static final String HIGH_AVAILABILITY_PLUGIN = "/plugins/high-availability/";
+  private static final String HIGH_AVAILABILITY_FORWARDED = "Forwarded-Index-Event";
+  private static final long TEST_TIMEOUT_SEC = 5L;
+
+  @Mock private BrokerApiWrapper brokerMock;
+
+  private TestBrokerForwarder brokerForwarder;
+
+  private Configuration cfg;
+
+  private EventTopic testTopic;
+
+  private String testTopicName;
+
+  private TestEvent testEvent;
+
+  private ExecutorService executor;
+
+  public class TestBrokerForwarder extends BrokerForwarder {
+
+    TestBrokerForwarder() {
+      super(brokerMock, cfg);
+    }
+
+    public void send(ForwarderTask task, EventTopic eventTopic, TestEvent testEvent) {
+      super.send(task, eventTopic, testEvent);
+    }
+  }
+
+  public class TestEvent extends MultiSiteEvent {
+
+    protected TestEvent() {
+      super("test");
+    }
+  }
+
+  @Before
+  public void setup() {
+    cfg = new Configuration(new Config(), new Config());
+    testTopic = EventTopic.INDEX_TOPIC;
+    testTopicName = testTopic.topic(cfg);
+    testEvent = new TestEvent();
+    brokerForwarder = new TestBrokerForwarder();
+    executor = Executors.newSingleThreadExecutor();
+  }
+
+  @After
+  public void teardown() {
+    executor.shutdown();
+  }
+
+  @Test
+  public void shouldSendEventToBrokerFromGenericSourceThread() {
+    brokerForwarder.send(newForwarderTask(), testTopic, testEvent);
+    verify(brokerMock).send(eq(testTopicName), eq(testEvent));
+  }
+
+  @Test
+  public void shouldSkipEventFromHighAvailabilityPluginThread() {
+    brokerForwarder.send(newForwarderTask(HIGH_AVAILABILITY_PLUGIN), testTopic, testEvent);
+    verifyZeroInteractions(brokerMock);
+  }
+
+  @Test
+  public void shouldSkipEventFromHighAvailabilityPluginForwardedThread() {
+    brokerForwarder.send(newForwarderTask(HIGH_AVAILABILITY_FORWARDED), testTopic, testEvent);
+
+    verifyZeroInteractions(brokerMock);
+  }
+
+  private ForwarderTask newForwarderTask(String threadName) {
+    try {
+      return executor
+          .submit(
+              () -> {
+                Thread.currentThread().setName(threadName);
+                return newForwarderTask();
+              })
+          .get(TEST_TIMEOUT_SEC, TimeUnit.SECONDS);
+    } catch (InterruptedException | ExecutionException | TimeoutException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private ForwarderTask newForwarderTask() {
+    return new ForwarderTask() {
+
+      @Override
+      public void run() {}
+    };
+  }
+}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedCacheEvictionHandlerTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedCacheEvictionHandlerTest.java
index 747da79..90ae8da 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedCacheEvictionHandlerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedCacheEvictionHandlerTest.java
@@ -15,11 +15,10 @@
 package com.googlesource.gerrit.plugins.multisite.forwarder;
 
 import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
 
 import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.googlesource.gerrit.plugins.multisite.cache.Constants;
@@ -30,19 +29,19 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.stubbing.Answer;
 
 @RunWith(MockitoJUnitRunner.class)
 public class ForwardedCacheEvictionHandlerTest {
 
   @Rule public ExpectedException exception = ExpectedException.none();
   @Mock private DynamicMap<Cache<?, ?>> cacheMapMock;
-  @Mock private Cache<?, ?> cacheMock;
+  private Cache<Object, Object> cacheUnderTest;
   private ForwardedCacheEvictionHandler handler;
 
   @Before
   public void setUp() throws Exception {
     handler = new ForwardedCacheEvictionHandler(cacheMapMock);
+    cacheUnderTest = CacheBuilder.newBuilder().build();
   }
 
   @Test
@@ -58,65 +57,22 @@
   @Test
   public void testSuccessfulCacheEviction() throws Exception {
     CacheEntry entry = new CacheEntry(Constants.GERRIT, Constants.ACCOUNTS, Account.id(123));
-    doReturn(cacheMock).when(cacheMapMock).get(entry.getPluginName(), entry.getCacheName());
+    cacheUnderTest.put(entry.getKey(), new Object());
+    doReturn(cacheUnderTest).when(cacheMapMock).get(entry.getPluginName(), entry.getCacheName());
 
     handler.evict(entry);
-    verify(cacheMock).invalidate(entry.getKey());
+    assertThat(cacheUnderTest.getIfPresent(entry.getKey())).isNull();
   }
 
   @Test
   public void testSuccessfulProjectListCacheEviction() throws Exception {
     CacheEntry entry = new CacheEntry(Constants.GERRIT, Constants.PROJECT_LIST, null);
-    doReturn(cacheMock).when(cacheMapMock).get(entry.getPluginName(), entry.getCacheName());
+    cacheUnderTest.put("foo", new Object());
+    cacheUnderTest.put("bar", new Object());
+    doReturn(cacheUnderTest).when(cacheMapMock).get(entry.getPluginName(), entry.getCacheName());
 
     handler.evict(entry);
-    verify(cacheMock).invalidateAll();
-  }
-
-  @Test
-  public void shouldSetAndUnsetForwardedContext() throws Exception {
-    CacheEntry entry = new CacheEntry(Constants.GERRIT, Constants.ACCOUNTS, Account.id(456));
-    doReturn(cacheMock).when(cacheMapMock).get(entry.getPluginName(), entry.getCacheName());
-
-    // this doAnswer is to allow to assert that context is set to forwarded
-    // while cache eviction is called.
-    doAnswer(
-            (Answer<Void>)
-                invocation -> {
-                  assertThat(Context.isForwardedEvent()).isTrue();
-                  return null;
-                })
-        .when(cacheMock)
-        .invalidate(entry.getKey());
-
-    assertThat(Context.isForwardedEvent()).isFalse();
-    handler.evict(entry);
-    assertThat(Context.isForwardedEvent()).isFalse();
-
-    verify(cacheMock).invalidate(entry.getKey());
-  }
-
-  @Test
-  public void shouldSetAndUnsetForwardedContextEvenIfExceptionIsThrown() throws Exception {
-    CacheEntry entry = new CacheEntry(Constants.GERRIT, Constants.ACCOUNTS, Account.id(789));
-    doReturn(cacheMock).when(cacheMapMock).get(entry.getPluginName(), entry.getCacheName());
-
-    doAnswer(
-            (Answer<Void>)
-                invocation -> {
-                  assertThat(Context.isForwardedEvent()).isTrue();
-                  throw new RuntimeException();
-                })
-        .when(cacheMock)
-        .invalidate(entry.getKey());
-
-    assertThat(Context.isForwardedEvent()).isFalse();
-    try {
-      handler.evict(entry);
-    } catch (RuntimeException e) {
-    }
-    assertThat(Context.isForwardedEvent()).isFalse();
-
-    verify(cacheMock).invalidate(entry.getKey());
+    assertThat(cacheUnderTest.getIfPresent("foo")).isNull();
+    assertThat(cacheUnderTest.getIfPresent("bar")).isNull();
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/MultiSiteRefUpdateTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/MultiSiteRefUpdateTest.java
index 9e75c8f..d9e5c07 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/MultiSiteRefUpdateTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/MultiSiteRefUpdateTest.java
@@ -92,7 +92,7 @@
   }
 
   @Test
-  public void newUpdateShouldIncreaseRefUpdateFailureCountWhenFailing() throws IOException {
+  public void newUpdateShouldIncreaseRefUpdateFailureCountWhenFailing() {
 
     doReturn(false).when(sharedRefDb).isUpToDate(A_TEST_PROJECT_NAME_KEY, oldRef);
 
@@ -106,8 +106,7 @@
   }
 
   @Test
-  public void newUpdateShouldNotIncreaseSplitBrainPreventedCounterIfFailingSharedDbPostUpdate()
-      throws IOException {
+  public void newUpdateShouldNotIncreaseSplitBrainPreventedCounterIfFailingSharedDbPostUpdate() {
 
     doReturn(true).when(sharedRefDb).isUpToDate(A_TEST_PROJECT_NAME_KEY, oldRef);
     doReturn(false)
@@ -124,8 +123,7 @@
   }
 
   @Test
-  public void newUpdateShouldtIncreaseSplitBrainCounterIfFailingSharedDbPostUpdate()
-      throws IOException {
+  public void newUpdateShouldtIncreaseSplitBrainCounterIfFailingSharedDbPostUpdate() {
 
     doReturn(true).when(sharedRefDb).isUpToDate(A_TEST_PROJECT_NAME_KEY, oldRef);
     doReturn(false)
@@ -159,7 +157,7 @@
   }
 
   @Test
-  public void deleteShouldIncreaseRefUpdateFailureCountWhenFailing() throws IOException {
+  public void deleteShouldIncreaseRefUpdateFailureCountWhenFailing() {
 
     doReturn(false).when(sharedRefDb).isUpToDate(A_TEST_PROJECT_NAME_KEY, oldRef);
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/RefUpdateValidatorTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/RefUpdateValidatorTest.java
index eefb685..3f4d1bb 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/RefUpdateValidatorTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/RefUpdateValidatorTest.java
@@ -23,6 +23,7 @@
 
 import com.google.gerrit.entities.Project;
 import com.googlesource.gerrit.plugins.multisite.SharedRefDatabaseWrapper;
+import com.googlesource.gerrit.plugins.multisite.SharedRefLogger;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.DefaultSharedRefEnforcement;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.RefFixture;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.SharedDbSplitBrainException;
@@ -44,6 +45,8 @@
 
   @Mock SharedRefDatabaseWrapper sharedRefDb;
 
+  @Mock SharedRefLogger sharedRefLogger;
+
   @Mock RefDatabase localRefDb;
 
   @Mock ValidationMetrics validationMetrics;
@@ -64,21 +67,24 @@
     newUpdateRef = newRef(refName, AN_OBJECT_ID_2);
     localRef = newRef(refName, AN_OBJECT_ID_3);
 
-    doReturn(localRef).when(localRefDb).getRef(refName);
+    doReturn(localRef).when(localRefDb).findRef(refName);
     doReturn(localRef).when(localRefDb).exactRef(refName);
     doReturn(oldUpdateRef).when(refUpdate).getRef();
     doReturn(newUpdateRef.getObjectId()).when(refUpdate).getNewObjectId();
     doReturn(refName).when(refUpdate).getName();
     lenient().doReturn(oldUpdateRef.getObjectId()).when(refUpdate).getOldObjectId();
 
-    refUpdateValidator =
-        new RefUpdateValidator(
-            sharedRefDb,
-            validationMetrics,
-            defaultRefEnforcement,
-            new DummyLockWrapper(),
-            A_TEST_PROJECT_NAME,
-            localRefDb);
+    refUpdateValidator = newRefUpdateValidator(sharedRefDb);
+  }
+
+  @Test
+  public void validationShouldSucceedWhenSharedRefDbIsNoop() throws Exception {
+    SharedRefDatabaseWrapper noopSharedRefDbWrapper = new SharedRefDatabaseWrapper(sharedRefLogger);
+
+    Result result =
+        newRefUpdateValidator(noopSharedRefDbWrapper)
+            .executeRefUpdate(refUpdate, () -> RefUpdate.Result.NEW);
+    assertThat(result).isEqualTo(RefUpdate.Result.NEW);
   }
 
   @Test
@@ -112,7 +118,7 @@
     doReturn(true)
         .when(sharedRefDb)
         .compareAndPut(A_TEST_PROJECT_NAME_KEY, localRef, ObjectId.zeroId());
-    doReturn(localRef).doReturn(null).when(localRefDb).getRef(refName);
+    doReturn(localRef).doReturn(null).when(localRefDb).findRef(refName);
 
     Result result = refUpdateValidator.executeRefUpdate(refUpdate, () -> RefUpdate.Result.FORCED);
 
@@ -131,7 +137,7 @@
     doReturn(true)
         .when(sharedRefDb)
         .compareAndPut(A_TEST_PROJECT_NAME_KEY, localNullRef, newUpdateRef.getObjectId());
-    doReturn(localNullRef).doReturn(newUpdateRef).when(localRefDb).getRef(refName);
+    doReturn(localNullRef).doReturn(newUpdateRef).when(localRefDb).findRef(refName);
 
     Result result = refUpdateValidator.executeRefUpdate(refUpdate, () -> RefUpdate.Result.NEW);
 
@@ -186,4 +192,14 @@
         .compareAndPut(any(Project.NameKey.class), any(Ref.class), any(ObjectId.class));
     assertThat(result).isEqualTo(RefUpdate.Result.LOCK_FAILURE);
   }
+
+  private RefUpdateValidator newRefUpdateValidator(SharedRefDatabaseWrapper refDbWrapper) {
+    return new RefUpdateValidator(
+        refDbWrapper,
+        validationMetrics,
+        defaultRefEnforcement,
+        new DummyLockWrapper(),
+        A_TEST_PROJECT_NAME,
+        localRefDb);
+  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/MultisiteReplicationPushFilterTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/MultisiteReplicationPushFilterTest.java
index 7e4593b..f2b57a1 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/MultisiteReplicationPushFilterTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/MultisiteReplicationPushFilterTest.java
@@ -68,7 +68,7 @@
   private TestRepository<InMemoryRepository> repo;
 
   @Before
-  public void setUp() throws Exception {
+  public void setupTestRepo() throws Exception {
     InMemoryRepository inMemoryRepo =
         gitRepositoryManager.createRepository(A_TEST_PROJECT_NAME_KEY);
     repo = new TestRepository<>(inMemoryRepo);