Merge branch 'stable-3.1' * stable-3.1: Set version to 3.1.1 Set version to 3.0.1 Allow write of generic value in global-refdb Expose getter to return values stored in the SharedRefDB Add bazel build Set version to 3.1.0-rc1 Change-Id: Ied09c05097b6374311bd6c5f43dc83649badbf3d
diff --git a/src/main/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabase.java b/src/main/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabase.java index ecaa14b..3b75019 100644 --- a/src/main/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabase.java +++ b/src/main/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabase.java
@@ -15,6 +15,7 @@ package com.gerritforge.gerrit.globalrefdb; import com.google.gerrit.entities.Project; +import java.util.Optional; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; @@ -56,6 +57,21 @@ throws GlobalRefDbSystemError; /** + * Compare a value of generic type T, and put if it is up-to-date with the current. + * + * <p>Compare and put are executed as an atomic operation. + * + * @param project project name of the ref. + * @param refName to store the value for. + * @param currValue current expected value in the DB. + * @param newValue new value to store. + * @return true if the put was successful; false otherwise. + * @throws GlobalRefDbSystemError the reference cannot be put due to a system error. + */ + <T> boolean compareAndPut(Project.NameKey project, String refName, T currValue, T newValue) + throws GlobalRefDbSystemError; + + /** * Lock a reference. * * @param project project name @@ -81,4 +97,14 @@ * @throws GlobalRefDbSystemError project cannot be removed due to a system error. */ void remove(Project.NameKey project) throws GlobalRefDbSystemError; + + /** + * Return value for a specific project and ref name + * + * @param project project name + * @param refName reference name + * @return {@link java.util.Optional} of the value + * @throws GlobalRefDbSystemError value cannot be returned due to a system error. + */ + <T> Optional<T> get(Project.NameKey project, String refName) throws GlobalRefDbSystemError; }
diff --git a/src/test/java/com/gerritforge/gerrit/globalrefdb/FakeGlobalRefDatabase.java b/src/test/java/com/gerritforge/gerrit/globalrefdb/FakeGlobalRefDatabase.java index deed53b..40d7a8e 100644 --- a/src/test/java/com/gerritforge/gerrit/globalrefdb/FakeGlobalRefDatabase.java +++ b/src/test/java/com/gerritforge/gerrit/globalrefdb/FakeGlobalRefDatabase.java
@@ -16,6 +16,7 @@ import com.google.common.collect.MapMaker; import com.google.gerrit.entities.Project; +import java.util.Optional; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; @@ -29,12 +30,14 @@ private ConcurrentMap<Project.NameKey, ConcurrentMap<String, AtomicReference<ObjectId>>> keyValueStore; + private ConcurrentMap<String, AtomicReference<?>> genericKeyValueStore; private ConcurrentMap<Project.NameKey, ConcurrentMap<String, AtomicReference<Lock>>> refLockStore; public FakeGlobalRefDatabase() { keyValueStore = new MapMaker().concurrencyLevel(1).makeMap(); refLockStore = new MapMaker().concurrencyLevel(1).makeMap(); + genericKeyValueStore = new MapMaker().concurrencyLevel(1).makeMap(); } @Override @@ -60,6 +63,20 @@ } @Override + @SuppressWarnings("unchecked") + public <T> boolean compareAndPut(Project.NameKey project, String refName, T currValue, T newValue) + throws GlobalRefDbSystemError { + String key = String.format("%s/%s", project.get(), refName); + AtomicReference<T> storedValue = (AtomicReference<T>) genericKeyValueStore.get(key); + if (storedValue == null) { + genericKeyValueStore.put(key, new AtomicReference<>(newValue)); + return true; + } + + return storedValue.compareAndSet(currValue, newValue); + } + + @Override public AutoCloseable lockRef(Project.NameKey project, String refName) throws GlobalRefDbLockException { ConcurrentMap<String, AtomicReference<Lock>> projectRefLock = projectRefLock(project); @@ -86,6 +103,14 @@ keyValueStore.remove(project); } + @Override + @SuppressWarnings("unchecked") + public <T> Optional<T> get(Project.NameKey project, String refName) + throws GlobalRefDbSystemError { + return Optional.ofNullable((T) projectRefDb(project).get(refName)) + .map(v -> ((AtomicReference<T>) v).get()); + } + private ConcurrentMap<String, AtomicReference<ObjectId>> projectRefDb(Project.NameKey project) { ConcurrentMap<String, AtomicReference<ObjectId>> projectRefDb = keyValueStore.get(project); if (projectRefDb == null) {
diff --git a/src/test/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabaseTest.java b/src/test/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabaseTest.java index f26484c..b2656bb 100644 --- a/src/test/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabaseTest.java +++ b/src/test/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabaseTest.java
@@ -19,6 +19,7 @@ import com.google.gerrit.acceptance.AbstractDaemonTest; import com.google.gerrit.entities.RefNames; +import java.util.Optional; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import org.eclipse.jgit.lib.ObjectId; @@ -153,6 +154,48 @@ } } + @Test + public void shouldReturnValueInTheGlobalRefDB() { + objectUnderTest.compareAndPut(project, initialRef, objectId1); + Optional<ObjectId> o = objectUnderTest.get(project, initialRef.getName()); + assertThat(o.isPresent()).isTrue(); + assertThat(o.get()).isEqualTo(objectId1); + } + + @Test + public void shouldReturnEmptyIfValueIsNotInTheGlobalRefDB() { + Optional<ObjectId> o = objectUnderTest.get(project, "nonExistentRef"); + assertThat(o.isPresent()).isFalse(); + } + + @Test + public void shouldCreateGenericEntryInTheGlobalRefDBWhenFirstValue() { + assertThat(objectUnderTest.compareAndPut(project, refName, null, new Object())).isTrue(); + } + + @Test + public void shouldUpdateGenericEntryWithNewRef() throws Exception { + createChange(refName); + + Object object1 = new Object(); + objectUnderTest.compareAndPut(project, refName, null, object1); + + Object object2 = new Object(); + assertThat(objectUnderTest.compareAndPut(project, refName, object1, object2)).isTrue(); + } + + @Test + public void shouldRejectGenericUpdateWhenLocalRepoIsOutdated() throws Exception { + createChange(refName); + + Object object1 = new Object(); + objectUnderTest.compareAndPut(project, refName, null, object1); + + Object object2 = new Object(); + Object object3 = new Object(); + assertThat(objectUnderTest.compareAndPut(project, refName, object2, object3)).isFalse(); + } + private Ref ref(String refName, ObjectId objectId) { return new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, refName, objectId); }