Merge "Merge branch 'stable-3.4' into stable-3.5" into stable-3.5
diff --git a/example-setup/broker/configs/replication.config.template b/example-setup/broker/configs/replication.config.template
index d81662f..d464586 100644
--- a/example-setup/broker/configs/replication.config.template
+++ b/example-setup/broker/configs/replication.config.template
@@ -1,6 +1,6 @@
 [gerrit]
     autoReload = true
-    replicateOnStartup = false
+    replicateOnStartup = $REPLICATE_ON_STARTUP
 [replication]
     excludeRefs = ^refs/users/\\d\\d/\\d+/edit-\\d+/\\d+$
     lockErrorMaxRetries = 5
diff --git a/example-setup/broker/docker-compose.yaml b/example-setup/broker/docker-compose.yaml
index 4f7f99f..705aea6 100644
--- a/example-setup/broker/docker-compose.yaml
+++ b/example-setup/broker/docker-compose.yaml
@@ -10,6 +10,7 @@
       - DEBUG_PORT=5005
       - BROKER_HOST=broker
       - BROKER_PORT=9092
+      - REPLICATE_ON_STARTUP=false
     ports:
       - "8080:8080"
       - "29418:29418"
@@ -26,6 +27,7 @@
       - DEBUG_PORT=5006
       - BROKER_HOST=broker
       - BROKER_PORT=9092
+      - REPLICATE_ON_STARTUP=true
     ports:
       - "8081:8080"
       - "29419:29418"
diff --git a/example-setup/http/configs/replication.config.template b/example-setup/http/configs/replication.config.template
index 7e81055..6768146 100644
--- a/example-setup/http/configs/replication.config.template
+++ b/example-setup/http/configs/replication.config.template
@@ -1,6 +1,6 @@
 [gerrit]
     autoReload = true
-    replicateOnStartup = false
+    replicateOnStartup = $REPLICATE_ON_STARTUP
 [replication]
     excludeRefs = ^refs/users/\\d\\d/\\d+/edit-\\d+/\\d+$
     lockErrorMaxRetries = 5
diff --git a/example-setup/http/docker-compose.yaml b/example-setup/http/docker-compose.yaml
index af90341..ccb6b86 100644
--- a/example-setup/http/docker-compose.yaml
+++ b/example-setup/http/docker-compose.yaml
@@ -8,6 +8,7 @@
       - REMOTE=replica-1
       - REMOTE_URL=gerrit2
       - DEBUG_PORT=5005
+      - REPLICATE_ON_STARTUP=false
     ports:
       - "8080:8080"
       - "29418:29418"
@@ -20,6 +21,7 @@
       - REMOTE=primary
       - REMOTE_URL=gerrit1
       - DEBUG_PORT=5006
+      - REPLICATE_ON_STARTUP=true
     ports:
       - "8081:8080"
       - "29419:29418"
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ApplyObjectMetrics.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ApplyObjectMetrics.java
index 78745bb..d41dd8f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ApplyObjectMetrics.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/ApplyObjectMetrics.java
@@ -15,6 +15,7 @@
 package com.googlesource.gerrit.plugins.replication.pull;
 
 import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.metrics.Counter0;
 import com.google.gerrit.metrics.Description;
 import com.google.gerrit.metrics.Field;
 import com.google.gerrit.metrics.MetricMaker;
@@ -28,6 +29,8 @@
   private final Timer1<String> executionTime;
   private final Timer1<String> end2EndTime;
 
+  private final Counter0 maxApiPayloadSizeReachedCounter;
+
   @Inject
   ApplyObjectMetrics(@PluginName String pluginName, MetricMaker metricMaker) {
     Field<String> field =
@@ -53,6 +56,13 @@
                 .setCumulative()
                 .setUnit(Description.Units.MILLISECONDS),
             field);
