Merge branch 'stable-3.9' into stable-3.10

* stable-3.9:
  Make the local test environment more realistic

Change-Id: I2b3fc30426c4a518da649dc73711f4b8fbc19ad8
diff --git a/.gitignore b/.gitignore
index 8bccbdb..c2375ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,6 @@
 
 /eclipse-out/
 setup_local_env/**/*.env
+
+.idea
+*.iml
diff --git a/Jenkinsfile b/Jenkinsfile
index 6ef43af..be0b4fc 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -1,4 +1,4 @@
 pluginPipeline(formatCheckId: 'gerritforge:multi-site-format-47168e90078b0b3f11401610930e82830e76bff7',
                buildCheckId: 'gerritforge:multi-site-47168e90078b0b3f11401610930e82830e76bff7',
-               extraPlugins: [ 'pull-replication' ],
+               extraPlugins: [ 'pull-replication', 'healthcheck' ],
                extraModules: [ 'events-broker', 'global-refdb' ])
diff --git a/setup_local_env/setup.sh b/setup_local_env/setup.sh
index 96a4204..2673d3b 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.9
+GERRIT_BRANCH=stable-3.10
 GERRIT_CI=https://gerrit-ci.gerritforge.com/view/Plugins-$GERRIT_BRANCH/job
 LAST_BUILD=lastSuccessfulBuild/artifact/bazel-bin/plugins
 
@@ -234,46 +234,62 @@
   { echo >&2 "Cannot download $artifact_name $prefix: Check internet connection. Aborting"; exit 1; }
 }
 
+function show_help {
+  echo "Usage: $0 [--option ] "
+  echo
+  echo "[--release-war-file]            Location to release.war file;" \
+       "default: 'bazel-bin/release.war'"
+  echo "[--multisite-lib-file]          Location to lib multi-site.jar file;" \
+       "default: 'bazel-bin/plugins/multi-site/multi-site.jar'"
+  echo "[--eventsbroker-lib-file]       Location to lib events-broker.jar file;" \
+       "default: 'bazel-bin/plugins/events-broker/events-broker.jar'"
+  echo "[--globalrefdb-lib-file]        Location to lib global-refdb.jar file;" \
+       "default: 'bazel-bin/plugins/global-refdb/global-refdb.jar'"
+  echo
+  echo "[--new-deployment]              Cleans up previous gerrit deployment and re-installs it. default true"
+  echo "[--get-websession-plugin]       Download websession-broker plugin from CI lastSuccessfulBuild; default true"
+  echo "[--deployment-location]         Base location for the test deployment; default /tmp"
+  echo
+  echo "[--gerrit-canonical-host]       The default host for Gerrit to be accessed through; default localhost"
+  echo "[--gerrit-canonical-port]       The default port for Gerrit to be accessed through; default 8080"
+  echo
+  echo "[--gerrit-ssh-advertised-port]  Gerrit Instance 1 sshd port; default 29418"
+  echo
+  echo "[--gerrit1-httpd-port]          Gerrit Instance 1 http port; default 18080"
+  echo "[--gerrit1-sshd-port]           Gerrit Instance 1 sshd port; default 39418"
+  echo
+  echo "[--gerrit2-httpd-port]          Gerrit Instance 2 http port; default 18081"
+  echo "[--gerrit2-sshd-port]           Gerrit Instance 2 sshd port; default 49418"
+  echo
+  echo "[--replication-delay]           Replication delay across the two instances in seconds"
+  echo
+  echo "[--just-cleanup-env]            Cleans up previous deployment; default false"
+  echo
+  echo "[--enabled-https]               Enabled https; default true"
+  echo
+  echo "[--broker-type]                 events broker type; 'kafka', 'kinesis' or 'gcloud-pubsub'; default 'kafka'"
+  echo
+  echo "[--sudo]                        run docker commands with sudo"
+  echo
+  echo "Note: Script should be run from the gerrit root source path to take advantage of local builds"\
+       "for release.war, multi-site.jar, events-broker.jar, and global-refdb.jar"
+  echo
+  echo "Examples of usage: "
+  echo "Cleanup last install -> $0 --just-cleanup-env true"
+  echo "Install and startup multisite based on local artifacts (Gerrit and multi-site)-> $0"
+}
+
+################################################################################
+###    Startup
 while [ $# -ne 0 ]
 do
 case "$1" in
   "--help" )
