Add put method to create/update global-refdb entry
This change extends the ExtendedBrokerApi interface with the
capability to put value in global-refdb. Opposite to compareAndPut
this method is not checking current global-refdb value.
Bug: Issue 297440085
Change-Id: Iba779d984e7888aeb9985e918241ebab4fca4c93
diff --git a/src/main/java/com/gerritforge/gerrit/globalrefdb/ExtendedGlobalRefDatabase.java b/src/main/java/com/gerritforge/gerrit/globalrefdb/ExtendedGlobalRefDatabase.java
new file mode 100644
index 0000000..c6f8de7
--- /dev/null
+++ b/src/main/java/com/gerritforge/gerrit/globalrefdb/ExtendedGlobalRefDatabase.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 GerritForge Ltd
+//
+// 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.gerritforge.gerrit.globalrefdb;
+
+import com.google.gerrit.entities.Project;
+
+public interface ExtendedGlobalRefDatabase extends GlobalRefDatabase {
+
+ /**
+ * Set a value of generic type T.
+ *
+ * <p>Set is executed as an atomic operation.
+ *
+ * @param project project name of the ref.
+ * @param refName to store the value for.
+ * @param newValue new value to store.
+ * @param <T> Type of the current and new value
+ * @throws GlobalRefDbSystemError the reference cannot be set due to a system error.
+ */
+ <T> void put(Project.NameKey project, String refName, T newValue) throws GlobalRefDbSystemError;
+}
diff --git a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/Log4jSharedRefLogger.java b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/Log4jSharedRefLogger.java
index 3b4dbb2..04ffeed 100644
--- a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/Log4jSharedRefLogger.java
+++ b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/Log4jSharedRefLogger.java
@@ -140,6 +140,18 @@
}
}
+ @Override
+ public <T> void logRefUpdate(String project, String refName, T newRefValue) {
+ if (newRefValue != null) {
+ sharedRefDBLog.info(
+ gson.toJson(
+ new SharedRefLogEntry.UpdateRef(
+ project, refName, null, safeToString(newRefValue), null, null)));
+ } else {
+ sharedRefDBLog.info(gson.toJson(new SharedRefLogEntry.DeleteRef(project, refName, null)));
+ }
+ }
+
/**
* {@inheritDoc}.
*
diff --git a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDBMetrics.java b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDBMetrics.java
index ff483ab..c907831 100644
--- a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDBMetrics.java
+++ b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDBMetrics.java
@@ -28,6 +28,7 @@
private final Timer0 getOperationExecutionTime;
private final Timer0 existsExecutionTime;
private Timer0 compareAndPutExecutionTime;
+ private Timer0 setExecutionTime;
private Timer0 removeExecutionTime;
private Timer0 isUpToDateExecutionTime;
@@ -39,6 +40,12 @@
new Description("Time spent on compareAndPut.")
.setCumulative()
.setUnit(Description.Units.MILLISECONDS));
+ setExecutionTime =
+ metricMaker.newTimer(
+ "global_refdb/set_latency",
+ new Description("Time spent on set.")
+ .setCumulative()
+ .setUnit(Description.Units.MILLISECONDS));
getOperationExecutionTime =
metricMaker.newTimer(
"global_refdb/get_latency",
@@ -76,6 +83,10 @@
return compareAndPutExecutionTime.start();
}
+ public Context startSetExecutionTime() {
+ return setExecutionTime.start();
+ }
+
public Context startGetExecutionTime() {
return getOperationExecutionTime.start();
}
diff --git a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDatabaseWrapper.java b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDatabaseWrapper.java
index c3b41e8..c9a6c7d 100644
--- a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDatabaseWrapper.java
+++ b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDatabaseWrapper.java
@@ -14,6 +14,7 @@
package com.gerritforge.gerrit.globalrefdb.validation;
+import com.gerritforge.gerrit.globalrefdb.ExtendedGlobalRefDatabase;
import com.gerritforge.gerrit.globalrefdb.GlobalRefDatabase;
import com.gerritforge.gerrit.globalrefdb.GlobalRefDbLockException;
import com.gerritforge.gerrit.globalrefdb.GlobalRefDbSystemError;
@@ -35,7 +36,7 @@
* {@link NoopSharedRefDatabase} instance is wrapped instead.
*/
@Singleton
-public class SharedRefDatabaseWrapper implements GlobalRefDatabase {
+public class SharedRefDatabaseWrapper implements ExtendedGlobalRefDatabase {
private static final FluentLogger log = FluentLogger.forEnclosingClass();
private static final GlobalRefDatabase NOOP_REFDB = new NoopSharedRefDatabase();
@@ -99,6 +100,23 @@
}
}
+ @Override
+ public <T> void put(Project.NameKey project, String refName, T newValue)
+ throws GlobalRefDbSystemError {
+ if (!isSetOperationSupported()) {
+ throw new UnsupportedOperationException(
+ "GlobalRefDb implementation doesn't support set operation");
+ }
+ try (Context context = metrics.startSetExecutionTime()) {
+ ((ExtendedGlobalRefDatabase) sharedRefDb()).put(project, refName, newValue);
+ sharedRefLogger.logRefUpdate(project.get(), refName, newValue);
+ }
+ }
+
+ public boolean isSetOperationSupported() {
+ return sharedRefDb() instanceof ExtendedGlobalRefDatabase;
+ }
+
/** {@inheritDoc}. The operation is logged. */
@Override
public AutoCloseable lockRef(Project.NameKey project, String refName)
diff --git a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefLogEntry.java b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefLogEntry.java
index c72ab7c..4b8132f 100644
--- a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefLogEntry.java
+++ b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefLogEntry.java
@@ -41,7 +41,7 @@
UpdateRef(
String projectName,
String refName,
- String oldId,
+ @Nullable String oldId,
String newId,
@Nullable GitPerson committer,
@Nullable String comment) {
@@ -68,7 +68,7 @@
public String refName;
public String oldId;
- DeleteRef(String projectName, String refName, String oldId) {
+ DeleteRef(String projectName, String refName, @Nullable String oldId) {
this.type = Type.DELETE_REF;
this.projectName = projectName;
this.refName = refName;
diff --git a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefLogger.java b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefLogger.java
index 858ba18..f61f253 100644
--- a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefLogger.java
+++ b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefLogger.java
@@ -42,6 +42,16 @@
<T> void logRefUpdate(String project, String refName, T currRef, T newRefValue);
/**
+ * Log the update of ref pointed to by refName, in project 'project' to ref 'newRefValue'
+ *
+ * @param project the project the update is for
+ * @param refName the name of the ref being updatex
+ * @param newRefValue the new value of the ref being updated
+ * @param <T> Type of the 'currRef' and the 'newRefValue'
+ */
+ <T> void logRefUpdate(String project, String refName, T newRefValue);
+
+ /**
* Log the deletion of 'project' from the global refdb
*
* @param project the project being deleted
diff --git a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/dfsrefdb/NoopSharedRefDatabase.java b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/dfsrefdb/NoopSharedRefDatabase.java
index 960b311..1ce480a 100644
--- a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/dfsrefdb/NoopSharedRefDatabase.java
+++ b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/dfsrefdb/NoopSharedRefDatabase.java
@@ -14,6 +14,7 @@
package com.gerritforge.gerrit.globalrefdb.validation.dfsrefdb;
+import com.gerritforge.gerrit.globalrefdb.ExtendedGlobalRefDatabase;
import com.gerritforge.gerrit.globalrefdb.GlobalRefDatabase;
import com.gerritforge.gerrit.globalrefdb.GlobalRefDbLockException;
import com.gerritforge.gerrit.globalrefdb.GlobalRefDbSystemError;
@@ -29,7 +30,7 @@
* <p>This is useful for setting up a test environment and allows multi-site library to be installed
* independently from any additional libModules or the existence of a specific Ref-DB installation.
*/
-public class NoopSharedRefDatabase implements GlobalRefDatabase {
+public class NoopSharedRefDatabase implements ExtendedGlobalRefDatabase {
/**
* Project/ref is always considered up-to-date
@@ -126,4 +127,10 @@
throws GlobalRefDbSystemError {
return Optional.empty();
}
+
+ @Override
+ public <T> void put(Project.NameKey project, String refName, T newValue)
+ throws GlobalRefDbSystemError {
+ // do nothing
+ }
}
diff --git a/src/test/java/com/gerritforge/gerrit/globalrefdb/ExtendedGlobalRefDatabaseTest.java b/src/test/java/com/gerritforge/gerrit/globalrefdb/ExtendedGlobalRefDatabaseTest.java
new file mode 100644
index 0000000..d58e0f3
--- /dev/null
+++ b/src/test/java/com/gerritforge/gerrit/globalrefdb/ExtendedGlobalRefDatabaseTest.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 GerritForge Ltd
+//
+// 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.gerritforge.gerrit.globalrefdb;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.util.Optional;
+import org.junit.Test;
+
+public class ExtendedGlobalRefDatabaseTest extends GlobalRefDatabaseTest {
+
+ @Test
+ public void shouldSetLongValueInTheGlobalRefDB() {
+ objectUnderTest.put(project, refName, 1L);
+
+ Optional<Long> o = objectUnderTest.get(project, refName, Long.class);
+
+ assertThat(o.isPresent()).isTrue();
+ assertThat(o.get()).isEqualTo(1L);
+ }
+}
diff --git a/src/test/java/com/gerritforge/gerrit/globalrefdb/FakeGlobalRefDatabase.java b/src/test/java/com/gerritforge/gerrit/globalrefdb/FakeGlobalRefDatabase.java
index 44c4fbb..5d4175f 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 com.google.gerrit.entities.Project.NameKey;
import java.util.Optional;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;
@@ -26,7 +27,7 @@
import org.junit.Ignore;
@Ignore
-public class FakeGlobalRefDatabase implements GlobalRefDatabase {
+public class FakeGlobalRefDatabase implements ExtendedGlobalRefDatabase {
private ConcurrentMap<Project.NameKey, ConcurrentMap<String, AtomicReference<ObjectId>>>
keyValueStore;
@@ -132,6 +133,12 @@
return projectRefLock;
}
+ @Override
+ public <T> void put(NameKey project, String refName, T newValue) throws GlobalRefDbSystemError {
+ String key = String.format("%s/%s", project.get(), refName);
+ genericKeyValueStore.put(key, new AtomicReference<>(newValue));
+ }
+
private static class RefLock implements AutoCloseable {
private Lock lock;
diff --git a/src/test/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabaseTest.java b/src/test/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabaseTest.java
index bb5ad8d..8ed1f4c 100644
--- a/src/test/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabaseTest.java
+++ b/src/test/java/com/gerritforge/gerrit/globalrefdb/GlobalRefDatabaseTest.java
@@ -31,7 +31,8 @@
public class GlobalRefDatabaseTest extends AbstractDaemonTest {
- private String refName = RefNames.REFS_HEADS + "branch";
+ protected ExtendedGlobalRefDatabase objectUnderTest;
+ protected String refName = RefNames.REFS_HEADS + "branch";
private ObjectId objectId1;
private ObjectId objectId2;
@@ -45,8 +46,6 @@
private Executor executor = Executors.newFixedThreadPool(1);
- private GlobalRefDatabase objectUnderTest;
-
@Before
public void setup() throws Exception {
this.objectUnderTest = new FakeGlobalRefDatabase();
diff --git a/src/test/java/com/gerritforge/gerrit/globalrefdb/validation/DisabledSharedRefLogger.java b/src/test/java/com/gerritforge/gerrit/globalrefdb/validation/DisabledSharedRefLogger.java
index f9a1e57..d6dd338 100644
--- a/src/test/java/com/gerritforge/gerrit/globalrefdb/validation/DisabledSharedRefLogger.java
+++ b/src/test/java/com/gerritforge/gerrit/globalrefdb/validation/DisabledSharedRefLogger.java
@@ -35,4 +35,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) {}
}