Merge branch 'stable-3.1' * stable-3.1: Fix mocks in ProjectVersionRefUpdateTest Re-create global-refdb project versions when missing Add unit-test for SubscriberMetrics Silence exception when repository is not found Generate project version logging for analyzing replication lag Rely on replication for version ref propagation Change-Id: I5f62e452339da023dba5794490c9555792440b86
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/Log4jProjectVersionLogger.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/Log4jProjectVersionLogger.java new file mode 100644 index 0000000..c2c4b46 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/Log4jProjectVersionLogger.java
@@ -0,0 +1,48 @@ +// Copyright (C) 2020 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.entities.Project; +import com.google.gerrit.server.util.SystemLog; +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 Log4jProjectVersionLogger extends LibModuleLogFile implements ProjectVersionLogger { + private static final String LOG_NAME = "project_version_log"; + private final Logger verLog; + + @Inject + public Log4jProjectVersionLogger(SystemLog systemLog) { + super(systemLog, LOG_NAME, new PatternLayout("[%d{ISO8601}] [%t] %-5p : %m%n")); + this.verLog = LoggerFactory.getLogger(LOG_NAME); + } + + @Override + public void log(Project.NameKey projectName, long currentVersion, long replicationLag) { + if (replicationLag > 0) { + verLog.warn( + "{ \"project\":\"{}\", \"version\":{}, \"lag\":{} }", + projectName, + currentVersion, + replicationLag); + } else { + verLog.info("{ \"project\":\"{}\", \"version\":{} }", projectName, currentVersion); + } + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/ProjectVersionLogger.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/ProjectVersionLogger.java new file mode 100644 index 0000000..6ababb6 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/ProjectVersionLogger.java
@@ -0,0 +1,22 @@ +// Copyright (C) 2020 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.entities.Project; + +public interface ProjectVersionLogger { + + public void log(Project.NameKey projectName, long currentVersion, long replicationLag); +}
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 b4e5c94..4459859 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
@@ -25,6 +25,7 @@ import com.google.inject.Inject; import com.google.inject.Singleton; 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.RefReplicatedEvent; import com.googlesource.gerrit.plugins.replication.RefReplicationDoneEvent; @@ -46,14 +47,18 @@ private final Counter1<String> subscriberSuccessCounter; private final Counter1<String> subscriberFailureCounter; + private final ProjectVersionLogger verLogger; - public Map<String, Long> replicationStatusPerProject = new HashMap<>(); + private final Map<String, Long> replicationStatusPerProject = new HashMap<>(); + private final Map<String, Long> localVersionPerProject = new HashMap<>(); private ProjectVersionRefUpdate projectVersionRefUpdate; @Inject public SubscriberMetrics( - MetricMaker metricMaker, ProjectVersionRefUpdate projectVersionRefUpdate) { + MetricMaker metricMaker, + ProjectVersionRefUpdate projectVersionRefUpdate, + ProjectVersionLogger verLogger) { this.projectVersionRefUpdate = projectVersionRefUpdate; this.subscriberSuccessCounter = @@ -81,6 +86,8 @@ } return Collections.max(lags); }); + + this.verLogger = verLogger; } public void incrementSubscriberConsumedMessage() { @@ -115,10 +122,16 @@ Optional<Long> localVersion = projectVersionRefUpdate.getProjectLocalVersion(projectName.get()); if (remoteVersion.isPresent() && localVersion.isPresent()) { long lag = remoteVersion.get() - localVersion.get(); - logger.atFine().log( - "Published replication lag metric for project '%s' of %d sec(s) [local-ref=%d global-ref=%d]", - projectName, lag, localVersion.get(), remoteVersion.get()); - replicationStatusPerProject.put(projectName.get(), lag); + + if (!localVersion.get().equals(localVersionPerProject.get(projectName.get())) + || lag != replicationStatusPerProject.get(projectName.get())) { + logger.atFine().log( + "Published replication lag metric for project '%s' of %d sec(s) [local-ref=%d global-ref=%d]", + projectName, lag, localVersion.get(), remoteVersion.get()); + replicationStatusPerProject.put(projectName.get(), lag); + localVersionPerProject.put(projectName.get(), localVersion.get()); + verLogger.log(projectName, localVersion.get(), lag); + } } else { logger.atFine().log( "Did not publish replication lag metric for %s because the %s version is not defined",
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdate.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdate.java index 45d0806..bd910ad 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdate.java +++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdate.java
@@ -27,16 +27,18 @@ 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.gerrit.server.notedb.IntBlob; import com.google.inject.Inject; import com.google.inject.Singleton; +import com.googlesource.gerrit.plugins.multisite.ProjectVersionLogger; import com.googlesource.gerrit.plugins.multisite.SharedRefDatabaseWrapper; import com.googlesource.gerrit.plugins.multisite.forwarder.Context; -import com.googlesource.gerrit.plugins.replication.RefReplicationDoneEvent; import java.io.IOException; import java.util.Optional; import java.util.Set; +import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; import org.eclipse.jgit.lib.ObjectInserter; @@ -60,14 +62,21 @@ new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, MULTI_SITE_VERSIONING_REF, ObjectId.zeroId()); private final GitRepositoryManager gitRepositoryManager; + private final GitReferenceUpdated gitReferenceUpdated; + private final ProjectVersionLogger verLogger; protected final SharedRefDatabaseWrapper sharedRefDb; @Inject public ProjectVersionRefUpdate( - GitRepositoryManager gitRepositoryManager, SharedRefDatabaseWrapper sharedRefDb) { + GitRepositoryManager gitRepositoryManager, + SharedRefDatabaseWrapper sharedRefDb, + GitReferenceUpdated gitReferenceUpdated, + ProjectVersionLogger verLogger) { this.gitRepositoryManager = gitRepositoryManager; this.sharedRefDb = sharedRefDb; + this.gitReferenceUpdated = gitReferenceUpdated; + this.verLogger = verLogger; } @Override @@ -77,34 +86,12 @@ if (!Context.isForwardedEvent() && event instanceof RefUpdatedEvent) { updateProducerProjectVersionUpdate((RefUpdatedEvent) event); } - - // Consumers of the Event use RefReplicationDoneEvent to trigger the version update - if (Context.isForwardedEvent() && event instanceof RefReplicationDoneEvent) { - updateConsumerProjectVersion((RefReplicationDoneEvent) event); - } - } - - private void updateConsumerProjectVersion(RefReplicationDoneEvent refReplicationDoneEvent) { - Project.NameKey projectNameKey = refReplicationDoneEvent.getProjectNameKey(); - String refName = refReplicationDoneEvent.getRefName(); - - if (isSpecialRefName(refName)) { - logger.atFine().log( - "Found a special ref name %s, skipping update for %s", refName, projectNameKey.get()); - return; - } - try { - updateLocalProjectVersion( - projectNameKey, getLastRefUpdatedTimestamp(projectNameKey, refName)); - } catch (LocalProjectVersionUpdateException e) { - logger.atSevere().withCause(e).log( - "Issue encountered when updating version for project " + projectNameKey); - } } private boolean isSpecialRefName(String refName) { return refName.startsWith(RefNames.REFS_SEQUENCES) - || refName.startsWith(RefNames.REFS_STARRED_CHANGES); + || refName.startsWith(RefNames.REFS_STARRED_CHANGES) + || refName.equals(MULTI_SITE_VERSIONING_REF); } private void updateProducerProjectVersionUpdate(RefUpdatedEvent refUpdatedEvent) { @@ -127,6 +114,8 @@ updateLocalProjectVersion(projectNameKey, lastRefUpdatedTimestamp); if (newProjectVersionObjectId.isPresent()) { + verLogger.log(projectNameKey, lastRefUpdatedTimestamp.get(), 0L); + updateSharedProjectVersion( projectNameKey, currentProjectVersionRef, @@ -211,15 +200,24 @@ "Updating shared project version for %s. Current value %s, new value: %s", projectNameKey.get(), currentRef.getObjectId(), newObjectId)); try { + if (!sharedRefDb.exists(projectNameKey, MULTI_SITE_VERSIONING_REF)) { + currentRef = + new ObjectIdRef.Unpeeled(Ref.Storage.NEW, MULTI_SITE_VERSIONING_REF, ObjectId.zeroId()); + } + if (!sharedRefDb.exists(projectNameKey, MULTI_SITE_VERSIONING_VALUE_REF)) { + currentVersion = Optional.empty(); + } + boolean success = sharedRefDb.compareAndPut(projectNameKey, currentRef, newObjectId); if (!success) { String message = String.format( "Project version blob update failed for %s. Current value %s, new value: %s", - projectNameKey.get(), currentRef.getObjectId(), newObjectId); + projectNameKey.get(), safeGetObjectId(currentRef), newObjectId); logger.atSevere().log(message); throw new SharedProjectVersionUpdateException(message); } + success = sharedRefDb.compareAndPut( projectNameKey, @@ -230,7 +228,7 @@ String message = String.format( "Project version update failed for %s. Current value %s, new value: %s", - projectNameKey.get(), currentRef.getObjectId(), newObjectId); + projectNameKey.get(), safeGetObjectId(currentRef), newObjectId); logger.atSevere().log(message); throw new SharedProjectVersionUpdateException(message); } @@ -256,6 +254,8 @@ logger.atFine().log("Local project '%s' has version %d", projectName, repoVersion); return Optional.of(repoVersion); } + } catch (RepositoryNotFoundException re) { + logger.atFine().log("Project '%s' not found", projectName); } catch (IOException e) { logger.atSevere().withCause(e).log("Cannot read local project '%s' version", projectName); } @@ -269,6 +269,10 @@ return globalVersion.flatMap(longString -> getLongValueOf(longString)); } + private Object safeGetObjectId(Ref currentRef) { + return currentRef == null ? "null" : currentRef.getObjectId(); + } + private Optional<Long> getLongValueOf(String longString) { try { return Optional.ofNullable(Long.parseLong(longString)); @@ -324,6 +328,8 @@ logger.atSevere().log(message); throw new LocalProjectVersionUpdateException(message); } + + gitReferenceUpdated.fire(projectNameKey, refUpdate, null); return Optional.of(refUpdate.getNewObjectId()); } catch (IOException e) { String message = "Cannot create versioning command for " + projectNameKey.get();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ValidationModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ValidationModule.java index 1719f38..481d288 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ValidationModule.java +++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ValidationModule.java
@@ -21,7 +21,9 @@ import com.google.inject.Scopes; import com.googlesource.gerrit.plugins.multisite.Configuration; import com.googlesource.gerrit.plugins.multisite.LockWrapper; +import com.googlesource.gerrit.plugins.multisite.Log4jProjectVersionLogger; import com.googlesource.gerrit.plugins.multisite.Log4jSharedRefLogger; +import com.googlesource.gerrit.plugins.multisite.ProjectVersionLogger; import com.googlesource.gerrit.plugins.multisite.SharedRefDatabaseWrapper; import com.googlesource.gerrit.plugins.multisite.SharedRefLogger; import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.CustomSharedRefEnforcementByProject; @@ -45,6 +47,7 @@ bind(SharedRefDatabaseWrapper.class).in(Scopes.SINGLETON); bind(SharedRefLogger.class).to(Log4jSharedRefLogger.class); + bind(ProjectVersionLogger.class).to(Log4jProjectVersionLogger.class); factory(LockWrapper.Factory.class); factory(MultiSiteRepository.Factory.class);
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 new file mode 100644 index 0000000..99148fa --- /dev/null +++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/consumer/SubscriberMetricsTest.java
@@ -0,0 +1,98 @@ +// Copyright (C) 2020 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.consumer; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.gerritforge.gerrit.eventbroker.EventMessage; +import com.google.common.base.Suppliers; +import com.google.gerrit.entities.Project; +import com.google.gerrit.metrics.MetricMaker; +import com.google.gerrit.server.data.RefUpdateAttribute; +import com.google.gerrit.server.events.RefUpdatedEvent; +import com.google.gerrit.server.extensions.events.GitReferenceUpdated; +import com.googlesource.gerrit.plugins.multisite.ProjectVersionLogger; +import com.googlesource.gerrit.plugins.multisite.SharedRefDatabaseWrapper; +import com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate; +import java.util.Optional; +import java.util.UUID; +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 SubscriberMetricsTest { + private static final String A_TEST_PROJECT_NAME = "test-project"; + private static final Project.NameKey A_TEST_PROJECT_NAME_KEY = + Project.nameKey(A_TEST_PROJECT_NAME); + + @Mock private SharedRefDatabaseWrapper sharedRefDb; + @Mock private GitReferenceUpdated gitReferenceUpdated; + @Mock private MetricMaker metricMaker; + @Mock private ProjectVersionLogger verLogger; + @Mock private ProjectVersionRefUpdate projectVersionRefUpdate; + private SubscriberMetrics metrics; + private EventMessage.Header msgHeader; + + @Before + public void setup() throws Exception { + msgHeader = new EventMessage.Header(UUID.randomUUID(), UUID.randomUUID()); + metrics = new SubscriberMetrics(metricMaker, projectVersionRefUpdate, verLogger); + } + + @Test + public void shouldLogProjectVersionWhenReceivingRefUpdatedEventWithoutLag() { + Optional<Long> globalRefDbVersion = Optional.of(System.currentTimeMillis() / 1000); + when(projectVersionRefUpdate.getProjectRemoteVersion(A_TEST_PROJECT_NAME)) + .thenReturn(globalRefDbVersion); + when(projectVersionRefUpdate.getProjectLocalVersion(A_TEST_PROJECT_NAME)) + .thenReturn(globalRefDbVersion); + + EventMessage eventMessage = new EventMessage(msgHeader, newRefUpdateEvent()); + + metrics.updateReplicationStatusMetrics(eventMessage); + + verify(verLogger).log(A_TEST_PROJECT_NAME_KEY, globalRefDbVersion.get(), 0); + } + + @Test + public void shouldLogProjectVersionWhenReceivingRefUpdatedEventWithALag() { + Optional<Long> globalRefDbVersion = Optional.of(System.currentTimeMillis() / 1000); + long replicationLag = 60; + when(projectVersionRefUpdate.getProjectRemoteVersion(A_TEST_PROJECT_NAME)) + .thenReturn(globalRefDbVersion.map(ts -> ts + replicationLag)); + when(projectVersionRefUpdate.getProjectLocalVersion(A_TEST_PROJECT_NAME)) + .thenReturn(globalRefDbVersion); + + EventMessage eventMessage = new EventMessage(msgHeader, newRefUpdateEvent()); + + metrics.updateReplicationStatusMetrics(eventMessage); + + verify(verLogger).log(A_TEST_PROJECT_NAME_KEY, globalRefDbVersion.get(), replicationLag); + } + + private RefUpdatedEvent newRefUpdateEvent() { + RefUpdateAttribute refUpdate = new RefUpdateAttribute(); + refUpdate.project = A_TEST_PROJECT_NAME; + refUpdate.refName = "refs/heads/foo"; + refUpdate.newRev = "591727cfec5174368a7829f79741c41683d84c89"; + RefUpdatedEvent refUpdateEvent = new RefUpdatedEvent(); + refUpdateEvent.refUpdate = Suppliers.ofInstance(refUpdate); + return refUpdateEvent; + } +}
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 9a4020d..20b49c1 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
@@ -18,22 +18,24 @@ import static com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate.MULTI_SITE_VERSIONING_REF; import static com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate.MULTI_SITE_VERSIONING_VALUE_REF; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atMost; -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.google.gerrit.entities.Project; import com.google.gerrit.entities.RefNames; import com.google.gerrit.server.events.RefUpdatedEvent; +import com.google.gerrit.server.extensions.events.GitReferenceUpdated; import com.google.gerrit.server.project.ProjectConfig; import com.google.gerrit.testing.InMemoryRepositoryManager; import com.google.gerrit.testing.InMemoryTestEnvironment; import com.google.inject.Inject; +import com.googlesource.gerrit.plugins.multisite.ProjectVersionLogger; import com.googlesource.gerrit.plugins.multisite.SharedRefDatabaseWrapper; import com.googlesource.gerrit.plugins.multisite.forwarder.Context; import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.RefFixture; -import com.googlesource.gerrit.plugins.replication.RefReplicationDoneEvent; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Optional; @@ -59,6 +61,8 @@ @Mock RefUpdatedEvent refUpdatedEvent; @Mock SharedRefDatabaseWrapper sharedRefDb; + @Mock GitReferenceUpdated gitReferenceUpdated; + @Mock ProjectVersionLogger verLogger; @Inject private ProjectConfig.Factory projectConfigFactory; @Inject private InMemoryRepositoryManager repoManager; @@ -84,12 +88,21 @@ @Test public void producerShouldUpdateProjectVersionUponRefUpdatedEvent() throws IOException { Context.setForwardedEvent(false); + when(sharedRefDb.exists( + A_TEST_PROJECT_NAME_KEY, ProjectVersionRefUpdate.MULTI_SITE_VERSIONING_REF)) + .thenReturn(true); + when(sharedRefDb.exists( + A_TEST_PROJECT_NAME_KEY, ProjectVersionRefUpdate.MULTI_SITE_VERSIONING_VALUE_REF)) + .thenReturn(true); when(sharedRefDb.compareAndPut(any(Project.NameKey.class), any(Ref.class), any(ObjectId.class))) .thenReturn(true); + when(sharedRefDb.compareAndPut(any(Project.NameKey.class), any(String.class), any(), any())) + .thenReturn(true); when(refUpdatedEvent.getProjectNameKey()).thenReturn(A_TEST_PROJECT_NAME_KEY); when(refUpdatedEvent.getRefName()).thenReturn(A_TEST_REF_NAME); - new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refUpdatedEvent); + new ProjectVersionRefUpdate(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) + .onEvent(refUpdatedEvent); Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); @@ -101,6 +114,43 @@ ObjectLoader loader = repo.getRepository().open(ref.getObjectId()); String storedVersion = IOUtils.toString(loader.openStream(), StandardCharsets.UTF_8.name()); assertThat(Long.parseLong(storedVersion)).isEqualTo(masterCommit.getCommitTime()); + + verify(verLogger).log(A_TEST_PROJECT_NAME_KEY, masterCommit.getCommitTime(), 0); + } + + @Test + public void producerShouldCreateNewProjectVersionWhenMissingUponRefUpdatedEvent() + throws IOException { + Context.setForwardedEvent(false); + when(sharedRefDb.exists( + A_TEST_PROJECT_NAME_KEY, ProjectVersionRefUpdate.MULTI_SITE_VERSIONING_REF)) + .thenReturn(false); + when(sharedRefDb.exists( + A_TEST_PROJECT_NAME_KEY, ProjectVersionRefUpdate.MULTI_SITE_VERSIONING_VALUE_REF)) + .thenReturn(false); + + when(sharedRefDb.compareAndPut(any(Project.NameKey.class), any(Ref.class), any(ObjectId.class))) + .thenReturn(true); + when(sharedRefDb.compareAndPut(any(Project.NameKey.class), any(String.class), any(), any())) + .thenReturn(true); + when(refUpdatedEvent.getProjectNameKey()).thenReturn(A_TEST_PROJECT_NAME_KEY); + when(refUpdatedEvent.getRefName()).thenReturn(A_TEST_REF_NAME); + + new ProjectVersionRefUpdate(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) + .onEvent(refUpdatedEvent); + + Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); + + verify(sharedRefDb, atMost(1)) + .compareAndPut(any(Project.NameKey.class), isNull(), any(ObjectId.class)); + + assertThat(ref).isNotNull(); + + ObjectLoader loader = repo.getRepository().open(ref.getObjectId()); + String storedVersion = IOUtils.toString(loader.openStream(), StandardCharsets.UTF_8.name()); + assertThat(Long.parseLong(storedVersion)).isEqualTo(masterCommit.getCommitTime()); + + verify(verLogger).log(A_TEST_PROJECT_NAME_KEY, masterCommit.getCommitTime(), 0); } @Test @@ -122,10 +172,13 @@ when(refUpdatedEvent.getRefName()).thenReturn(magicRefName); repo.branch(magicRefName).commit().create(); - new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refUpdatedEvent); + new ProjectVersionRefUpdate(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) + .onEvent(refUpdatedEvent); Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); assertThat(ref).isNull(); + + verifyZeroInteractions(verLogger); } @Test @@ -134,57 +187,13 @@ when(refUpdatedEvent.getProjectNameKey()).thenReturn(Project.nameKey("aNonExistentProject")); when(refUpdatedEvent.getRefName()).thenReturn(A_TEST_REF_NAME); - new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refUpdatedEvent); + new ProjectVersionRefUpdate(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) + .onEvent(refUpdatedEvent); Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); assertThat(ref).isNull(); - } - @Test - public void consumerShouldUpdateProjectVersionUponRefReplicationDoneEvent() throws IOException { - Context.setForwardedEvent(true); - RefReplicationDoneEvent refReplicatedEvent = - new RefReplicationDoneEvent(A_TEST_PROJECT_NAME, A_TEST_REF_NAME, 1); - - new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refReplicatedEvent); - - Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); - assertThat(ref).isNotNull(); - - verify(sharedRefDb, never()) - .compareAndPut(any(Project.NameKey.class), any(Ref.class), any(ObjectId.class)); - - ObjectLoader loader = repo.getRepository().open(ref.getObjectId()); - String storedVersion = IOUtils.toString(loader.openStream(), StandardCharsets.UTF_8.name()); - assertThat(Long.parseLong(storedVersion)) - .isEqualTo(Integer.toUnsignedLong(masterCommit.getCommitTime())); - } - - @Test - public void consumerShouldNotUpdateProjectVersionUponSequenceRefReplicationDoneEvent() - throws Exception { - consumerShouldNotUpdateProjectVersionUponMagicRefReplicationDoneEvent(RefNames.REFS_SEQUENCES); - } - - @Test - public void consumerShouldNotUpdateProjectVersionUponStarredChangesRefReplicationDoneEvent() - throws Exception { - consumerShouldNotUpdateProjectVersionUponMagicRefReplicationDoneEvent( - RefNames.REFS_STARRED_CHANGES); - } - - private void consumerShouldNotUpdateProjectVersionUponMagicRefReplicationDoneEvent( - String magicRefPrefix) throws Exception { - String magicRef = magicRefPrefix + "/foo"; - Context.setForwardedEvent(true); - RefReplicationDoneEvent refReplicationDoneEvent = - new RefReplicationDoneEvent(A_TEST_PROJECT_NAME, magicRef, 1); - repo.branch(magicRef).commit().create(); - - new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refReplicationDoneEvent); - - Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); - assertThat(ref).isNull(); + verifyZeroInteractions(verLogger); } @Test @@ -193,31 +202,10 @@ .thenReturn(Optional.of("123")); Optional<Long> version = - new ProjectVersionRefUpdate(repoManager, sharedRefDb) + new ProjectVersionRefUpdate(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) .getProjectRemoteVersion(A_TEST_PROJECT_NAME); assertThat(version.isPresent()).isTrue(); assertThat(version.get()).isEqualTo(123L); } - - @Test - public void getLocalProjectVersionShouldReturnCorrectValue() throws IOException { - updateLocalVersion(); - Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); - assertThat(ref).isNotNull(); - - Optional<Long> version = - new ProjectVersionRefUpdate(repoManager, sharedRefDb) - .getProjectLocalVersion(A_TEST_PROJECT_NAME); - - assertThat(version.isPresent()).isTrue(); - assertThat(version.get()).isEqualTo(masterCommit.getCommitTime()); - } - - private void updateLocalVersion() { - Context.setForwardedEvent(true); - RefReplicationDoneEvent refReplicatedEvent = - new RefReplicationDoneEvent(A_TEST_PROJECT_NAME, A_TEST_REF_NAME, 1); - new ProjectVersionRefUpdate(repoManager, sharedRefDb).onEvent(refReplicatedEvent); - } }