-    echo "Usage: sh $0 [--option $value]"
-    echo
-    echo "[--release-war-file]            Location to release.war file"
-    echo "[--multisite-lib-file]          Location to lib multi-site.jar file"
-    echo "[--eventsbroker-lib-file]       Location to lib events-broker.jar file"
-    echo "[--globalrefdb-lib-file]        Location to lib global-refdb.jar file"
-    echo
-    echo "[--new-deployment]              Cleans up previous gerrit deployment and re-installs it. default true"
-    echo "[--get-websession-plugin]       Download websession-broker plugin from CI lastSuccessfulBuild; default true"
-    echo "[--deployment-location]         Base location for the test deployment; default /tmp"
-    echo
-    echo "[--gerrit-canonical-host]       The default host for Gerrit to be accessed through; default localhost"
-    echo "[--gerrit-canonical-port]       The default port for Gerrit to be accessed throug; default 8080"
-    echo
-    echo "[--gerrit-ssh-advertised-port]  Gerrit Instance 1 sshd port; default 29418"
-    echo
-    echo "[--gerrit1-httpd-port]          Gerrit Instance 1 http port; default 18080"
-    echo "[--gerrit1-sshd-port]           Gerrit Instance 1 sshd port; default 39418"
-    echo
-    echo "[--gerrit2-httpd-port]          Gerrit Instance 2 http port; default 18081"
-    echo "[--gerrit2-sshd-port]           Gerrit Instance 2 sshd port; default 49418"
-    echo
-    echo "[--replication-delay]           Replication delay across the two instances in seconds"
-    echo
-    echo "[--just-cleanup-env]            Cleans up previous deployment; default false"
-    echo
-    echo "[--enabled-https]               Enabled https; default true"
-    echo
-    echo "[--broker-type]                 events broker type; 'kafka', 'kinesis' or 'gcloud-pubsub'. Default 'kafka'"
-    echo
-    echo "[--sudo]                        run docker commands with sudo"
-    echo
+    show_help
     exit 0
   ;;
   "--new-deployment")
-        NEW_INSTALLATION=$2
+    NEW_INSTALLATION=$2
     shift
     shift
   ;;
@@ -562,12 +578,12 @@
 
 echo "Re-deploying configuration files"
 deploy_config_files $GERRIT_1_HOSTNAME $GERRIT_1_HTTPD_PORT $GERRIT_1_SSHD_PORT $GERRIT_2_HOSTNAME $GERRIT_2_HTTPD_PORT $GERRIT_2_SSHD_PORT
-echo "Remove replication plugin from gerrit site 1"
-rm $LOCATION_TEST_SITE_1/plugins/replication.jar
+echo "Move replication and delete-project plugins to /lib on gerrit site 1"
+mv $LOCATION_TEST_SITE_1/plugins/{replication,delete-project}.jar $LOCATION_TEST_SITE_1/lib/.
 echo "Starting gerrit site 1"
 $LOCATION_TEST_SITE_1/bin/gerrit.sh restart
