Allow write of generic value in global-refdb
Currently it is only possible to write ObjectId. The possibility of
writing, for example, Long is needed to version the repositories.
Feature: Issue 12256
Change-Id: Id782729b8d7e786211ddc95c944dbb7b00c1e027
diff --git a/src/main/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabase.java b/src/main/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabase.java
index 4217751..24f86a7 100644
--- a/src/main/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabase.java
+++ b/src/main/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabase.java
@@ -57,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
diff --git a/src/test/java/com/gerritforge/gerrit/globalrefdb/FakeGlobalRefDatabase.java b/src/test/java/com/gerritforge/gerrit/globalrefdb/FakeGlobalRefDatabase.java
index d96f532..4d2e0b6 100644
--- a/src/test/java/com/gerritforge/gerrit/globalrefdb/FakeGlobalRefDatabase.java
+++ b/src/test/java/com/gerritforge/gerrit/globalrefdb/FakeGlobalRefDatabase.java
@@ -30,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
@@ -61,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);
diff --git a/src/test/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabaseTest.java b/src/test/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabaseTest.java
index 9e2ee81..02d060b 100644
--- a/src/test/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabaseTest.java
+++ b/src/test/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabaseTest.java
@@ -167,6 +167,34 @@
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);
}