+    maxApiPayloadSizeReachedCounter =
+        metricMaker.newCounter(
+            "apply_object_max_api_payload_reached",
+            new Description(
+                    "Number of apply object operation with payload larger than maxApiPayloadSize")
+                .setRate()
+                .setUnit("errors"));
   }
 
   /**
@@ -74,4 +84,9 @@
   public Timer1.Context<String> startEnd2End(String name) {
     return end2EndTime.start(name);
   }
+
+  /** Increment metric when ref size is larger than maxApiPayloadSize. */
+  public void incrementMaxPayloadSizeReached() {
+    maxApiPayloadSizeReachedCounter.increment();
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java
index d8da65e..551b36c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/FetchOne.java
@@ -91,6 +91,7 @@
   private final int maxLockRetries;
   private int lockRetryCount;
   private final int id;
+  private String taskIdHex;
   private final long createdAt;
   private final FetchReplicationMetrics metrics;
   private final AtomicBoolean canceledWhileRunning;
@@ -119,6 +120,7 @@
     lockRetryCount = 0;
     maxLockRetries = pool.getLockErrorMaxRetries();
     id = ig.next();
+    taskIdHex = HexFormat.fromInt(id);
     stateLog = sl;
     createdAt = System.nanoTime();
     metrics = m;
@@ -158,7 +160,7 @@
 
   @Override
   public String toString() {
-    String print = "[" + HexFormat.fromInt(id) + "] fetch " + uri;
+    String print = "[" + taskIdHex + "] fetch " + uri;
 
     if (retryCount > 0) {
       print = "(retry " + retryCount + ") " + print;
@@ -288,13 +290,19 @@
     if (!pool.requestRunway(this)) {
       if (!canceled) {
         repLog.info(
-            "Rescheduling replication to {} to avoid collision with an in-flight fetch.", uri);
+            "Rescheduling [{}] replication to {} to avoid collision with an in-flight fetch.",
+            taskIdHex,
+            uri);
         pool.reschedule(this, Source.RetryReason.COLLISION);
       }
       return;
     }
 
-    repLog.info("Replication from {} started...", uri);
+    repLog.info(
+        "Replication [{}] from {} started for refs [{}] ...",
+        taskIdHex,
+        uri,
+        String.join(",", getRefs()));
     Timer1.Context<String> context = metrics.start(config.getName());
     try {
       long startedAt = context.getStartTime();
@@ -308,7 +316,8 @@
               .flatMap(metrics -> metrics.stop(config.getName()))
               .map(NANOSECONDS::toMillis);
       repLog.info(
-          "Replication from {} completed in {}ms, {}ms delay, {} retries{}",
+          "Replication [{}] from {} completed in {}ms, {}ms delay, {} retries{}",
+          taskIdHex,
           uri,
           elapsed,
           delay,
@@ -324,7 +333,8 @@
       // does not exist.  In this case NoRemoteRepositoryException is not
       // raised.
       String msg = e.getMessage();
-      repLog.error("Cannot replicate {}; Remote repository error: {}", projectName, msg);
+      repLog.error(
+          "Cannot replicate [{}] {}; Remote repository error: {}", taskIdHex, projectName, msg);
     } catch (NotSupportedException e) {
       stateLog.error("Cannot replicate from " + uri, e, getStatesAsArray());
     } catch (TransportException e) {
@@ -333,7 +343,7 @@
         lockRetryCount++;
         // The LockFailureException message contains both URI and reason
         // for this failure.
-        repLog.error("Cannot replicate from {}: {}", uri, e.getMessage());
+        repLog.error("Cannot replicate [{}] from {}: {}", taskIdHex, uri, e.getMessage());
 
         // The remote fetch operation should be retried.
         if (lockRetryCount <= maxLockRetries) {
@@ -344,16 +354,17 @@
           }
         } else {
           repLog.error(
-              "Giving up after {} occurrences of this error: {} during replication from {}",
+              "Giving up after {} occurrences of this error: {} during replication from [{}] {}",
               lockRetryCount,
               e.getMessage(),
+              taskIdHex,
               uri);
         }
       } else {
         if (canceledWhileRunning.get()) {
           logCanceledWhileRunningException(e);
         } else {
-          repLog.error("Cannot replicate from {}", uri, e);
+          repLog.error("Cannot replicate [{}] from {}", taskIdHex, uri, e);
           // The remote fetch operation should be retried.
           pool.reschedule(this, Source.RetryReason.TRANSPORT_ERROR);
         }
@@ -371,7 +382,7 @@
   }
 
   private void logCanceledWhileRunningException(TransportException e) {
-    repLog.info("Cannot replicate from {}. It was canceled while running", uri, e);
+    repLog.info("Cannot replicate [{}] from {}. It was canceled while running", taskIdHex, uri, e);
   }
 
   private void runImpl() throws IOException {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/RevisionReader.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/RevisionReader.java
index db46b23..cd6a0ea 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/RevisionReader.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/RevisionReader.java
@@ -55,9 +55,13 @@
   private GitRepositoryManager gitRepositoryManager;
   private Long maxRefSize;
   private final int maxDepth;
+  private ApplyObjectMetrics metrics;
 
   @Inject
-  public RevisionReader(GitRepositoryManager gitRepositoryManager, ReplicationConfig cfg) {
+  public RevisionReader(
+      GitRepositoryManager gitRepositoryManager,
+      ReplicationConfig cfg,
+      ApplyObjectMetrics metrics) {
     this.gitRepositoryManager = gitRepositoryManager;
     this.maxRefSize =
         cfg.getConfig()
@@ -65,6 +69,7 @@
     this.maxDepth =
         cfg.getConfig()
             .getInt("replication", CONFIG_MAX_API_HISTORY_DEPTH, DEFAULT_MAX_API_HISTORY_DEPTH);
+    this.metrics = metrics;
   }
 
   public Optional<RevisionData> read(
@@ -146,6 +151,7 @@
 
       return Optional.of(new RevisionData(parentObjectIds, commitRev, treeRev, blobs));
     } catch (LargeObjectException e) {
+      metrics.incrementMaxPayloadSizeReached();
       repLog.trace(
           "Ref {} size for project {} is greater than configured '{}'",
           refName,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java
index d86287a..d4b7819 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/api/DeleteRefCommand.java
@@ -25,7 +25,6 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.permissions.RefPermission;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Inject;
@@ -93,12 +92,6 @@
       URIish sourceUri = source.getURI(name);
 
       try {
-        projectState.get().checkStatePermitsWrite();
-        permissionBackend
-            .currentUser()
-            .project(projectState.get().getNameKey())
-            .ref(refName)
-            .check(RefPermission.DELETE);
 
         Context.setLocalEvent(true);
         deleteRef(name, refName);