Merge branch 'stable-3.4' into stable-3.5

* stable-3.4:
  Specify cache.threads = 0 in the multi-site config documentation

Change-Id: Ibdf826516b3fd253693558bf6644124efbfbd555
diff --git a/e2e-tests/test.sh b/e2e-tests/test.sh
index 3ecb9fd..12aca41 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.4
+GERRIT_BRANCH=stable-3.5
 GERRIT_CI=https://archive-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
diff --git a/setup_local_env/configs/gerrit.config b/setup_local_env/configs/gerrit.config
index 8a176f2..20075d7 100644
--- a/setup_local_env/configs/gerrit.config
+++ b/setup_local_env/configs/gerrit.config
@@ -39,8 +39,6 @@
     directory = cache
 [plugins]
     allowRemoteAdmin = true
-[plugin "websession-flatfile"]
-    directory = $FAKE_NFS
 [plugin "events-kafka"]
     sendAsync = true
     bootstrapServers = $BROKER_HOST:$BROKER_PORT
diff --git a/setup_local_env/setup.sh b/setup_local_env/setup.sh
index 247a721..5180473 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.4
+GERRIT_BRANCH=stable-3.5
 GERRIT_CI=https://archive-ci.gerritforge.com/view/Plugins-$GERRIT_BRANCH/job
 LAST_BUILD=lastSuccessfulBuild/artifact/bazel-bin/plugins
 
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 2d67eab..5c443e7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/Configuration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/Configuration.java
@@ -65,6 +65,8 @@
   private static final String NUM_STRIPED_LOCKS = "numStripedLocks";
   private static final int DEFAULT_NUM_STRIPED_LOCKS = 10;
 
+  private static final long DEFALULT_STREAM_EVENT_PUBLISH_TIMEOUT = 30000;
+
   private final Supplier<Cache> cache;
   private final Supplier<Event> event;
   private final Supplier<Index> index;
@@ -227,6 +229,17 @@
     }
   }
 
