Merge branch 'stable-2.12'

* stable-2.12:
  Revert "Replicate HEAD reference when replicating a project"
  Fetch parent groups of the authGroups
  Replicate HEAD reference when replicating a project
  Revert "Remove obsolete remote.NAME.timeout from config documentation"
  Add logging of cancelled replication events

Change-Id: I159cba10662e7aaa6cd8d2f7b121e99e87ac24ce
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
index e267da3..120c94c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/Destination.java
@@ -264,7 +264,7 @@
       if (e == null) {
         try (Repository git = gitManager.openRepository(project)) {
           try {
-            Ref head = git.getRef(Constants.HEAD);
+            Ref head = git.exactRef(Constants.HEAD);
             if (head != null
                 && head.isSymbolic()
                 && RefNames.REFS_CONFIG.equals(head.getLeaf().getName())) {
@@ -380,6 +380,7 @@
             pool.schedule(pushOp, delay, TimeUnit.SECONDS);
             break;
           case TRANSPORT_ERROR:
+          case REPOSITORY_MISSING:
           default:
             pushOp.setToRetry();
             pool.schedule(pushOp, retryDelay, TimeUnit.MINUTES);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java b/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
index 4a2d01b..002361a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/PushOne.java
@@ -15,6 +15,7 @@
 package com.googlesource.gerrit.plugins.replication;
 
 import static com.googlesource.gerrit.plugins.replication.ReplicationQueue.repLog;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
 
 import com.google.common.base.Throwables;
 import com.google.common.collect.LinkedListMultimap;
@@ -22,8 +23,8 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
-import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.metrics.Timer1;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -111,6 +112,7 @@
   private int lockRetryCount;
   private final int id;
   private final long createdAt;
+  private final ReplicationMetrics metrics;
 
   @Inject
   PushOne(final GitRepositoryManager grm,
@@ -124,6 +126,7 @@
       final ReplicationQueue rq,
       final IdGenerator ig,
       final ReplicationStateListener sl,
+      final ReplicationMetrics m,
       @Assisted final Project.NameKey d,
       @Assisted final URIish u) {
     gitManager = grm;
@@ -141,7 +144,8 @@
     maxLockRetries = pool.getLockErrorMaxRetries();
     id = ig.next();
     stateLog = sl;
-    createdAt = TimeUtil.nowMs();
+    createdAt = System.nanoTime();
+    metrics = m;
   }
 
   @Override
@@ -281,15 +285,17 @@
       return;
     }
 
-    long startedAt = TimeUtil.nowMs();
     repLog.info("Replication to " + uri + " started...");
+    Timer1.Context context = metrics.start(config.getName());
     try {
+      long startedAt = context.getStartTime();
       git = gitManager.openRepository(projectName);
       runImpl();
-      long finishedAt = TimeUtil.nowMs();
+      long elapsed = NANOSECONDS.toMillis(context.stop());
+      long delay = NANOSECONDS.toMillis(startedAt - createdAt);
       repLog.info("Replication to " + uri + " completed in "
-          + (finishedAt - startedAt) + "ms, "
-          + (startedAt - createdAt) + "ms delay, " + retryCount + " retries");
+          + (elapsed) + "ms, "
+          + (delay) + "ms delay, " + retryCount + " retries");
     } catch (RepositoryNotFoundException e) {
       stateLog.error("Cannot replicate " + projectName
           + "; Local repository error: "
@@ -350,7 +356,7 @@
   private void createRepository() {
     if (pool.isCreateMissingRepos()) {
       try {
-        final Ref head = git.getRef(Constants.HEAD);
+        final Ref head = git.exactRef(Constants.HEAD);
         if (replicationQueue.createProject(projectName, head != null ? head.getName() : null)) {
           repLog.warn("Missing repository created; retry replication to " + uri);
           pool.reschedule(this, Destination.RetryReason.REPOSITORY_MISSING);
@@ -369,18 +375,10 @@
   }
 
   private void runImpl() throws IOException {
-    Transport tn = Transport.open(git, uri);
     PushResult res;
-    try {
+    try (Transport tn = Transport.open(git, uri)) {
       res = pushVia(tn);
-    } finally {
-      try {
-        tn.close();
-      } catch (Throwable e2) {
-        repLog.warn("Unexpected error while closing " + uri, e2);
-      }
     }
-
     updateStates(res.getRemoteUpdates());
   }
 
@@ -500,11 +498,8 @@
 
   private Map<String, Ref> listRemote(Transport tn)
       throws NotSupportedException, TransportException {
-    FetchConnection fc = tn.openFetch();
-    try {
+    try (FetchConnection fc = tn.openFetch()) {
       return fc.getRefsMap();
-    } finally {
-      fc.close();
     }
   }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicatedEvent.java b/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicatedEvent.java
index f200194..05936dd 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicatedEvent.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicatedEvent.java
@@ -23,6 +23,8 @@
 import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
 
 public class RefReplicatedEvent extends RefEvent {
+  static final String TYPE = "ref-replicated";
+
   public final String project;
   public final String ref;
   public final String targetNode;
@@ -31,7 +33,7 @@
 
   public RefReplicatedEvent(String project, String ref, String targetNode,
       RefPushResult status, RemoteRefUpdate.Status refStatus) {
-    super("ref-replicated");
+    super(TYPE);
     this.project = project;
     this.ref = ref;
     this.targetNode = targetNode;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicationDoneEvent.java b/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicationDoneEvent.java
index fe92bc8..cde3e9e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicationDoneEvent.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/RefReplicationDoneEvent.java
@@ -18,12 +18,14 @@
 import com.google.gerrit.server.events.RefEvent;
 
 public class RefReplicationDoneEvent extends RefEvent {
+  static final String TYPE = "ref-replication-done";
+
   public final String project;
   public final String ref;
   public final int nodesCount;
 
   public RefReplicationDoneEvent(String project, String ref, int nodesCount) {
-    super("ref-replication-done");
+    super(TYPE);
     this.project = project;
     this.ref = ref;
     this.nodesCount = nodesCount;
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationMetrics.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationMetrics.java
new file mode 100644
index 0000000..ad266da
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationMetrics.java
@@ -0,0 +1,42 @@
+// 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.replication;
+
+import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.Field;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.metrics.Timer1;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+@Singleton
+public class ReplicationMetrics {
+  Timer1<String> executionTime;
+
+  @Inject
+  ReplicationMetrics(MetricMaker metricMaker) {
+    executionTime = metricMaker.newTimer(
+        "replication_latency",
+        new Description("Time spent pushing to remote destination.")
+          .setCumulative()
+          .setUnit(Description.Units.SECONDS),
+        Field.ofString("destination"));
+  }
+
+  Timer1.Context start(String name) {
+    return executionTime.start(name);
+  }
+
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationModule.java b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationModule.java
index a5d0b82..e813885 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/ReplicationModule.java
@@ -14,7 +14,6 @@
 
 package com.googlesource.gerrit.plugins.replication;
 
-import static com.googlesource.gerrit.plugins.replication.ReplicationState.RefPushResult.SUCCEEDED;
 import static com.googlesource.gerrit.plugins.replication.StartReplicationCapability.START_REPLICATION;
 
 import com.google.gerrit.extensions.annotations.Exports;
@@ -31,8 +30,6 @@
 import com.google.inject.assistedinject.FactoryModuleBuilder;
 import com.google.inject.internal.UniqueAnnotations;
 
-import org.eclipse.jgit.transport.RemoteRefUpdate;
-
 
 class ReplicationModule extends AbstractModule {
   @Override
@@ -66,8 +63,7 @@
     bind(ReplicationConfig.class).to(AutoReloadConfigDecorator.class);
     bind(ReplicationStateListener.class).to(ReplicationStateLogger.class);
 
-    EventTypes.registerClass(new RefReplicatedEvent(null, null, null,
-        SUCCEEDED, RemoteRefUpdate.Status.OK));
-    EventTypes.registerClass(new RefReplicationDoneEvent(null, null, 0));
+    EventTypes.register(RefReplicatedEvent.TYPE, RefReplicatedEvent.class);
+    EventTypes.register(RefReplicationDoneEvent.TYPE, RefReplicationDoneEvent.class);
   }
 }