Merge branch 'stable-3.0' into stable-3.1 * stable-3.0: Add newline at EOF as required by HAProxy Use GerritForge's archive-ci for downloading artifacts Fix issue with disabling ref-database Change-Id: I9b5ee3270e7aeecbead81ac4ede3fc0ea747cfce
diff --git a/setup_local_env/haproxy-config/haproxy.cfg b/setup_local_env/haproxy-config/haproxy.cfg index 94b22d8..de4f709 100644 --- a/setup_local_env/haproxy-config/haproxy.cfg +++ b/setup_local_env/haproxy-config/haproxy.cfg
@@ -65,4 +65,5 @@ timeout connect 10s timeout server 5m server ssh_node1 $HA_GERRIT_SITE1_HOSTNAME:$HA_GERRIT_SITE1_SSHD_PORT check inter 10s check port $HA_GERRIT_SITE1_HTTPD_PORT inter 10s - server ssh_node2 $HA_GERRIT_SITE2_HOSTNAME:$HA_GERRIT_SITE2_SSHD_PORT check inter 10s check port $HA_GERRIT_SITE2_HTTPD_PORT inter 10s backup \ No newline at end of file + server ssh_node2 $HA_GERRIT_SITE2_HOSTNAME:$HA_GERRIT_SITE2_SSHD_PORT check inter 10s check port $HA_GERRIT_SITE2_HTTPD_PORT inter 10s backup +
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/GitModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/GitModule.java index 4f7205d..dfafd8a 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/multisite/GitModule.java +++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/GitModule.java
@@ -28,6 +28,7 @@ @Override protected void configure() { + bind(ProjectVersionLogger.class).to(Log4jProjectVersionLogger.class); if (config.getSharedRefDb().isEnabled()) { install(new ValidationModule(config)); }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java index 708e707..8d8b206 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java +++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java
@@ -73,7 +73,7 @@ install(new CacheModule()); } if (config.event().synchronize()) { - install(new EventModule()); + install(new EventModule(config)); } if (config.index().synchronize()) { install(new IndexModule());
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventModule.java index 1c0c644..e122c24 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventModule.java +++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/event/EventModule.java
@@ -17,16 +17,34 @@ import com.google.gerrit.extensions.registration.DynamicSet; import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.server.events.EventListener; +import com.google.inject.Scopes; +import com.google.inject.multibindings.OptionalBinder; +import com.googlesource.gerrit.plugins.multisite.Configuration; import com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate; +import com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdateImpl; import java.util.concurrent.Executor; public class EventModule extends LifecycleModule { + private final Configuration config; + + public EventModule(Configuration config) { + this.config = config; + } + @Override protected void configure() { bind(Executor.class).annotatedWith(EventExecutor.class).toProvider(EventExecutorProvider.class); listener().to(EventExecutorProvider.class); DynamicSet.bind(binder(), EventListener.class).to(EventHandler.class); - DynamicSet.bind(binder(), EventListener.class).to(ProjectVersionRefUpdate.class); + OptionalBinder<ProjectVersionRefUpdate> projectVersionRefUpdateBinder = + OptionalBinder.newOptionalBinder(binder(), ProjectVersionRefUpdate.class); + if (config.getSharedRefDb().isEnabled()) { + DynamicSet.bind(binder(), EventListener.class).to(ProjectVersionRefUpdateImpl.class); + projectVersionRefUpdateBinder + .setBinding() + .to(ProjectVersionRefUpdateImpl.class) + .in(Scopes.SINGLETON); + } } }
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 73bab49..6924e71 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
@@ -14,291 +14,19 @@ package com.googlesource.gerrit.plugins.multisite.validation; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; - -import com.gerritforge.gerrit.globalrefdb.GlobalRefDbSystemError; -import com.google.common.collect.ImmutableSet; -import com.google.common.flogger.FluentLogger; -import com.google.gerrit.entities.Project; -import com.google.gerrit.entities.RefNames; -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 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; import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.RefUpdate; -import org.eclipse.jgit.lib.Repository; -@Singleton -public class ProjectVersionRefUpdate implements EventListener { - private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - private static final Set<RefUpdate.Result> SUCCESSFUL_RESULTS = - ImmutableSet.of(RefUpdate.Result.NEW, RefUpdate.Result.FORCED, RefUpdate.Result.NO_CHANGE); +public interface ProjectVersionRefUpdate { - public static final String MULTI_SITE_VERSIONING_REF = "refs/multi-site/version"; - public static final String MULTI_SITE_VERSIONING_VALUE_REF = "refs/multi-site/version/value"; - public static final Ref NULL_PROJECT_VERSION_REF = + String MULTI_SITE_VERSIONING_REF = "refs/multi-site/version"; + String MULTI_SITE_VERSIONING_VALUE_REF = "refs/multi-site/version/value"; + Ref NULL_PROJECT_VERSION_REF = new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, MULTI_SITE_VERSIONING_REF, ObjectId.zeroId()); - private final GitRepositoryManager gitRepositoryManager; - private final GitReferenceUpdated gitReferenceUpdated; - private final ProjectVersionLogger verLogger; + Optional<Long> getProjectLocalVersion(String projectName); - protected final SharedRefDatabaseWrapper sharedRefDb; - - @Inject - public ProjectVersionRefUpdate( - GitRepositoryManager gitRepositoryManager, - SharedRefDatabaseWrapper sharedRefDb, - GitReferenceUpdated gitReferenceUpdated, - ProjectVersionLogger verLogger) { - this.gitRepositoryManager = gitRepositoryManager; - this.sharedRefDb = sharedRefDb; - this.gitReferenceUpdated = gitReferenceUpdated; - this.verLogger = verLogger; - } - - @Override - public void onEvent(Event event) { - logger.atFine().log("Processing event type: " + event.type); - // Producer of the Event use RefUpdatedEvent to trigger the version update - if (!Context.isForwardedEvent() && event instanceof RefUpdatedEvent) { - updateProducerProjectVersionUpdate((RefUpdatedEvent) event); - } - } - - private boolean isSpecialRefName(String refName) { - return refName.startsWith(RefNames.REFS_SEQUENCES) - || refName.startsWith(RefNames.REFS_STARRED_CHANGES) - || refName.equals(MULTI_SITE_VERSIONING_REF); - } - - private void updateProducerProjectVersionUpdate(RefUpdatedEvent refUpdatedEvent) { - String refName = refUpdatedEvent.getRefName(); - - if (isSpecialRefName(refName)) { - logger.atFine().log( - "Found a special ref name %s, skipping update for %s", - refName, refUpdatedEvent.getProjectNameKey().get()); - return; - } - try { - Project.NameKey projectNameKey = refUpdatedEvent.getProjectNameKey(); - long newVersion = getCurrentGlobalVersionNumber(); - - Optional<RefUpdate> newProjectVersionRefUpdate = - updateLocalProjectVersion(projectNameKey, newVersion); - - if (newProjectVersionRefUpdate.isPresent()) { - verLogger.log(projectNameKey, newVersion, 0L); - - if (updateSharedProjectVersion( - projectNameKey, newProjectVersionRefUpdate.get().getNewObjectId(), newVersion)) { - gitReferenceUpdated.fire(projectNameKey, newProjectVersionRefUpdate.get(), null); - } - } else { - logger.atWarning().log( - "Ref %s not found on projet %s: skipping project version update", - refUpdatedEvent.getRefName(), projectNameKey); - } - } catch (LocalProjectVersionUpdateException | SharedProjectVersionUpdateException e) { - logger.atSevere().withCause(e).log( - "Issue encountered when updating version for project " - + refUpdatedEvent.getProjectNameKey()); - } - } - - private RefUpdate getProjectVersionRefUpdate(Repository repository, Long version) - throws IOException { - RefUpdate refUpdate = repository.getRefDatabase().newUpdate(MULTI_SITE_VERSIONING_REF, false); - refUpdate.setNewObjectId(getNewId(repository, version)); - refUpdate.setForceUpdate(true); - return refUpdate; - } - - private ObjectId getNewId(Repository repository, Long version) throws IOException { - ObjectInserter ins = repository.newObjectInserter(); - ObjectId newId = ins.insert(OBJ_BLOB, Long.toString(version).getBytes(UTF_8)); - ins.flush(); - return newId; - } - - private boolean updateSharedProjectVersion( - Project.NameKey projectNameKey, ObjectId newObjectId, Long newVersion) - throws SharedProjectVersionUpdateException { - - Ref sharedRef = - sharedRefDb - .get(projectNameKey, MULTI_SITE_VERSIONING_REF, String.class) - .map( - (String objectId) -> - new ObjectIdRef.Unpeeled( - Ref.Storage.NEW, MULTI_SITE_VERSIONING_REF, ObjectId.fromString(objectId))) - .orElse( - new ObjectIdRef.Unpeeled( - Ref.Storage.NEW, MULTI_SITE_VERSIONING_REF, ObjectId.zeroId())); - Optional<Long> sharedVersion = - sharedRefDb - .get(projectNameKey, MULTI_SITE_VERSIONING_VALUE_REF, String.class) - .map(Long::parseLong); - - try { - if (sharedVersion.isPresent() && sharedVersion.get() >= newVersion) { - logger.atWarning().log( - String.format( - "NOT Updating project %s version %s (value=%d) in shared ref-db because is more recent than the local one %s (value=%d) ", - projectNameKey.get(), - newObjectId, - newVersion, - sharedRef.getObjectId().getName(), - sharedVersion.get())); - return false; - } - - logger.atFine().log( - String.format( - "Updating shared project %s version to %s (value=%d)", - projectNameKey.get(), newObjectId, newVersion)); - - boolean success = sharedRefDb.compareAndPut(projectNameKey, sharedRef, newObjectId); - if (!success) { - String message = - String.format( - "Project version blob update failed for %s. Current value %s, new value: %s", - projectNameKey.get(), safeGetObjectId(sharedRef), newObjectId); - logger.atSevere().log(message); - throw new SharedProjectVersionUpdateException(message); - } - - success = - sharedRefDb.compareAndPut( - projectNameKey, - MULTI_SITE_VERSIONING_VALUE_REF, - sharedVersion.map(Object::toString).orElse(null), - newVersion.toString()); - if (!success) { - String message = - String.format( - "Project version update failed for %s. Current value %s, new value: %s", - projectNameKey.get(), safeGetObjectId(sharedRef), newObjectId); - logger.atSevere().log(message); - throw new SharedProjectVersionUpdateException(message); - } - - return true; - } catch (GlobalRefDbSystemError refDbSystemError) { - String message = - String.format( - "Error while updating shared project version for %s. Current value %s, new value: %s. Error: %s", - projectNameKey.get(), - sharedRef.getObjectId(), - newObjectId, - refDbSystemError.getMessage()); - logger.atSevere().withCause(refDbSystemError).log(message); - throw new SharedProjectVersionUpdateException(message); - } - } - - public Optional<Long> getProjectLocalVersion(String projectName) { - try (Repository repository = - gitRepositoryManager.openRepository(Project.NameKey.parse(projectName))) { - Optional<IntBlob> blob = IntBlob.parse(repository, MULTI_SITE_VERSIONING_REF); - if (blob.isPresent()) { - Long repoVersion = Integer.toUnsignedLong(blob.get().value()); - 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); - } - return Optional.empty(); - } - - public Optional<Long> getProjectRemoteVersion(String projectName) { - Optional<String> globalVersion = - sharedRefDb.get( - Project.NameKey.parse(projectName), MULTI_SITE_VERSIONING_VALUE_REF, String.class); - 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)); - } catch (NumberFormatException e) { - logger.atSevere().withCause(e).log( - "Unable to parse timestamp value %s into Long", longString); - return Optional.empty(); - } - } - - private Optional<RefUpdate> updateLocalProjectVersion( - Project.NameKey projectNameKey, long newVersionNumber) - throws LocalProjectVersionUpdateException { - logger.atFine().log( - "Updating local version for project %s with version %d", - projectNameKey.get(), newVersionNumber); - try (Repository repository = gitRepositoryManager.openRepository(projectNameKey)) { - RefUpdate refUpdate = getProjectVersionRefUpdate(repository, newVersionNumber); - RefUpdate.Result result = refUpdate.update(); - if (!isSuccessful(result)) { - String message = - String.format( - "RefUpdate failed with result %s for: project=%s, version=%d", - result.name(), projectNameKey.get(), newVersionNumber); - logger.atSevere().log(message); - throw new LocalProjectVersionUpdateException(message); - } - - return Optional.of(refUpdate); - } catch (IOException e) { - String message = "Cannot create versioning command for " + projectNameKey.get(); - logger.atSevere().withCause(e).log(message); - throw new LocalProjectVersionUpdateException(message); - } - } - - private long getCurrentGlobalVersionNumber() { - return System.currentTimeMillis() / 1000; - } - - private Boolean isSuccessful(RefUpdate.Result result) { - return SUCCESSFUL_RESULTS.contains(result); - } - - public static class LocalProjectVersionUpdateException extends Exception { - private static final long serialVersionUID = 7649956232401457023L; - - public LocalProjectVersionUpdateException(String projectName) { - super("Cannot update local project version of " + projectName); - } - } - - public static class SharedProjectVersionUpdateException extends Exception { - private static final long serialVersionUID = -9153858177700286314L; - - public SharedProjectVersionUpdateException(String projectName) { - super("Cannot update shared project version of " + projectName); - } - } + Optional<Long> getProjectRemoteVersion(String projectName); }
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 new file mode 100644 index 0000000..1e2403f --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateImpl.java
@@ -0,0 +1,304 @@ +// Copyright (C) 2022 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.validation; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; + +import com.gerritforge.gerrit.globalrefdb.GlobalRefDbSystemError; +import com.google.common.collect.ImmutableSet; +import com.google.common.flogger.FluentLogger; +import com.google.gerrit.entities.Project; +import com.google.gerrit.entities.RefNames; +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.googlesource.gerrit.plugins.multisite.ProjectVersionLogger; +import com.googlesource.gerrit.plugins.multisite.SharedRefDatabaseWrapper; +import com.googlesource.gerrit.plugins.multisite.forwarder.Context; +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; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.Repository; + +public class ProjectVersionRefUpdateImpl implements EventListener, ProjectVersionRefUpdate { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + private static final Set<RefUpdate.Result> SUCCESSFUL_RESULTS = + ImmutableSet.of(RefUpdate.Result.NEW, RefUpdate.Result.FORCED, RefUpdate.Result.NO_CHANGE); + + private final GitRepositoryManager gitRepositoryManager; + private final GitReferenceUpdated gitReferenceUpdated; + private final ProjectVersionLogger verLogger; + + protected final SharedRefDatabaseWrapper sharedRefDb; + + @Inject + public ProjectVersionRefUpdateImpl( + GitRepositoryManager gitRepositoryManager, + SharedRefDatabaseWrapper sharedRefDb, + GitReferenceUpdated gitReferenceUpdated, + ProjectVersionLogger verLogger) { + this.gitRepositoryManager = gitRepositoryManager; + this.sharedRefDb = sharedRefDb; + this.gitReferenceUpdated = gitReferenceUpdated; + this.verLogger = verLogger; + } + + @Override + public void onEvent(Event event) { + logger.atFine().log("Processing event type: " + event.type); + // Producer of the Event use RefUpdatedEvent to trigger the version update + if (!Context.isForwardedEvent() && event instanceof RefUpdatedEvent) { + updateProducerProjectVersionUpdate((RefUpdatedEvent) event); + } + } + + private boolean isSpecialRefName(String refName) { + return refName.startsWith(RefNames.REFS_SEQUENCES) + || refName.startsWith(RefNames.REFS_STARRED_CHANGES) + || refName.equals(MULTI_SITE_VERSIONING_REF); + } + + private void updateProducerProjectVersionUpdate(RefUpdatedEvent refUpdatedEvent) { + String refName = refUpdatedEvent.getRefName(); + + if (isSpecialRefName(refName)) { + logger.atFine().log( + "Found a special ref name %s, skipping update for %s", + refName, refUpdatedEvent.getProjectNameKey().get()); + return; + } + try { + Project.NameKey projectNameKey = refUpdatedEvent.getProjectNameKey(); + long newVersion = getCurrentGlobalVersionNumber(); + + Optional<RefUpdate> newProjectVersionRefUpdate = + updateLocalProjectVersion(projectNameKey, newVersion); + + if (newProjectVersionRefUpdate.isPresent()) { + verLogger.log(projectNameKey, newVersion, 0L); + + if (updateSharedProjectVersion( + projectNameKey, newProjectVersionRefUpdate.get().getNewObjectId(), newVersion)) { + gitReferenceUpdated.fire(projectNameKey, newProjectVersionRefUpdate.get(), null); + } + } else { + logger.atWarning().log( + "Ref %s not found on projet %s: skipping project version update", + refUpdatedEvent.getRefName(), projectNameKey); + } + } catch (LocalProjectVersionUpdateException | SharedProjectVersionUpdateException e) { + logger.atSevere().withCause(e).log( + "Issue encountered when updating version for project " + + refUpdatedEvent.getProjectNameKey()); + } + } + + private RefUpdate getProjectVersionRefUpdate(Repository repository, Long version) + throws IOException { + RefUpdate refUpdate = repository.getRefDatabase().newUpdate(MULTI_SITE_VERSIONING_REF, false); + refUpdate.setNewObjectId(getNewId(repository, version)); + refUpdate.setForceUpdate(true); + return refUpdate; + } + + private ObjectId getNewId(Repository repository, Long version) throws IOException { + ObjectInserter ins = repository.newObjectInserter(); + ObjectId newId = ins.insert(OBJ_BLOB, Long.toString(version).getBytes(UTF_8)); + ins.flush(); + return newId; + } + + private boolean updateSharedProjectVersion( + Project.NameKey projectNameKey, ObjectId newObjectId, Long newVersion) + throws SharedProjectVersionUpdateException { + + Ref sharedRef = + sharedRefDb + .get(projectNameKey, MULTI_SITE_VERSIONING_REF, String.class) + .map( + (String objectId) -> + new ObjectIdRef.Unpeeled( + Ref.Storage.NEW, MULTI_SITE_VERSIONING_REF, ObjectId.fromString(objectId))) + .orElse( + new ObjectIdRef.Unpeeled( + Ref.Storage.NEW, MULTI_SITE_VERSIONING_REF, ObjectId.zeroId())); + Optional<Long> sharedVersion = + sharedRefDb + .get(projectNameKey, MULTI_SITE_VERSIONING_VALUE_REF, String.class) + .map(Long::parseLong); + + try { + if (sharedVersion.isPresent() && sharedVersion.get() >= newVersion) { + logger.atWarning().log( + String.format( + "NOT Updating project %s version %s (value=%d) in shared ref-db because is more recent than the local one %s (value=%d) ", + projectNameKey.get(), + newObjectId, + newVersion, + sharedRef.getObjectId().getName(), + sharedVersion.get())); + return false; + } + + logger.atFine().log( + String.format( + "Updating shared project %s version to %s (value=%d)", + projectNameKey.get(), newObjectId, newVersion)); + + boolean success = sharedRefDb.compareAndPut(projectNameKey, sharedRef, newObjectId); + if (!success) { + String message = + String.format( + "Project version blob update failed for %s. Current value %s, new value: %s", + projectNameKey.get(), safeGetObjectId(sharedRef), newObjectId); + logger.atSevere().log(message); + throw new SharedProjectVersionUpdateException(message); + } + + success = + sharedRefDb.compareAndPut( + projectNameKey, + MULTI_SITE_VERSIONING_VALUE_REF, + sharedVersion.map(Object::toString).orElse(null), + newVersion.toString()); + if (!success) { + String message = + String.format( + "Project version update failed for %s. Current value %s, new value: %s", + projectNameKey.get(), safeGetObjectId(sharedRef), newObjectId); + logger.atSevere().log(message); + throw new SharedProjectVersionUpdateException(message); + } + + return true; + } catch (GlobalRefDbSystemError refDbSystemError) { + String message = + String.format( + "Error while updating shared project version for %s. Current value %s, new value: %s. Error: %s", + projectNameKey.get(), + sharedRef.getObjectId(), + newObjectId, + refDbSystemError.getMessage()); + logger.atSevere().withCause(refDbSystemError).log(message); + throw new SharedProjectVersionUpdateException(message); + } + } + + /* (non-Javadoc) + * @see com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate#getProjectLocalVersion(java.lang.String) + */ + @Override + public Optional<Long> getProjectLocalVersion(String projectName) { + try (Repository repository = + gitRepositoryManager.openRepository(Project.NameKey.parse(projectName))) { + Optional<IntBlob> blob = IntBlob.parse(repository, MULTI_SITE_VERSIONING_REF); + if (blob.isPresent()) { + Long repoVersion = Integer.toUnsignedLong(blob.get().value()); + 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); + } + return Optional.empty(); + } + + /* (non-Javadoc) + * @see com.googlesource.gerrit.plugins.multisite.validation.ProjectVersionRefUpdate#getProjectRemoteVersion(java.lang.String) + */ + public Optional<Long> getProjectRemoteVersion(String projectName) { + Optional<String> globalVersion = + sharedRefDb.get( + Project.NameKey.parse(projectName), MULTI_SITE_VERSIONING_VALUE_REF, String.class); + 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)); + } catch (NumberFormatException e) { + logger.atSevere().withCause(e).log( + "Unable to parse timestamp value %s into Long", longString); + return Optional.empty(); + } + } + + private Optional<RefUpdate> updateLocalProjectVersion( + Project.NameKey projectNameKey, long newVersionNumber) + throws LocalProjectVersionUpdateException { + logger.atFine().log( + "Updating local version for project %s with version %d", + projectNameKey.get(), newVersionNumber); + try (Repository repository = gitRepositoryManager.openRepository(projectNameKey)) { + RefUpdate refUpdate = getProjectVersionRefUpdate(repository, newVersionNumber); + RefUpdate.Result result = refUpdate.update(); + if (!isSuccessful(result)) { + String message = + String.format( + "RefUpdate failed with result %s for: project=%s, version=%d", + result.name(), projectNameKey.get(), newVersionNumber); + logger.atSevere().log(message); + throw new LocalProjectVersionUpdateException(message); + } + + return Optional.of(refUpdate); + } catch (IOException e) { + String message = "Cannot create versioning command for " + projectNameKey.get(); + logger.atSevere().withCause(e).log(message); + throw new LocalProjectVersionUpdateException(message); + } + } + + private long getCurrentGlobalVersionNumber() { + return System.currentTimeMillis() / 1000; + } + + private Boolean isSuccessful(RefUpdate.Result result) { + return SUCCESSFUL_RESULTS.contains(result); + } + + public static class LocalProjectVersionUpdateException extends Exception { + private static final long serialVersionUID = 7649956232401457023L; + + public LocalProjectVersionUpdateException(String projectName) { + super("Cannot update local project version of " + projectName); + } + } + + public static class SharedProjectVersionUpdateException extends Exception { + private static final long serialVersionUID = -9153858177700286314L; + + public SharedProjectVersionUpdateException(String projectName) { + super("Cannot update shared project version of " + projectName); + } + } +}
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 481d288..1719f38 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,9 +21,7 @@ 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; @@ -47,7 +45,6 @@ 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/validation/ProjectVersionRefUpdateTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/ProjectVersionRefUpdateTest.java index 2eefb5a..a1303ff 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
@@ -105,7 +105,7 @@ when(refUpdatedEvent.getProjectNameKey()).thenReturn(A_TEST_PROJECT_NAME_KEY); when(refUpdatedEvent.getRefName()).thenReturn(A_TEST_REF_NAME); - new ProjectVersionRefUpdate(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) + new ProjectVersionRefUpdateImpl(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) .onEvent(refUpdatedEvent); Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); @@ -150,7 +150,7 @@ when(refUpdatedEvent.getProjectNameKey()).thenReturn(A_TEST_PROJECT_NAME_KEY); when(refUpdatedEvent.getRefName()).thenReturn(A_TEST_REF_NAME); - new ProjectVersionRefUpdate(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) + new ProjectVersionRefUpdateImpl(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) .onEvent(refUpdatedEvent); Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); @@ -190,7 +190,7 @@ when(refUpdatedEvent.getProjectNameKey()).thenReturn(A_TEST_PROJECT_NAME_KEY); when(refUpdatedEvent.getRefName()).thenReturn(A_TEST_REF_NAME); - new ProjectVersionRefUpdate(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) + new ProjectVersionRefUpdateImpl(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) .onEvent(refUpdatedEvent); Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); @@ -227,7 +227,7 @@ when(refUpdatedEvent.getRefName()).thenReturn(magicRefName); repo.branch(magicRefName).commit().create(); - new ProjectVersionRefUpdate(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) + new ProjectVersionRefUpdateImpl(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) .onEvent(refUpdatedEvent); Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); @@ -242,7 +242,7 @@ when(refUpdatedEvent.getProjectNameKey()).thenReturn(Project.nameKey("aNonExistentProject")); when(refUpdatedEvent.getRefName()).thenReturn(A_TEST_REF_NAME); - new ProjectVersionRefUpdate(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) + new ProjectVersionRefUpdateImpl(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) .onEvent(refUpdatedEvent); Ref ref = repo.getRepository().findRef(MULTI_SITE_VERSIONING_REF); @@ -257,7 +257,7 @@ .thenReturn(Optional.of("123")); Optional<Long> version = - new ProjectVersionRefUpdate(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) + new ProjectVersionRefUpdateImpl(repoManager, sharedRefDb, gitReferenceUpdated, verLogger) .getProjectRemoteVersion(A_TEST_PROJECT_NAME); assertThat(version.isPresent()).isTrue();