+  private static long getLong(
+      Supplier<Config> cfg, String section, String subSection, String name, long defaultValue) {
+    try {
+      return cfg.get().getLong(section, subSection, name, defaultValue);
+    } catch (IllegalArgumentException e) {
+      log.error("invalid value for {}; using default value {}", name, defaultValue);
+      log.debug("Failed to retrieve long value: {}", e.getMessage(), e);
+      return defaultValue;
+    }
+  }
+
   public static class Projects {
     public static final String SECTION = "projects";
     public static final String PATTERN_KEY = "pattern";
@@ -330,15 +343,28 @@
 
   public static class Broker {
     static final String BROKER_SECTION = "broker";
+    static final String STREAM_EVENT_PUBLISH_TIMEOUT = "streamEventPublishTimeoutMs";
     private final Config cfg;
+    private long streamEventPublishTimeout;
 
     Broker(Supplier<Config> cfgSupplier) {
       cfg = cfgSupplier.get();
+      streamEventPublishTimeout =
+          getLong(
+              cfgSupplier,
+              BROKER_SECTION,
+              null,
+              STREAM_EVENT_PUBLISH_TIMEOUT,
+              DEFALULT_STREAM_EVENT_PUBLISH_TIMEOUT);
     }
 
     public String getTopic(String topicKey, String defValue) {
       return MoreObjects.firstNonNull(cfg.getString(BROKER_SECTION, null, topicKey), defValue);
     }
+
+    public long getStreamEventPublishTimeout() {
+      return streamEventPublishTimeout;
+    }
   }
 
   public static class ReplicationFilter {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/broker/BrokerApiWrapper.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/broker/BrokerApiWrapper.java
index f58efa7..88422e1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/broker/BrokerApiWrapper.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/broker/BrokerApiWrapper.java
@@ -22,11 +22,11 @@
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
 import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.server.config.GerritInstanceId;
 import com.google.gerrit.server.events.Event;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.multisite.MessageLogger;
 import com.googlesource.gerrit.plugins.multisite.MessageLogger.Direction;
-import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -39,17 +39,20 @@
   private final DynamicItem<BrokerApi> apiDelegate;
   private final BrokerMetrics metrics;
   private final MessageLogger msgLog;
+  private final String nodeInstanceId;
 
   @Inject
   public BrokerApiWrapper(
       @BrokerExecutor Executor executor,
       DynamicItem<BrokerApi> apiDelegate,
       BrokerMetrics metrics,
-      MessageLogger msgLog) {
+      MessageLogger msgLog,
+      @GerritInstanceId String instanceId) {
     this.apiDelegate = apiDelegate;
     this.executor = executor;
     this.metrics = metrics;
     this.msgLog = msgLog;
+    this.nodeInstanceId = instanceId;
   }
 
   public boolean sendSync(String topic, Event event) {
@@ -70,7 +73,7 @@
   @Override
   public ListenableFuture<Boolean> send(String topic, Event message) {
     SettableFuture<Boolean> resultFuture = SettableFuture.create();
-    if (Context.isForwardedEvent()) {
+    if (!nodeInstanceId.equals(message.instanceId)) {
       resultFuture.set(true);
       return resultFuture;
     }
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 44f8417..6a2c7a5 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
@@ -14,7 +14,6 @@
 
 package com.googlesource.gerrit.plugins.multisite.cache;
 
-import com.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter;
 import com.google.gerrit.extensions.events.NewProjectCreatedListener;
 import com.google.gerrit.extensions.events.ProjectDeletedListener;
 import com.google.gerrit.extensions.events.ProjectEvent;
@@ -33,18 +32,15 @@
 
   private final DynamicSet<ProjectListUpdateForwarder> forwarders;
   private final Executor executor;
-  private final ProjectsFilter projectsFilter;
   private final String instanceId;
 
   @Inject
   public ProjectListUpdateHandler(
       DynamicSet<ProjectListUpdateForwarder> forwarders,
       @CacheExecutor Executor executor,
-      ProjectsFilter filter,
       @GerritInstanceId String instanceId) {
     this.forwarders = forwarders;
     this.executor = executor;
-    this.projectsFilter = filter;
     this.instanceId = instanceId;
   }
 
@@ -61,7 +57,7 @@
   }
 
   private void process(ProjectEvent event, boolean delete) {
-    if (!Context.isForwardedEvent() && projectsFilter.matches(event.getProjectName())) {
+    if (!Context.isForwardedEvent()) {
       executor.execute(
           new ProjectListUpdateTask(
               new ProjectListUpdateEvent(event.getProjectName(), delete, instanceId)));
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/event/EventExecutor.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventExecutor.java
deleted file mode 100644
index 07bbbf7..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventExecutor.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2015 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.event;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import com.google.inject.BindingAnnotation;
-import java.lang.annotation.Retention;
-
-@Retention(RUNTIME)
-@BindingAnnotation
-@interface EventExecutor {}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventExecutorProvider.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventExecutorProvider.java
deleted file mode 100644
index e4979f9..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventExecutorProvider.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2015 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.event;
-
-import com.google.gerrit.server.git.WorkQueue;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.googlesource.gerrit.plugins.multisite.ExecutorProvider;
-
-@Singleton
-class EventExecutorProvider extends ExecutorProvider {
-
-  @Inject
-  EventExecutorProvider(WorkQueue workQueue) {
-    super(workQueue, 1, "Forward-Stream-Event");
-  }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventHandler.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventHandler.java
deleted file mode 100644
index b2efb80..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventHandler.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (C) 2015 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.event;
-
-import com.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.server.events.Event;
-import com.google.gerrit.server.events.EventListener;
-import com.google.gerrit.server.events.ProjectEvent;
-import com.google.inject.Inject;
-import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
-import com.googlesource.gerrit.plugins.multisite.forwarder.StreamEventForwarder;
-import java.util.concurrent.Executor;
-
-class EventHandler implements EventListener {
-  private final Executor executor;
-  private final DynamicSet<StreamEventForwarder> forwarders;
-  private final ProjectsFilter projectsFilter;
-
-  @Inject
-  EventHandler(
-      DynamicSet<StreamEventForwarder> forwarders,
-      @EventExecutor Executor executor,
-      ProjectsFilter projectsFilter) {
-    this.forwarders = forwarders;
-    this.executor = executor;
-    this.projectsFilter = projectsFilter;
-  }
-
-  @Override
-  public void onEvent(Event event) {
-    if (!Context.isForwardedEvent() && event instanceof ProjectEvent) {
-      if (projectsFilter.matches(event)) {
-        executor.execute(new EventTask(event));
-      }
-    }
-  }
-
-  class EventTask implements Runnable {
-    private final Event event;
-
-    EventTask(Event event) {
-      this.event = event;
-    }
-
-    @Override
-    public void run() {
-      forwarders.forEach(f -> f.send(event));
-    }
-
-    @Override
-    public String toString() {
-      return String.format("Send event '%s' to target instance", event.type);
-    }
-  }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventModule.java
index 5f16210..5e70aca 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventModule.java
@@ -14,37 +14,41 @@
 
 package com.googlesource.gerrit.plugins.multisite.event;
 
+import com.gerritforge.gerrit.eventbroker.publisher.StreamEventPublisherConfig;
+import com.gerritforge.gerrit.eventbroker.publisher.StreamEventPublisherModule;
 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;
+import com.google.inject.Inject;
 import com.google.inject.multibindings.OptionalBinder;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
+import com.googlesource.gerrit.plugins.multisite.forwarder.events.EventTopic;
 import com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate;
 import com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdateImpl;
-import java.util.concurrent.Executor;
 
 public class EventModule extends LifecycleModule {
+  private final Configuration configuration;
 
-  private final Configuration config;
-
-  public EventModule(Configuration config) {
-    this.config = config;
+  @Inject
+  public EventModule(Configuration configuration) {
+    this.configuration = configuration;
   }
 
   @Override
   protected void configure() {
-    bind(Executor.class).annotatedWith(EventExecutor.class).toProvider(EventExecutorProvider.class);
-    listener().to(EventExecutorProvider.class);
-    DynamicSet.bind(binder(), EventListener.class).to(EventHandler.class);
+    bind(StreamEventPublisherConfig.class)
+        .toInstance(
+            new StreamEventPublisherConfig(
+                EventTopic.STREAM_EVENT_TOPIC.topic(configuration),
+                configuration.broker().getStreamEventPublishTimeout()));
+
+    install(new StreamEventPublisherModule());
+
     OptionalBinder<ProjectVersionRefUpdate> projectVersionRefUpdateBinder =
         OptionalBinder.newOptionalBinder(binder(), ProjectVersionRefUpdate.class);
-    if (config.getSharedRefDbConfiguration().getSharedRefDb().isEnabled()) {
+    if (configuration.getSharedRefDbConfiguration().getSharedRefDb().isEnabled()) {
       DynamicSet.bind(binder(), EventListener.class).to(ProjectVersionRefUpdateImpl.class);
-      projectVersionRefUpdateBinder
-          .setBinding()
-          .to(ProjectVersionRefUpdateImpl.class)
-          .in(Scopes.SINGLETON);
+      projectVersionRefUpdateBinder.setBinding().to(ProjectVersionRefUpdateImpl.class);
     }
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedEventHandler.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedEventHandler.java
index dcfd1b0..e2e3954 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedEventHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedEventHandler.java
@@ -51,11 +51,8 @@
    */
   public void dispatch(Event event) throws PermissionBackendException {
     try (ManualRequestContext ctx = oneOffCtx.open()) {
-      Context.setForwardedEvent(true);
       log.debug("dispatching event {}", event.getType());
       dispatcher.get().postEvent(event);
-    } finally {
-      Context.unsetForwardedEvent();
     }
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwarderModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwarderModule.java
index ca17004..1d36e23 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwarderModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwarderModule.java
@@ -24,6 +24,5 @@
     DynamicSet.setOf(binder(), CacheEvictionForwarder.class);
     DynamicSet.setOf(binder(), IndexEventForwarder.class);
     DynamicSet.setOf(binder(), ProjectListUpdateForwarder.class);
-    DynamicSet.setOf(binder(), StreamEventForwarder.class);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/StreamEventForwarder.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/StreamEventForwarder.java
deleted file mode 100644
index 79a9af2..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/StreamEventForwarder.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2019 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 com.google.gerrit.server.events.Event;
-
-public interface StreamEventForwarder {
-  /**
-   * Forward a stream event to the other master.
-   *
-   * @param event the event to forward.
-   * @return true if successful, otherwise false.
-   */
-  boolean send(Event event);
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerForwarderModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerForwarderModule.java
index 6bd6437..d6c5658 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerForwarderModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerForwarderModule.java
@@ -19,7 +19,6 @@
 import com.googlesource.gerrit.plugins.multisite.forwarder.CacheEvictionForwarder;
 import com.googlesource.gerrit.plugins.multisite.forwarder.IndexEventForwarder;
 import com.googlesource.gerrit.plugins.multisite.forwarder.ProjectListUpdateForwarder;
-import com.googlesource.gerrit.plugins.multisite.forwarder.StreamEventForwarder;
 
 public class BrokerForwarderModule extends LifecycleModule {
   @Override
@@ -28,6 +27,5 @@
     DynamicSet.bind(binder(), CacheEvictionForwarder.class).to(BrokerCacheEvictionForwarder.class);
     DynamicSet.bind(binder(), ProjectListUpdateForwarder.class)
         .to(BrokerProjectListUpdateForwarder.class);
-    DynamicSet.bind(binder(), StreamEventForwarder.class).to(BrokerStreamEventForwarder.class);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerStreamEventForwarder.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerStreamEventForwarder.java
deleted file mode 100644
index 8a020fc..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/broker/BrokerStreamEventForwarder.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2019 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.google.gerrit.server.events.Event;
-import com.google.inject.Inject;
-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.StreamEventForwarder;
-import com.googlesource.gerrit.plugins.multisite.forwarder.events.EventTopic;
-
-@Singleton
-public class BrokerStreamEventForwarder implements StreamEventForwarder {
-  private final BrokerApiWrapper broker;
-  private final Configuration cfg;
-
-  @Inject
-  BrokerStreamEventForwarder(BrokerApiWrapper broker, Configuration cfg) {
-    this.broker = broker;
-    this.cfg = cfg;
-  }
-
-  @Override
-  public boolean send(Event event) {
-    return broker.sendSync(EventTopic.STREAM_EVENT_TOPIC.topic(cfg), event);
-  }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/events/EventTopic.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/events/EventTopic.java
index 4e7a781..3255bcf 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/events/EventTopic.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/events/EventTopic.java
@@ -21,7 +21,7 @@
   BATCH_INDEX_TOPIC("GERRIT.EVENT.BATCH.INDEX", "batchIndexEvent"),
   CACHE_TOPIC("GERRIT.EVENT.CACHE", "cacheEvent"),
   PROJECT_LIST_TOPIC("GERRIT.EVENT.PROJECT.LIST", "projectListEvent"),
-  STREAM_EVENT_TOPIC("GERRIT.EVENT.STREAM", "streamEvent");
+  STREAM_EVENT_TOPIC("gerrit", "streamEvent");
 
   private final String topic;
   private final String aliasKey;
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..6e8fa15 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
@@ -14,10 +14,7 @@
 
 package com.googlesource.gerrit.plugins.multisite.index;
 
-import com.google.gerrit.entities.Change;
-import com.google.gerrit.entities.HumanComment;
 import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.change.ChangeFinder;
 import com.google.gerrit.server.config.GerritInstanceId;
 import com.google.gerrit.server.git.GitRepositoryManager;
@@ -28,7 +25,6 @@
 import com.google.inject.assistedinject.Assisted;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.ChangeIndexEvent;
 import java.io.IOException;
-import java.sql.Timestamp;
 import java.util.Objects;
 import java.util.Optional;
 import org.eclipse.jgit.errors.MissingObjectException;
@@ -42,7 +38,6 @@
 public class ChangeCheckerImpl implements ChangeChecker {
   private static final Logger log = LoggerFactory.getLogger(ChangeCheckerImpl.class);
   private final GitRepositoryManager gitRepoMgr;
-  private final CommentsUtil commentsUtil;
   private final OneOffRequestContext oneOffReqCtx;
   private final String changeId;
   private final ChangeFinder changeFinder;
@@ -57,14 +52,12 @@
   @Inject
   public ChangeCheckerImpl(
       GitRepositoryManager gitRepoMgr,
-      CommentsUtil commentsUtil,
       ChangeFinder changeFinder,
       OneOffRequestContext oneOffReqCtx,
       @GerritInstanceId String instanceId,
       @Assisted String changeId) {
     this.changeFinder = changeFinder;
     this.gitRepoMgr = gitRepoMgr;
-    this.commentsUtil = commentsUtil;
     this.oneOffReqCtx = oneOffReqCtx;
     this.changeId = changeId;
     this.instanceId = instanceId;
@@ -177,20 +170,6 @@
   }
 
   private Optional<Long> computeLastChangeTs() {
-    return getChangeNotes().map(notes -> getTsFromChangeAndDraftComments(notes));
-  }
-
-  private long getTsFromChangeAndDraftComments(ChangeNotes notes) {
-    Change change = notes.getChange();
-    Timestamp changeTs = change.getLastUpdatedOn();
-    try {
-      for (HumanComment comment : commentsUtil.draftByChange(changeNotes.get())) {
-        Timestamp commentTs = comment.writtenOn;
-        changeTs = commentTs.after(changeTs) ? commentTs : changeTs;
-      }
-    } catch (StorageException e) {
-      log.warn("Unable to access draft comments for change {}", change, e);
-    }
-    return changeTs.getTime() / 1000;
+    return getChangeNotes().map(notes -> notes.getChange().getLastUpdatedOn().getTime() / 1000);
   }
 }
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 02f1b1c..ee16b07 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
@@ -14,7 +14,6 @@
 
 package com.googlesource.gerrit.plugins.multisite.index;
 
-import com.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter;
 import com.google.common.base.Objects;
 import com.google.gerrit.extensions.events.AccountIndexedListener;
 import com.google.gerrit.extensions.events.ChangeIndexedListener;
@@ -47,7 +46,6 @@
   private final DynamicSet<IndexEventForwarder> forwarders;
   private final Set<IndexTask> queuedTasks = Collections.newSetFromMap(new ConcurrentHashMap<>());
   private final ChangeCheckerImpl.Factory changeChecker;
-  private final ProjectsFilter projectsFilter;
   private final GroupChecker groupChecker;
   private final String instanceId;
 
@@ -56,13 +54,11 @@
       @IndexExecutor Executor executor,
       DynamicSet<IndexEventForwarder> forwarders,
       ChangeCheckerImpl.Factory changeChecker,
-      ProjectsFilter projectsFilter,
       GroupChecker groupChecker,
       @GerritInstanceId String instanceId) {
     this.forwarders = forwarders;
     this.executor = executor;
     this.changeChecker = changeChecker;
-    this.projectsFilter = projectsFilter;
     this.groupChecker = groupChecker;
     this.instanceId = instanceId;
   }
@@ -101,7 +97,7 @@
 
   @Override
   public void onProjectIndexed(String projectName) {
-    if (!Context.isForwardedEvent() && projectsFilter.matches(projectName)) {
+    if (!Context.isForwardedEvent()) {
       IndexProjectTask task = new IndexProjectTask(new ProjectIndexEvent(projectName, instanceId));
       if (queuedTasks.add(task)) {
         executor.execute(task);
@@ -110,7 +106,7 @@
   }
 
   private void executeIndexChangeTask(String projectName, int id) {
-    if (!Context.isForwardedEvent() && projectsFilter.matches(projectName)) {
+    if (!Context.isForwardedEvent()) {
       ChangeChecker checker = changeChecker.create(projectName + "~" + id);
 
       try {
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 d851420..3fc57ae 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
@@ -86,7 +86,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();
     }
   }
@@ -157,13 +157,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();
     }
   }
@@ -194,7 +194,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 e07fef0..4d2515e 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
@@ -18,7 +18,6 @@
 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 
 import com.gerritforge.gerrit.globalrefdb.GlobalRefDbSystemError;
-import com.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter;
 import com.gerritforge.gerrit.globalrefdb.validation.SharedRefDatabaseWrapper;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableSet;
@@ -26,14 +25,15 @@
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.Project.NameKey;
 import com.google.gerrit.entities.RefNames;
+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.RefUpdatedEvent;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.multisite.ProjectVersionLogger;
-import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Optional;
@@ -44,6 +44,7 @@
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
 
+@Singleton
 public class ProjectVersionRefUpdateImpl implements EventListener, ProjectVersionRefUpdate {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
   private static final Set<RefUpdate.Result> SUCCESSFUL_RESULTS =
@@ -52,7 +53,7 @@
   private final GitRepositoryManager gitRepositoryManager;
   private final GitReferenceUpdated gitReferenceUpdated;
   private final ProjectVersionLogger verLogger;
-  private final ProjectsFilter projectsFilter;
+  private final String nodeInstanceId;
 
   protected final SharedRefDatabaseWrapper sharedRefDb;
 
@@ -62,22 +63,20 @@
       SharedRefDatabaseWrapper sharedRefDb,
       GitReferenceUpdated gitReferenceUpdated,
       ProjectVersionLogger verLogger,
-      ProjectsFilter projectsFilter) {
+      @GerritInstanceId String nodeInstanceId) {
     this.gitRepositoryManager = gitRepositoryManager;
     this.sharedRefDb = sharedRefDb;
     this.gitReferenceUpdated = gitReferenceUpdated;
     this.verLogger = verLogger;
-    this.projectsFilter = projectsFilter;
+    this.nodeInstanceId = nodeInstanceId;
   }
 
   @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 (!Context.isForwardedEvent() && event instanceof RefUpdatedEvent) {
-      if (projectsFilter.matches(event)) {
-        updateProducerProjectVersionUpdate((RefUpdatedEvent) event);
-      }
+    if (nodeInstanceId.equals(event.instanceId) && event instanceof RefUpdatedEvent) {
+      updateProducerProjectVersionUpdate((RefUpdatedEvent) event);
     }
   }
 
@@ -116,8 +115,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());
     }
   }
 
@@ -136,6 +135,7 @@
     return newId;
   }
 
+  @SuppressWarnings("FloggerLogString")
   private boolean updateSharedProjectVersion(Project.NameKey projectNameKey, Long newVersion)
       throws SharedProjectVersionUpdateException {
 
@@ -147,15 +147,13 @@
     try {
       if (sharedVersion.isPresent() && sharedVersion.get() >= newVersion) {
         logger.atWarning().log(
-            String.format(
-                "NOT Updating project %s value=%d in shared ref-db because is more recent than the local value=%d",
-                projectNameKey.get(), newVersion, sharedVersion.get()));
+            "NOT Updating project %s value=%d in shared ref-db because is more recent than the local value=%d",
+            projectNameKey.get(), newVersion, sharedVersion.get());
         return false;
       }
 
       logger.atFine().log(
-          String.format(
-              "Updating shared project %s value to %d", projectNameKey.get(), newVersion));
+          "Updating shared project %s value to %d", projectNameKey.get(), newVersion);
 
       updateProjectVersionValue(projectNameKey, newVersion, sharedVersion);
       return true;
@@ -250,6 +248,7 @@
     }
   }
 
+  @SuppressWarnings("FloggerLogString")
   private Optional<RefUpdate> updateLocalProjectVersion(
       Project.NameKey projectNameKey, long newVersionNumber)
       throws LocalProjectVersionUpdateException {
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index e451cb3..2d73320 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -67,9 +67,9 @@
 :   Name of the topic to use for publishing indexing events
     Defaults to GERRIT.EVENT.INDEX
 
-```broker.streamEventTopic```
-:   Name of the topic to use for publishing stream events
-    Defaults to GERRIT.EVENT.STREAM
+`broker.streamEventTopic`
+:   Name of the topic to use for publishing all stream events.
+    Default: gerrit
 
 ```broker.cacheEventTopic```
 :   Name of the topic to use for publishing cache eviction events
@@ -79,6 +79,10 @@
 :   Name of the topic to use for publishing cache eviction events
     Defaults to GERRIT.EVENT.PROJECT.LIST
 
+```broker.streamEventPublishTimeoutMs```
+:   The timeout in milliseconds for publishing stream events.
+    Defaults to 30000 (30 seconds).
+
 **NOTE**: All broker settings are ignored when all of the `cache`,
 `index` or `event` synchronization is disabled.
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/broker/BrokerApiWrapperTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/broker/BrokerApiWrapperTest.java
index 7d1751c..50f55b2 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/broker/BrokerApiWrapperTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/broker/BrokerApiWrapperTest.java
@@ -22,6 +22,7 @@
 
 @RunWith(MockitoJUnitRunner.class)
 public class BrokerApiWrapperTest {
+  private static final String DEFAULT_INSTANCE_ID = "instance-id";
   @Mock private BrokerMetrics brokerMetrics;
   @Mock private BrokerApi brokerApi;
   @Mock Event event;
@@ -32,13 +33,14 @@
 
   @Before
   public void setUp() {
-    event.instanceId = "instance-id";
+    event.instanceId = DEFAULT_INSTANCE_ID;
     objectUnderTest =
         new BrokerApiWrapper(
             MoreExecutors.directExecutor(),
             DynamicItem.itemOf(BrokerApi.class, brokerApi),
             brokerMetrics,
-            msgLog);
+            msgLog,
+            DEFAULT_INSTANCE_ID);
   }
 
   @Test
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 c8216bd..69968b5 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
@@ -23,7 +23,6 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
-import com.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.gerrit.extensions.events.NewProjectCreatedListener;
 import com.google.gerrit.extensions.events.ProjectDeletedListener;
@@ -46,14 +45,12 @@
   private ProjectListUpdateHandler handler;
 
   @Mock private ProjectListUpdateForwarder forwarder;
-  @Mock private ProjectsFilter projectsFilter;
 
   @Before
   public void setUp() {
-    when(projectsFilter.matches(any(String.class))).thenReturn(true);
     handler =
         new ProjectListUpdateHandler(
-            asDynamicSet(forwarder), MoreExecutors.directExecutor(), projectsFilter, INSTANCE_ID);
+            asDynamicSet(forwarder), MoreExecutors.directExecutor(), INSTANCE_ID);
   }
 
   private DynamicSet<ProjectListUpdateForwarder> asDynamicSet(
@@ -98,7 +95,6 @@
 
   @Test
   public void shouldNotForwardIfFilteredOutByProjectName() throws Exception {
-    when(projectsFilter.matches(any(String.class))).thenReturn(false);
     String projectName = "projectToAdd";
     NewProjectCreatedListener.Event event = mock(NewProjectCreatedListener.Event.class);
     when(event.getProjectName()).thenReturn(projectName);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/event/EventExecutorProviderTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/event/EventExecutorProviderTest.java
deleted file mode 100644
index a025dd0..0000000
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/event/EventExecutorProviderTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2016 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.event;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import com.google.gerrit.server.git.WorkQueue;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-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 EventExecutorProviderTest {
-  @Mock private ScheduledThreadPoolExecutor executorMock;
-  private EventExecutorProvider eventsExecutorProvider;
-
-  @Before
-  public void setUp() throws Exception {
-    WorkQueue workQueueMock = mock(WorkQueue.class);
-    when(workQueueMock.createQueue(1, "Forward-Stream-Event")).thenReturn(executorMock);
-    eventsExecutorProvider = new EventExecutorProvider(workQueueMock);
-  }
-
-  @Test
-  public void shouldReturnExecutor() throws Exception {
-    assertThat(eventsExecutorProvider.get()).isEqualTo(executorMock);
-  }
-
-  @Test
-  public void testStop() throws Exception {
-    eventsExecutorProvider.start();
-    assertThat(eventsExecutorProvider.get()).isEqualTo(executorMock);
-    eventsExecutorProvider.stop();
-    verify(executorMock).shutdown();
-    assertThat(eventsExecutorProvider.get()).isNull();
-  }
-}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/event/EventHandlerTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/event/EventHandlerTest.java
deleted file mode 100644
index b9b416d..0000000
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/event/EventHandlerTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright (C) 2015 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.event;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.any;
-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.when;
-
-import com.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter;
-import com.google.common.util.concurrent.MoreExecutors;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.server.events.Event;
-import com.google.gerrit.server.events.ProjectEvent;
-import com.google.gerrit.server.events.RefUpdatedEvent;
-import com.googlesource.gerrit.plugins.multisite.event.EventHandler.EventTask;
-import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
-import com.googlesource.gerrit.plugins.multisite.forwarder.StreamEventForwarder;
-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 EventHandlerTest {
-
-  private EventHandler eventHandler;
-
-  @Mock private StreamEventForwarder forwarder;
-  @Mock private ProjectsFilter projectsFilter;
-
-  @Before
-  public void setUp() {
-    when(projectsFilter.matches(any(ProjectEvent.class))).thenReturn(true);
-    eventHandler =
-        new EventHandler(asDynamicSet(forwarder), MoreExecutors.directExecutor(), projectsFilter);
-  }
-
-  private DynamicSet<StreamEventForwarder> asDynamicSet(StreamEventForwarder forwarder) {
-    DynamicSet<StreamEventForwarder> result = new DynamicSet<>();
-    result.add("multi-site", forwarder);
-    return result;
-  }
-
-  @Test
-  public void shouldForwardAnyProjectEvent() throws Exception {
-    ProjectEvent event = mock(ProjectEvent.class);
-    eventHandler.onEvent(event);
-    verify(forwarder).send(event);
-  }
-
-  @Test
-  public void shouldNotForwardNonProjectEvent() throws Exception {
-    eventHandler.onEvent(mock(Event.class));
-    verifyZeroInteractions(forwarder);
-  }
-
-  @Test
-  public void shouldNotForwardIfAlreadyForwardedEvent() throws Exception {
-    Context.setForwardedEvent(true);
-    eventHandler.onEvent(mock(ProjectEvent.class));
-    Context.unsetForwardedEvent();
-    verifyZeroInteractions(forwarder);
-  }
-
-  @Test
-  public void shouldNotForwardIfFilteredOutByProjectName() throws Exception {
-    when(projectsFilter.matches(any(ProjectEvent.class))).thenReturn(false);
-
-    ProjectEvent event = mock(ProjectEvent.class);
-
-    eventHandler.onEvent(event);
-    verify(forwarder, never()).send(event);
-  }
-
-  @Test
-  public void tesEventTaskToString() throws Exception {
-    Event event = new RefUpdatedEvent();
-    EventTask task = eventHandler.new EventTask(event);
-    assertThat(task.toString())
-        .isEqualTo(String.format("Send event '%s' to target instance", event.type));
-  }
-}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedEventHandlerTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedEventHandlerTest.java
index 704401b..73ef353 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedEventHandlerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedEventHandlerTest.java
@@ -14,13 +14,9 @@
 
 package com.googlesource.gerrit.plugins.multisite.forwarder;
 
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.server.events.Event;
 import com.google.gerrit.server.events.EventDispatcher;
@@ -33,7 +29,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.stubbing.Answer;
 
 @RunWith(MockitoJUnitRunner.class)
 public class ForwardedEventHandlerTest {
@@ -56,45 +51,4 @@
     handler.dispatch(event);
     verify(dispatcherMock).postEvent(event);
   }
-
-  @Test
-  public void shouldSetAndUnsetForwardedContext() throws Exception {
-    Event event = new ProjectCreatedEvent();
-    // 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(dispatcherMock)
-        .postEvent(event);
-
-    assertThat(Context.isForwardedEvent()).isFalse();
-    handler.dispatch(event);
-    assertThat(Context.isForwardedEvent()).isFalse();
-
-    verify(dispatcherMock).postEvent(event);
-  }
-
-  @Test
-  public void shouldSetAndUnsetForwardedContextEvenIfExceptionIsThrown() throws Exception {
-    Event event = new ProjectCreatedEvent();
-    doAnswer(
-            (Answer<Void>)
-                invocation -> {
-                  assertThat(Context.isForwardedEvent()).isTrue();
-                  throw new StorageException("someMessage");
-                })
-        .when(dispatcherMock)
-        .postEvent(event);
-
-    assertThat(Context.isForwardedEvent()).isFalse();
-    StorageException thrown = assertThrows(StorageException.class, () -> handler.dispatch(event));
-    assertThat(thrown).hasMessageThat().isEqualTo("someMessage");
-    assertThat(Context.isForwardedEvent()).isFalse();
-
-    verify(dispatcherMock).postEvent(event);
-  }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/index/IndexEventHandlerTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/index/IndexEventHandlerTest.java
index 8cf6ea9..ae5851d 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/index/IndexEventHandlerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/index/IndexEventHandlerTest.java
@@ -15,23 +15,14 @@
 package com.googlesource.gerrit.plugins.multisite.index;
 
 import static com.googlesource.gerrit.plugins.replication.pull.api.PullReplicationEndpoints.APPLY_OBJECT_API_ENDPOINT;
-import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.lenient;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
 
-import com.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
 import com.googlesource.gerrit.plugins.multisite.forwarder.IndexEventForwarder;
-import com.googlesource.gerrit.plugins.multisite.forwarder.events.ProjectIndexEvent;
-import com.googlesource.gerrit.plugins.multisite.index.IndexEventHandler.IndexProjectTask;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,7 +36,6 @@
 
   private IndexEventHandler eventHandler;
 
-  @Mock private ProjectsFilter projectsFilter;
   @Mock private IndexEventForwarder forwarder;
   @Mock private ChangeCheckerImpl.Factory changeChecker;
 
@@ -56,7 +46,6 @@
             MoreExecutors.directExecutor(),
             asDynamicSet(forwarder),
             changeChecker,
-            projectsFilter,
             new TestGroupChecker(true),
             INSTANCE_ID);
   }
@@ -68,24 +57,6 @@
   }
 
   @Test
-  public void shouldNotForwardProjectIndexedIfFilteredOutByProjectName() throws Exception {
-    when(projectsFilter.matches(any(String.class))).thenReturn(false);
-
-    eventHandler.onProjectIndexed("test_project");
-    verify(forwarder, never())
-        .index(any(IndexProjectTask.class), eq(new ProjectIndexEvent("test_project", INSTANCE_ID)));
-  }
-
-  @Test
-  public void shouldNotForwardIndexChangeIfFilteredOutByProjectName() throws Exception {
-    int changeId = 1;
-    when(projectsFilter.matches(any(String.class))).thenReturn(false);
-
-    eventHandler.onChangeIndexed("test_project", changeId);
-    verifyZeroInteractions(changeChecker);
-  }
-
-  @Test
   public void shouldNotForwardIndexChangeIfCurrentThreadIsPullReplicationApplyObject()
       throws Exception {
     String currentThreadName = Thread.currentThread().getName();
@@ -93,7 +64,6 @@
       Thread.currentThread().setName("pull-replication~" + APPLY_OBJECT_API_ENDPOINT);
       int changeId = 1;
       Context.setForwardedEvent(false);
-      lenient().when(projectsFilter.matches(any(String.class))).thenReturn(true);
       lenient()
           .when(changeChecker.create(anyString()))
           .thenThrow(
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 d99fb4b..fbf8ba0 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
@@ -27,13 +27,11 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
-import com.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter;
 import com.gerritforge.gerrit.globalrefdb.validation.SharedRefDatabaseWrapper;
 import com.google.common.base.Suppliers;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.server.data.RefUpdateAttribute;
-import com.google.gerrit.server.events.Event;
 import com.google.gerrit.server.events.RefUpdatedEvent;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.project.ProjectConfig;
@@ -41,7 +39,6 @@
 import com.google.gerrit.testing.InMemoryTestEnvironment;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.multisite.ProjectVersionLogger;
-import com.googlesource.gerrit.plugins.multisite.forwarder.Context;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.RefFixture;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
@@ -54,7 +51,6 @@
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.revwalk.RevCommit;
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -65,24 +61,24 @@
 @RunWith(MockitoJUnitRunner.class)
 public class ProjectVersionRefUpdateTest implements RefFixture {
 
+  private static final String DEFAULT_INSTANCE_ID = "instance-id";
+
   @Rule public InMemoryTestEnvironment testEnvironment = new InMemoryTestEnvironment();
 
   @Mock RefUpdatedEvent refUpdatedEvent;
   @Mock SharedRefDatabaseWrapper sharedRefDb;
   @Mock GitReferenceUpdated gitReferenceUpdated;
   @Mock ProjectVersionLogger verLogger;
-  @Mock ProjectsFilter projectsFilter;
 
   @Inject private ProjectConfig.Factory projectConfigFactory;
   @Inject private InMemoryRepositoryManager repoManager;
-
   private TestRepository<InMemoryRepository> repo;
   private ProjectConfig project;
   private RevCommit masterCommit;
 
   @Before
   public void setUp() throws Exception {
-    when(projectsFilter.matches(any(Event.class))).thenReturn(true);
+    refUpdatedEvent.instanceId = DEFAULT_INSTANCE_ID;
     InMemoryRepository inMemoryRepo = repoManager.createRepository(A_TEST_PROJECT_NAME_KEY);
     project = projectConfigFactory.create(A_TEST_PROJECT_NAME_KEY);
     project.load(inMemoryRepo);
@@ -90,14 +86,8 @@
     masterCommit = repo.branch("master").commit().create();
   }
 
-  @After
-  public void tearDown() {
-    Context.unsetForwardedEvent();
-  }
-
   @Test
   public void producerShouldUpdateProjectVersionUponRefUpdatedEvent() throws IOException {
-    Context.setForwardedEvent(false);
     when(sharedRefDb.get(
             A_TEST_PROJECT_NAME_KEY,
             ProjectVersionRefUpdate.MULTI_SITE_VERSIONING_VALUE_REF,
@@ -109,7 +99,7 @@
     when(refUpdatedEvent.getRefName()).thenReturn(A_TEST_REF_NAME);
 
     new ProjectVersionRefUpdateImpl(
-            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, projectsFilter)
+            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, DEFAULT_INSTANCE_ID)
         .onEvent(refUpdatedEvent);
 
     Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
@@ -131,13 +121,14 @@
       throws IOException {
     when(sharedRefDb.isSetOperationSupported()).thenReturn(true);
     RefUpdatedEvent refUpdatedEvent = new RefUpdatedEvent();
+    refUpdatedEvent.instanceId = DEFAULT_INSTANCE_ID;
     RefUpdateAttribute refUpdatedAttribute = new RefUpdateAttribute();
     refUpdatedAttribute.project = A_TEST_PROJECT_NAME_KEY.get();
     refUpdatedAttribute.refName = A_TEST_REF_NAME;
     refUpdatedEvent.refUpdate = Suppliers.memoize(() -> refUpdatedAttribute);
 
     new ProjectVersionRefUpdateImpl(
-            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, projectsFilter)
+            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, DEFAULT_INSTANCE_ID)
         .onEvent(refUpdatedEvent);
 
     Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
@@ -157,8 +148,6 @@
 
   @Test
   public void producerShouldUpdateProjectVersionUponForcedPushRefUpdatedEvent() throws Exception {
-    Context.setForwardedEvent(false);
-
     Thread.sleep(1000L);
     RevCommit masterPlusOneCommit = repo.branch("master").commit().create();
 
@@ -176,7 +165,7 @@
 
     ProjectVersionRefUpdateImpl projectVersion =
         new ProjectVersionRefUpdateImpl(
-            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, projectsFilter);
+            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, DEFAULT_INSTANCE_ID);
     projectVersion.onEvent(refUpdatedEvent);
 
     Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
@@ -200,7 +189,6 @@
   @Test
   public void producerShouldCreateNewProjectVersionWhenMissingUponRefUpdatedEvent()
       throws IOException {
-    Context.setForwardedEvent(false);
     when(sharedRefDb.get(
             A_TEST_PROJECT_NAME_KEY,
             ProjectVersionRefUpdate.MULTI_SITE_VERSIONING_VALUE_REF,
@@ -213,7 +201,7 @@
     when(refUpdatedEvent.getRefName()).thenReturn(A_TEST_REF_NAME);
 
     new ProjectVersionRefUpdateImpl(
-            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, projectsFilter)
+            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, DEFAULT_INSTANCE_ID)
         .onEvent(refUpdatedEvent);
 
     Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
@@ -250,13 +238,27 @@
   private void producerShouldNotUpdateProjectVersionUponMagicRefUpdatedEvent(String magicRefPrefix)
       throws Exception {
     String magicRefName = magicRefPrefix + "/foo";
-    Context.setForwardedEvent(false);
     when(refUpdatedEvent.getProjectNameKey()).thenReturn(A_TEST_PROJECT_NAME_KEY);
     when(refUpdatedEvent.getRefName()).thenReturn(magicRefName);
     repo.branch(magicRefName).commit().create();
 
     new ProjectVersionRefUpdateImpl(
-            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, projectsFilter)
+            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, DEFAULT_INSTANCE_ID)
+        .onEvent(refUpdatedEvent);
+
+    Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
+    assertThat(ref).isNull();
+
+    verifyZeroInteractions(verLogger);
+  }
+
+  @Test
+  public void producerShouldNotUpdateProjectVersionUponForwardedRefUpdatedEvent()
+      throws IOException {
+    refUpdatedEvent.instanceId = "instance-id-2";
+
+    new ProjectVersionRefUpdateImpl(
+            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, DEFAULT_INSTANCE_ID)
         .onEvent(refUpdatedEvent);
 
     Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
@@ -267,12 +269,11 @@
 
   @Test
   public void shouldNotUpdateProjectVersionWhenProjectDoesntExist() throws IOException {
-    Context.setForwardedEvent(false);
     when(refUpdatedEvent.getProjectNameKey()).thenReturn(Project.nameKey("aNonExistentProject"));
     when(refUpdatedEvent.getRefName()).thenReturn(A_TEST_REF_NAME);
 
     new ProjectVersionRefUpdateImpl(
-            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, projectsFilter)
+            repoManager, sharedRefDb, gitReferenceUpdated, verLogger, DEFAULT_INSTANCE_ID)
         .onEvent(refUpdatedEvent);
 
     Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF);
@@ -288,7 +289,7 @@
 
     Optional<Long> version =
         new ProjectVersionRefUpdateImpl(
-                repoManager, sharedRefDb, gitReferenceUpdated, verLogger, projectsFilter)
+                repoManager, sharedRefDb, gitReferenceUpdated, verLogger, DEFAULT_INSTANCE_ID)
             .getProjectRemoteVersion(A_TEST_PROJECT_NAME);
 
     assertThat(version.isPresent()).isTrue();