Use put operation for project version update Project version update is used to generate metrics about the project replication state. This information doesn't have to be milliseconds precise, if two updates happened within a short period of time we do not have to guarantee that the latest one will be stored as long as the project version ref is updated to one of those two. This means that using `compareAndPut` operation is not necessary and more lightweight `put` operation can be used instead. Bug: Issue 297440086 Change-Id: I6d3413f86c55f5fc9ab6323d30628e58ad74a137
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateImpl.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateImpl.java index 14a421e..e07fef0 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateImpl.java +++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateImpl.java
@@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.flogger.FluentLogger; import com.google.gerrit.entities.Project; +import com.google.gerrit.entities.Project.NameKey; import com.google.gerrit.entities.RefNames; import com.google.gerrit.server.events.Event; import com.google.gerrit.server.events.EventListener; @@ -143,7 +144,6 @@ .get(projectNameKey, MULTI_SITE_VERSIONING_VALUE_REF, String.class) .map(Long::parseLong); - String sharedValue = sharedVersion.map(Object::toString).orElse(null); try { if (sharedVersion.isPresent() && sharedVersion.get() >= newVersion) { logger.atWarning().log( @@ -157,29 +157,41 @@ String.format( "Updating shared project %s value to %d", projectNameKey.get(), newVersion)); - boolean success = - sharedRefDb.compareAndPut( - projectNameKey, MULTI_SITE_VERSIONING_VALUE_REF, sharedValue, newVersion.toString()); - if (!success) { - String message = - String.format( - "Project value update failed for %s. Current value %s, new value: %s", - projectNameKey.get(), sharedValue, newVersion); - logger.atSevere().log(message); - throw new SharedProjectVersionUpdateException(message); - } - + updateProjectVersionValue(projectNameKey, newVersion, sharedVersion); return true; } catch (GlobalRefDbSystemError refDbSystemError) { String message = String.format( "Error while updating shared project value for %s. Current value %s, new value: %s. Error: %s", - projectNameKey.get(), sharedValue, newVersion, refDbSystemError.getMessage()); + projectNameKey.get(), + sharedVersion.map(Object::toString).orElse(null), + newVersion, + refDbSystemError.getMessage()); logger.atSevere().withCause(refDbSystemError).log(message); throw new SharedProjectVersionUpdateException(message); } } + private void updateProjectVersionValue( + NameKey projectNameKey, Long newVersion, Optional<Long> sharedVersion) { + try { + if (sharedRefDb.isSetOperationSupported()) { + sharedRefDb.put(projectNameKey, MULTI_SITE_VERSIONING_VALUE_REF, newVersion.toString()); + return; + } + } catch (NoSuchMethodError e) { + logger.atSevere().log( + "Global-refdb library is outdated and is not supporting " + + "'put' method, update global-refdb to the newest version. Falling back to 'compareAndPut'"); + } + + sharedRefDb.compareAndPut( + projectNameKey, + MULTI_SITE_VERSIONING_VALUE_REF, + sharedVersion.map(Object::toString).orElse(null), + newVersion.toString()); + } + /* (non-Javadoc) * @see com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate#getProjectLocalVersion(java.lang.String) */
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/DisabledSharedRefLogger.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/DisabledSharedRefLogger.java index b7ad98c..fc2259f 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/DisabledSharedRefLogger.java +++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/DisabledSharedRefLogger.java
@@ -36,4 +36,7 @@ @Override public <T> void logRefUpdate(String project, String refName, T currRef, T newRefValue) {} + + @Override + public <T> void logRefUpdate(String project, String refName, T newRefValue) {} }
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 63da1b8..d99fb4b 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,16 +18,21 @@ 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.anyLong; +import static org.mockito.ArgumentMatchers.anyString; 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.gerritforge.gerrit.globalrefdb.validation.ProjectsFilter; import com.gerritforge.gerrit.globalrefdb.validation.SharedRefDatabaseWrapper; +import com.google.common.base.Suppliers; import com.google.gerrit.entities.Project; import com.google.gerrit.entities.RefNames; +import com.google.gerrit.server.data.RefUpdateAttribute; import com.google.gerrit.server.events.Event; import com.google.gerrit.server.events.RefUpdatedEvent; import com.google.gerrit.server.extensions.events.GitReferenceUpdated; @@ -122,6 +127,35 @@ } @Test + public void producerShouldUsePutInsteadOfCompareAndPutWhenExtendedGlobalRefDb() + throws IOException { + when(sharedRefDb.isSetOperationSupported()).thenReturn(true); + RefUpdatedEvent refUpdatedEvent = new RefUpdatedEvent(); + RefUpdateAttribute refUpdatedAttribute = new RefUpdateAttribute(); + refUpdatedAttribute.project = A_TEST_PROJECT_NAME_KEY.get(); + refUpdatedAttribute.refName = A_TEST_REF_NAME; + refUpdatedEvent.refUpdate = Suppliers.memoize(() -> refUpdatedAttribute); + + new ProjectVersionRefUpdateImpl( + repoManager, sharedRefDb, gitReferenceUpdated, verLogger, projectsFilter) + .onEvent(refUpdatedEvent); + + Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); + + verify(sharedRefDb, never()) + .compareAndPut(any(Project.NameKey.class), anyString(), anyLong(), anyLong()); + + verify(sharedRefDb).put(any(Project.NameKey.class), any(String.class), any(String.class)); + assertThat(ref).isNotNull(); + + ObjectLoader loader = repo.getRepository().open(ref.getObjectId()); + long storedVersion = readLongObject(loader); + assertThat(storedVersion).isGreaterThan((long) masterCommit.getCommitTime()); + + verify(verLogger).log(A_TEST_PROJECT_NAME_KEY, storedVersion, 0); + } + + @Test public void producerShouldUpdateProjectVersionUponForcedPushRefUpdatedEvent() throws Exception { Context.setForwardedEvent(false);