-echo "Remove replication plugin from gerrit site 2"
-rm $LOCATION_TEST_SITE_2/plugins/replication.jar
+echo "Move replication plugin to /lib on gerrit site 2"
+mv $LOCATION_TEST_SITE_2/plugins/{replication,delete-project}.jar $LOCATION_TEST_SITE_2/lib/.
 echo "Starting gerrit site 2"
 $LOCATION_TEST_SITE_2/bin/gerrit.sh restart
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/Log4jMessageLogger.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/Log4jMessageLogger.java
deleted file mode 100644
index 95c417f..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/Log4jMessageLogger.java
+++ /dev/null
@@ -1,47 +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;
-
-import com.google.gerrit.extensions.systemstatus.ServerInformation;
-import com.google.gerrit.server.events.Event;
-import com.google.gerrit.server.events.EventGsonProvider;
-import com.google.gerrit.server.util.PluginLogFile;
-import com.google.gerrit.server.util.SystemLog;
-import com.google.gson.Gson;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import org.apache.log4j.PatternLayout;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Singleton
-public class Log4jMessageLogger extends PluginLogFile implements MessageLogger {
-  private static final String LOG_NAME = "message_log";
-  private final Logger msgLog;
-  private final Gson gson;
-
-  @Inject
-  public Log4jMessageLogger(
-      SystemLog systemLog, ServerInformation serverInfo, EventGsonProvider gsonProvider) {
-    super(systemLog, serverInfo, LOG_NAME, new PatternLayout("[%d{ISO8601}] [%t] %-5p : %m%n"));
-    this.msgLog = LoggerFactory.getLogger(LOG_NAME);
-    this.gson = gsonProvider.get();
-  }
-
-  @Override
-  public void log(Direction direction, String topic, Event event) {
-    msgLog.info("{} {} {}", direction, topic, gson.toJson(event));
-  }
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/MessageLogger.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/MessageLogger.java
deleted file mode 100644
index cc64b02..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/MessageLogger.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;
-
-import com.google.gerrit.server.events.Event;
-
-public interface MessageLogger {
-
-  public enum Direction {
-    PUBLISH,
-    CONSUME;
-  }
-
-  public void log(Direction direction, String topic, Event event);
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java
index 9392077..c161a6d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java
@@ -45,9 +45,6 @@
 
     install(new LibModule());
 
-    listener().to(Log4jMessageLogger.class);
-    bind(MessageLogger.class).to(Log4jMessageLogger.class);
-
     install(new ForwarderModule());
 
     if (config.cache().synchronize()) {
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 88422e1..32fb0a5 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
@@ -16,6 +16,8 @@
 
 import com.gerritforge.gerrit.eventbroker.BrokerApi;
 import com.gerritforge.gerrit.eventbroker.TopicSubscriber;
+import com.gerritforge.gerrit.eventbroker.TopicSubscriberWithGroupId;
+import com.gerritforge.gerrit.eventbroker.log.MessageLogger;
 import com.google.common.base.Strings;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
@@ -25,8 +27,6 @@
 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 java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -92,7 +92,7 @@
         new FutureCallback<Boolean>() {
           @Override
           public void onSuccess(Boolean result) {
-            msgLog.log(Direction.PUBLISH, topic, message);
+            msgLog.log(MessageLogger.Direction.PUBLISH, topic, message);
             metrics.incrementBrokerPublishedMessage();
           }
 
@@ -117,11 +117,21 @@
   }
 
   @Override
+  public void receiveAsync(String topic, String groupId, Consumer<Event> consumer) {
+    apiDelegate.get().receiveAsync(topic, groupId, consumer);
+  }
+
+  @Override
   public void disconnect() {
     apiDelegate.get().disconnect();
   }
 
   @Override
+  public void disconnect(String topic, String groupId) {
+    apiDelegate.get().disconnect(topic, groupId);
+  }
+
+  @Override
   public Set<TopicSubscriber> topicSubscribers() {
     return apiDelegate.get().topicSubscribers();
   }
@@ -130,4 +140,9 @@
   public void replayAllEvents(String topic) {
     apiDelegate.get().replayAllEvents(topic);
   }
+
+  @Override
+  public Set<TopicSubscriberWithGroupId> topicSubscribersWithGroupId() {
+    return apiDelegate.get().topicSubscribersWithGroupId();
+  }
 }
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 6a2c7a5..ad42ec6 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
@@ -25,11 +25,11 @@
 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.Objects;
 import java.util.concurrent.Executor;
 
 @Singleton
 public class ProjectListUpdateHandler implements NewProjectCreatedListener, ProjectDeletedListener {
-
   private final DynamicSet<ProjectListUpdateForwarder> forwarders;
   private final Executor executor;
   private final String instanceId;
@@ -47,7 +47,9 @@
   @Override
   public void onNewProjectCreated(
       com.google.gerrit.extensions.events.NewProjectCreatedListener.Event event) {
-    process(event, false);
+    if (Objects.equals(instanceId, event.getInstanceId())) {
+      process(event, false);
+    }
   }
 
   @Override
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 c2b9e9c..d4c3618 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
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.plugins.multisite.consumer;
 
+import com.gerritforge.gerrit.eventbroker.log.MessageLogger;
 import com.google.common.base.Strings;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.extensions.registration.DynamicSet;
@@ -21,8 +22,6 @@
 import com.google.gerrit.server.events.Event;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
-import com.googlesource.gerrit.plugins.multisite.MessageLogger;
-import com.googlesource.gerrit.plugins.multisite.MessageLogger.Direction;
 import com.googlesource.gerrit.plugins.multisite.forwarder.CacheNotFoundException;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.EventTopic;
 import com.googlesource.gerrit.plugins.multisite.forwarder.router.ForwardedEventRouter;
@@ -77,7 +76,7 @@
       droppedEventListeners.forEach(l -> l.onEventDropped(event));
     } else {
       try {
-        msgLog.log(Direction.CONSUME, topic, event);
+        msgLog.log(MessageLogger.Direction.CONSUME, topic, event);
         eventRouter.route(event);
         subscriberMetrics.incrementSubscriberConsumedMessage();
       } catch (IOException e) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/BatchIndexEventSubscriber.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/BatchIndexEventSubscriber.java
index cdbf220..15d756b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/BatchIndexEventSubscriber.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/BatchIndexEventSubscriber.java
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.plugins.multisite.consumer;
 
+import com.gerritforge.gerrit.eventbroker.log.MessageLogger;
 import com.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.server.config.GerritInstanceId;
@@ -21,7 +22,6 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
-import com.googlesource.gerrit.plugins.multisite.MessageLogger;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.ChangeIndexEvent;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.EventTopic;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.ProjectIndexEvent;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/CacheEvictionEventSubscriber.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/CacheEvictionEventSubscriber.java
index d8342b0..e430dac 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/CacheEvictionEventSubscriber.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/CacheEvictionEventSubscriber.java
@@ -14,13 +14,13 @@
 
 package com.googlesource.gerrit.plugins.multisite.consumer;
 
+import com.gerritforge.gerrit.eventbroker.log.MessageLogger;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.server.config.GerritInstanceId;
 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.MessageLogger;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.EventTopic;
 import com.googlesource.gerrit.plugins.multisite.forwarder.router.CacheEvictionEventRouter;
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/IndexEventSubscriber.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/IndexEventSubscriber.java
index 480fc50..a632618 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/IndexEventSubscriber.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/IndexEventSubscriber.java
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.plugins.multisite.consumer;
 
+import com.gerritforge.gerrit.eventbroker.log.MessageLogger;
 import com.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.extensions.registration.DynamicSet;
@@ -23,7 +24,6 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
-import com.googlesource.gerrit.plugins.multisite.MessageLogger;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.ChangeIndexEvent;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.EventTopic;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.ProjectIndexEvent;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/ProjectUpdateEventSubscriber.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/ProjectUpdateEventSubscriber.java
index 069a516..2ab74a8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/ProjectUpdateEventSubscriber.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/ProjectUpdateEventSubscriber.java
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.plugins.multisite.consumer;
 
+import com.gerritforge.gerrit.eventbroker.log.MessageLogger;
 import com.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.server.config.GerritInstanceId;
@@ -21,7 +22,6 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
-import com.googlesource.gerrit.plugins.multisite.MessageLogger;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.EventTopic;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.ProjectListUpdateEvent;
 import com.googlesource.gerrit.plugins.multisite.forwarder.router.ProjectListUpdateRouter;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/StreamEventSubscriber.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/StreamEventSubscriber.java
index ab64651..63fc29a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/StreamEventSubscriber.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/StreamEventSubscriber.java
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.plugins.multisite.consumer;
 
+import com.gerritforge.gerrit.eventbroker.log.MessageLogger;
 import com.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.server.config.GerritInstanceId;
@@ -22,7 +23,6 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
-import com.googlesource.gerrit.plugins.multisite.MessageLogger;
 import com.googlesource.gerrit.plugins.multisite.forwarder.events.EventTopic;
 import com.googlesource.gerrit.plugins.multisite.forwarder.router.StreamEventRouter;
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedIndexingHandlerWithRetries.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedIndexingHandlerWithRetries.java
index f3d4e70..d135e39 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedIndexingHandlerWithRetries.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/forwarder/ForwardedIndexingHandlerWithRetries.java
@@ -64,7 +64,7 @@
   protected boolean rescheduleIndex(T id) {
     IndexingRetry retry = indexingRetryTaskMap.get(id);
     if (retry == null) {
-      log.info(
+      log.debug(
           "{} {} successfully indexed by different task, rescheduling isn't needed",
           indexName(),
           id);
@@ -110,7 +110,7 @@
     IndexingRetry retry = new IndexingRetry(event);
     if (indexingRetryTaskMap.put(id, retry) != null) {
       indexOnce.accept(id);
-      log.info(
+      log.debug(
           "Skipping indexing because there is already a running task for the specified id. Index name: {}, task id: {}",
           indexName(),
           id);
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 7b90468..f3fa2e3 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
@@ -26,11 +26,12 @@
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.inject.Inject;
+import com.google.inject.Module;
 import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
 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;
 import org.eclipse.jgit.lib.Config;
@@ -57,6 +58,12 @@
     public ChangeChecker create(String changeId);
   }
 
+  public static Module module() {
+    return new FactoryModuleBuilder()
+        .implement(ChangeChecker.class, ChangeCheckerImpl.class)
+        .build(ChangeCheckerImpl.Factory.class);
+  }
+
   @Inject
   public ChangeCheckerImpl(
       GitRepositoryManager gitRepoMgr,
@@ -114,8 +121,7 @@
         .map(
             e ->
                 (computedChangeTs.get() > e.eventCreatedOn)
-                    || ((computedChangeTs.get() == e.eventCreatedOn)
-                        && (Objects.equals(getBranchTargetSha(), e.targetSha))))
+                    || ((computedChangeTs.get() == e.eventCreatedOn) && repositoryHas(e.targetSha)))
         .orElse(true);
   }
 
@@ -154,6 +160,15 @@
     }
   }
 
+  private boolean repositoryHas(String targetSha) {
+    try (Repository repo = gitRepoMgr.openRepository(changeNotes.get().getProjectName())) {
+      return repo.parseCommit(ObjectId.fromString(targetSha)) != null;
+    } catch (IOException e) {
+      log.warn("Unable to find SHA1 {} for change {}", targetSha, changeId, e);
+      return false;
+    }
+  }
+
   @Override
   public boolean isChangeConsistent() {
     Optional<ChangeNotes> notes = getChangeNotes();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/index/IndexModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/index/IndexModule.java
index 5f47371..0e7a54f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/index/IndexModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/index/IndexModule.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.server.events.EventListener;
-import com.google.inject.assistedinject.FactoryModuleBuilder;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ScheduledExecutorService;
 
@@ -43,9 +42,6 @@
     bind(ProjectChecker.class).to(ProjectCheckerImpl.class);
     bind(GroupChecker.class).to(GroupCheckerImpl.class);
 
-    install(
-        new FactoryModuleBuilder()
-            .implement(ChangeChecker.class, ChangeCheckerImpl.class)
-            .build(ChangeCheckerImpl.Factory.class));
+    install(ChangeCheckerImpl.module());
   }
 }
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 0aba056..f076b7f 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
@@ -23,7 +23,7 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
-import com.googlesource.gerrit.plugins.replication.ReplicationPushFilter;
+import com.googlesource.gerrit.plugins.replication.api.ReplicationPushFilter;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.HashSet;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/PushReplicationFilterModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/PushReplicationFilterModule.java
index 8a8e826..77ac478 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/PushReplicationFilterModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/PushReplicationFilterModule.java
@@ -16,7 +16,7 @@
 
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.inject.AbstractModule;
-import com.googlesource.gerrit.plugins.replication.ReplicationPushFilter;
+import com.googlesource.gerrit.plugins.replication.api.ReplicationPushFilter;
 
 public class PushReplicationFilterModule extends AbstractModule {
 
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 50f55b2..ca1cc11 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
@@ -8,12 +8,12 @@
 import static org.mockito.Mockito.when;
 
 import com.gerritforge.gerrit.eventbroker.BrokerApi;
+import com.gerritforge.gerrit.eventbroker.log.MessageLogger;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.SettableFuture;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.server.events.Event;
 import com.google.gerrit.server.events.ProjectCreatedEvent;
-import com.googlesource.gerrit.plugins.multisite.MessageLogger;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
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 3a4242c..ed1160d 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
@@ -61,10 +61,11 @@
   }
 
   @Test
-  public void shouldForwardAddedProject() throws Exception {
+  public void shouldForwardAddedProjectForLocalEvent() {
     String projectName = "projectToAdd";
     NewProjectCreatedListener.Event event = mock(NewProjectCreatedListener.Event.class);
     when(event.getProjectName()).thenReturn(projectName);
+    when(event.getInstanceId()).thenReturn(INSTANCE_ID);
     handler.onNewProjectCreated(event);
     verify(forwarder)
         .updateProjectList(
@@ -73,6 +74,14 @@
   }
 
   @Test
+  public void shouldNotForwardAddedProjectForRemoteEvent() {
+    NewProjectCreatedListener.Event event = mock(NewProjectCreatedListener.Event.class);
+    when(event.getInstanceId()).thenReturn("aRandomInstanceId");
+    handler.onNewProjectCreated(event);
+    verifyNoInteractions(forwarder);
+  }
+
+  @Test
   public void shouldForwardDeletedProject() throws Exception {
     String projectName = "projectToDelete";
     ProjectDeletedListener.Event event = mock(ProjectDeletedListener.Event.class);
@@ -98,6 +107,7 @@
     String projectName = "projectToAdd";
     NewProjectCreatedListener.Event event = mock(NewProjectCreatedListener.Event.class);
     when(event.getProjectName()).thenReturn(projectName);
+    when(event.getInstanceId()).thenReturn(INSTANCE_ID);
     handler.onNewProjectCreated(event);
     verify(forwarder, never())
         .updateProjectList(
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/AbstractSubscriberTestBase.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/AbstractSubscriberTestBase.java
index cb1f78e..dc7197f 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/AbstractSubscriberTestBase.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/AbstractSubscriberTestBase.java
@@ -21,13 +21,13 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.gerritforge.gerrit.eventbroker.log.MessageLogger;
 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.permissions.PermissionBackendException;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
 import com.googlesource.gerrit.plugins.multisite.Configuration.Broker;
-import com.googlesource.gerrit.plugins.multisite.MessageLogger;
 import com.googlesource.gerrit.plugins.multisite.forwarder.CacheNotFoundException;
 import com.googlesource.gerrit.plugins.multisite.forwarder.router.ForwardedEventRouter;
 import java.io.IOException;
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/index/ChangeCheckerIT.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/index/ChangeCheckerIT.java
new file mode 100644
index 0000000..d3c5e8d
--- /dev/null
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/index/ChangeCheckerIT.java
@@ -0,0 +1,114 @@
+// Copyright (C) 2024 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.index;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.acceptance.config.GerritConfig;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.inject.AbstractModule;
+import com.googlesource.gerrit.plugins.multisite.forwarder.events.ChangeIndexEvent;
+import java.util.Optional;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+@TestPlugin(
+    name = "multi-site",
+    sysModule = "com.googlesource.gerrit.plugins.multisite.index.ChangeCheckerIT$TestModule")
+public class ChangeCheckerIT extends LightweightPluginDaemonTest {
+  private static final String TEST_BRANCH_REFS_HEADS = RefNames.fullName("master");
+  private static final String NONEXISTENTSHA1 = "d3d192b8f704aec0c764cca427fdaca88db71513";
+  private ChangeCheckerImpl.Factory changeCheckerFactory;
+
+  public static class TestModule extends AbstractModule {
+    @Override
+    protected void configure() {
+      install(ChangeCheckerImpl.module());
+    }
+  }
+
+  @Override
+  public void setUpTestPlugin() throws Exception {
+    super.setUpTestPlugin();
+
+    changeCheckerFactory = plugin.getSysInjector().getInstance(ChangeCheckerImpl.Factory.class);
+  }
+
+  @Test
+  @GerritConfig(name = "gerrit.instanceId", value = "test-instance")
+  public void shouldBeUpToDateIfTargetBranchSHA1HasAdvanced() throws Exception {
+    RevCommit headCommit = createCommit();
+    int changeNum = newChangeNum();
+    String changeId = changeProjectAndNum(changeNum);
+    long changeCommitTs = changeCommitTs(changeNum);
+
+    ChangeIndexEvent indexChangeEvent = newIndexChangeEvent(changeNum);
+    indexChangeEvent.eventCreatedOn = changeCommitTs / 1000L;
+    indexChangeEvent.targetSha = headCommit.getId().getName();
+
+    RevCommit secondCommit = createCommit();
+    assertThat(secondCommit.getId().getName()).isNotEqualTo(indexChangeEvent.targetSha);
+    assertThat(changeCheckerFactory.create(changeId).isUpToDate(Optional.of(indexChangeEvent)))
+        .isTrue();
+  }
+
+  @Test
+  @GerritConfig(name = "gerrit.instanceId", value = "test-instance")
+  public void shouldNotBeUpToDateIfTargetSHA1Absent() throws Exception {
+    int changeNum = newChangeNum();
+    String changeId = changeProjectAndNum(changeNum);
+    long changeCommitTs = changeCommitTs(changeNum);
+
+    ChangeIndexEvent indexChangeEvent = newIndexChangeEvent(changeNum);
+    indexChangeEvent.eventCreatedOn = changeCommitTs / 1000L;
+    indexChangeEvent.targetSha = NONEXISTENTSHA1;
+
+    assertThat(changeCheckerFactory.create(changeId).isUpToDate(Optional.of(indexChangeEvent)))
+        .isFalse();
+  }
+
+  private ChangeIndexEvent newIndexChangeEvent(int changeNum) {
+    ChangeIndexEvent indexChangeEvent =
+        new ChangeIndexEvent(project.get(), changeNum, false, instanceId);
+    return indexChangeEvent;
+  }
+
+  private int newChangeNum() throws Exception {
+    PushOneCommit.Result changeRes = createChange();
+    changeRes.assertOkStatus();
+    int changeNum = changeRes.getChange().getId().get();
+    return changeNum;
+  }
+
+  private String changeProjectAndNum(int changeNum) {
+    String changeId = String.format("%s~%d", project.get(), changeNum);
+    return changeId;
+  }
+
+  private long changeCommitTs(int changeNum) throws RestApiException {
+    long changeCommitTs = gApi.changes().id(project.get(), changeNum).get().updated.getTime();
+    return changeCommitTs;
+  }
+
+  private RevCommit createCommit() throws Exception {
+    PushOneCommit.Result result = pushTo(TEST_BRANCH_REFS_HEADS);
+    result.assertOkStatus();
+    return result.getCommit();
+  }
+}