Handle project versions upon project deletions
Project deletions were not handled by the multi-site plugin, possibly
causing false misalignments to be reported in the project_version_log
as well as the plugins_multi_site_multi_site_subscriber_subscriber_replication_status_sec_behind
metric.
Handle ProjectDeletionReplicationSucceededEvent to identify project
deletions:
- Log project deletion in the project_version_log, as such:
{ "project": "foo", "status": "DELETED" }
- Stop considering the project when publishing subscriber metrics
Bug: Issue 13598
Change-Id: I7259f520edc17b88bffce3e3e52a9ddfd476ed7f
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/Log4jProjectVersionLogger.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/Log4jProjectVersionLogger.java
index c2c4b46..23c720a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/Log4jProjectVersionLogger.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/Log4jProjectVersionLogger.java
@@ -45,4 +45,9 @@
verLog.info("{ \"project\":\"{}\", \"version\":{} }", projectName, currentVersion);
}
}
+
+ @Override
+ public void logDeleted(Project.NameKey projectName) {
+ verLog.info("{ \"project\":\"{}\", \"status\":\"DELETED\" }", projectName);
+ }
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/ProjectVersionLogger.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/ProjectVersionLogger.java
index 6ababb6..2ee2c13 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/ProjectVersionLogger.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/ProjectVersionLogger.java
@@ -19,4 +19,6 @@
public interface ProjectVersionLogger {
public void log(Project.NameKey projectName, long currentVersion, long replicationLag);
+
+ public void logDeleted(Project.NameKey projectName);
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetrics.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetrics.java
index e23a3c7..791c464 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetrics.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetrics.java
@@ -15,6 +15,7 @@
package com.googlesource.gerrit.plugins.multisite.consumer;
import com.gerritforge.gerrit.eventbroker.EventMessage;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.Project;
import com.google.gerrit.metrics.Counter1;
@@ -27,6 +28,7 @@
import com.googlesource.gerrit.plugins.multisite.MultiSiteMetrics;
import com.googlesource.gerrit.plugins.multisite.ProjectVersionLogger;
import com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate;
+import com.googlesource.gerrit.plugins.replication.events.ProjectDeletionReplicationSucceededEvent;
import com.googlesource.gerrit.plugins.replication.events.RefReplicatedEvent;
import com.googlesource.gerrit.plugins.replication.events.RefReplicationDoneEvent;
import com.googlesource.gerrit.plugins.replication.events.ReplicationScheduledEvent;
@@ -113,6 +115,21 @@
} else if (event instanceof RefUpdatedEvent) {
RefUpdatedEvent updated = (RefUpdatedEvent) event;
updateReplicationLagMetrics(updated.getProjectNameKey(), updated.getRefName());
+ } else if (event instanceof ProjectDeletionReplicationSucceededEvent) {
+ ProjectDeletionReplicationSucceededEvent projectDeletion =
+ (ProjectDeletionReplicationSucceededEvent) event;
+ removeProjectFromReplicationLagMetrics(projectDeletion.getProjectNameKey());
+ }
+ }
+
+ private void removeProjectFromReplicationLagMetrics(Project.NameKey projectName) {
+ Optional<Long> localVersion = projectVersionRefUpdate.getProjectLocalVersion(projectName.get());
+
+ if (!localVersion.isPresent() && localVersionPerProject.containsKey(projectName.get())) {
+ replicationStatusPerProject.remove(projectName.get());
+ localVersionPerProject.remove(projectName.get());
+ verLogger.logDeleted(projectName);
+ logger.atFine().log("Removed project '%s' from replication lag metrics", projectName);
}
}
@@ -138,4 +155,14 @@
projectName, localVersion.isPresent() ? "remote" : "local");
}
}
+
+ @VisibleForTesting
+ Long getReplicationStatus(String projectName) {
+ return replicationStatusPerProject.get(projectName);
+ }
+
+ @VisibleForTesting
+ Long getLocalVersion(String projectName) {
+ return localVersionPerProject.get(projectName);
+ }
}
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetricsTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetricsTest.java
index 8aa043d..ef7552a 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetricsTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetricsTest.java
@@ -14,7 +14,9 @@
package com.googlesource.gerrit.plugins.multisite.consumer;
+import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import com.gerritforge.gerrit.eventbroker.EventMessage;
@@ -27,8 +29,11 @@
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.googlesource.gerrit.plugins.multisite.ProjectVersionLogger;
import com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate;
+import com.googlesource.gerrit.plugins.replication.events.ProjectDeletionReplicationSucceededEvent;
+import java.net.URISyntaxException;
import java.util.Optional;
import java.util.UUID;
+import org.eclipse.jgit.transport.URIish;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -86,6 +91,93 @@
verify(verLogger).log(A_TEST_PROJECT_NAME_KEY, globalRefDbVersion.get(), replicationLag);
}
+ @Test
+ public void
+ shouldLogUponProjectDeletionSuccessWhenLocalVersionDoesNotExistAndSubscriberMetricsExist()
+ throws Exception {
+ long nowSecs = System.currentTimeMillis() / 1000;
+ long replicationLagSecs = 60;
+ Optional<Long> globalRefDbVersion = Optional.of(nowSecs);
+ when(projectVersionRefUpdate.getProjectRemoteVersion(A_TEST_PROJECT_NAME))
+ .thenReturn(globalRefDbVersion.map(ts -> ts + replicationLagSecs));
+ when(projectVersionRefUpdate.getProjectLocalVersion(A_TEST_PROJECT_NAME))
+ .thenReturn(globalRefDbVersion);
+
+ EventMessage refUpdateEventMessage = new EventMessage(msgHeader, newRefUpdateEvent());
+ metrics.updateReplicationStatusMetrics(refUpdateEventMessage);
+
+ assertThat(metrics.getReplicationStatus(A_TEST_PROJECT_NAME)).isEqualTo(replicationLagSecs);
+ assertThat(metrics.getLocalVersion(A_TEST_PROJECT_NAME)).isEqualTo(nowSecs);
+
+ when(projectVersionRefUpdate.getProjectLocalVersion(A_TEST_PROJECT_NAME))
+ .thenReturn(Optional.empty());
+
+ EventMessage projectDeleteEventMessage = new EventMessage(msgHeader, projectDeletionSuccess());
+ metrics.updateReplicationStatusMetrics(projectDeleteEventMessage);
+
+ verify(verLogger).logDeleted(A_TEST_PROJECT_NAME_KEY);
+ }
+
+ @Test
+ public void shouldNotLogUponProjectDeletionSuccessWhenSubscriberMetricsDoNotExist()
+ throws Exception {
+ EventMessage eventMessage = new EventMessage(msgHeader, projectDeletionSuccess());
+ when(projectVersionRefUpdate.getProjectLocalVersion(A_TEST_PROJECT_NAME))
+ .thenReturn(Optional.empty());
+
+ assertThat(metrics.getReplicationStatus(A_TEST_PROJECT_NAME)).isNull();
+ assertThat(metrics.getLocalVersion(A_TEST_PROJECT_NAME)).isNull();
+
+ metrics.updateReplicationStatusMetrics(eventMessage);
+
+ verifyZeroInteractions(verLogger);
+ }
+
+ @Test
+ public void shouldNotLogUponProjectDeletionSuccessWhenLocalVersionStillExists() throws Exception {
+ EventMessage eventMessage = new EventMessage(msgHeader, projectDeletionSuccess());
+ Optional<Long> anyRefVersionValue = Optional.of(System.currentTimeMillis() / 1000);
+ when(projectVersionRefUpdate.getProjectLocalVersion(A_TEST_PROJECT_NAME))
+ .thenReturn(anyRefVersionValue);
+
+ metrics.updateReplicationStatusMetrics(eventMessage);
+
+ verifyZeroInteractions(verLogger);
+ }
+
+ @Test
+ public void shouldRemoveProjectMetricsUponProjectDeletionSuccess() throws Exception {
+ long nowSecs = System.currentTimeMillis() / 1000;
+ long replicationLagSecs = 60;
+ Optional<Long> globalRefDbVersion = Optional.of(nowSecs);
+ when(projectVersionRefUpdate.getProjectRemoteVersion(A_TEST_PROJECT_NAME))
+ .thenReturn(globalRefDbVersion.map(ts -> ts + replicationLagSecs));
+ when(projectVersionRefUpdate.getProjectLocalVersion(A_TEST_PROJECT_NAME))
+ .thenReturn(globalRefDbVersion);
+
+ EventMessage eventMessage = new EventMessage(msgHeader, newRefUpdateEvent());
+
+ metrics.updateReplicationStatusMetrics(eventMessage);
+
+ assertThat(metrics.getReplicationStatus(A_TEST_PROJECT_NAME)).isEqualTo(replicationLagSecs);
+ assertThat(metrics.getLocalVersion(A_TEST_PROJECT_NAME)).isEqualTo(nowSecs);
+
+ when(projectVersionRefUpdate.getProjectLocalVersion(A_TEST_PROJECT_NAME))
+ .thenReturn(Optional.empty());
+ EventMessage projectDeleteEvent = new EventMessage(msgHeader, projectDeletionSuccess());
+
+ metrics.updateReplicationStatusMetrics(projectDeleteEvent);
+
+ assertThat(metrics.getReplicationStatus(A_TEST_PROJECT_NAME)).isNull();
+ assertThat(metrics.getLocalVersion(A_TEST_PROJECT_NAME)).isNull();
+ }
+
+ private ProjectDeletionReplicationSucceededEvent projectDeletionSuccess()
+ throws URISyntaxException {
+ return new ProjectDeletionReplicationSucceededEvent(
+ A_TEST_PROJECT_NAME, new URIish("git://target"));
+ }
+
private RefUpdatedEvent newRefUpdateEvent() {
RefUpdateAttribute refUpdate = new RefUpdateAttribute();
refUpdate.project = A_TEST_PROJECT_NAME;