Merge branch 'stable-3.4' into stable-3.5
* stable-3.4:
Add global-refdb operations latency metrics
Change-Id: Ic1bca7a461d9dd22841e8bafb89b9c5c391e7a19
diff --git a/README.md b/README.md
index 582bd2c..8249d8c 100644
--- a/README.md
+++ b/README.md
@@ -14,4 +14,9 @@
## Bindings
In order to consume this library, some Guice bindings need to be registered
-appropriately. More information in the relevant [documentation](./bindings.md).
\ No newline at end of file
+appropriately. More information in the relevant [documentation](./bindings.md).
+
+## Metrics
+
+Global ref-database expose metrics to measure the global ref-database operation latency.
+List of the available metrics can be found [here](./metrics.md).
\ No newline at end of file
diff --git a/metrics.md b/metrics.md
new file mode 100644
index 0000000..4e53f30
--- /dev/null
+++ b/metrics.md
@@ -0,0 +1,20 @@
+Metrics
+=============
+
+* global_refdb/compare_and_put_latency
+ : the latency in milliseconds of the compareAndPut operation.
+
+* global_refdb/get_latency
+ : the latency in milliseconds of the get operation.
+
+* global_refdb/lock_ref_latency
+ : the latency in milliseconds of the lock ref operation.
+
+* global_refdb/exists_latency
+ : the latency in milliseconds of the exists operation.
+
+* global_refdb/is_up_to_date_latency
+ : the latency in milliseconds of the isUpToDate operation.
+
+* global_refdb/remove_latency
+ : the latency in milliseconds of the remove operation.
\ No newline at end of file
diff --git a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDBMetrics.java b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDBMetrics.java
new file mode 100644
index 0000000..ff483ab
--- /dev/null
+++ b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDBMetrics.java
@@ -0,0 +1,98 @@
+// Copyright (C) 2023 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.gerritforge.gerrit.globalrefdb.validation;
+
+import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.metrics.Timer0;
+import com.google.gerrit.metrics.Timer0.Context;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+@Singleton
+public class SharedRefDBMetrics {
+
+ private final Timer0 lockRefExecutionTime;
+ private final Timer0 getOperationExecutionTime;
+ private final Timer0 existsExecutionTime;
+ private Timer0 compareAndPutExecutionTime;
+ private Timer0 removeExecutionTime;
+ private Timer0 isUpToDateExecutionTime;
+
+ @Inject
+ public SharedRefDBMetrics(MetricMaker metricMaker) {
+ compareAndPutExecutionTime =
+ metricMaker.newTimer(
+ "global_refdb/compare_and_put_latency",
+ new Description("Time spent on compareAndPut.")
+ .setCumulative()
+ .setUnit(Description.Units.MILLISECONDS));
+ getOperationExecutionTime =
+ metricMaker.newTimer(
+ "global_refdb/get_latency",
+ new Description("Time spent on get operation.")
+ .setCumulative()
+ .setUnit(Description.Units.MILLISECONDS));
+
+ lockRefExecutionTime =
+ metricMaker.newTimer(
+ "global_refdb/lock_ref_latency",
+ new Description("Time spent on locking ref.")
+ .setCumulative()
+ .setUnit(Description.Units.MILLISECONDS));
+ existsExecutionTime =
+ metricMaker.newTimer(
+ "global_refdb/exists_latency",
+ new Description("Time spent on verifying if the global-refdb contains a value.")
+ .setCumulative()
+ .setUnit(Description.Units.MILLISECONDS));
+ removeExecutionTime =
+ metricMaker.newTimer(
+ "global_refdb/remove_latency",
+ new Description("Time spent on cleaning up the path from global-ref db.")
+ .setCumulative()
+ .setUnit(Description.Units.MILLISECONDS));
+ isUpToDateExecutionTime =
+ metricMaker.newTimer(
+ "global_refdb/is_up_to_date_latency",
+ new Description("Time spent on checking in global ref-db if ref is up-to-date.")
+ .setCumulative()
+ .setUnit(Description.Units.MILLISECONDS));
+ }
+
+ public Context startCompareAndPutExecutionTime() {
+ return compareAndPutExecutionTime.start();
+ }
+
+ public Context startGetExecutionTime() {
+ return getOperationExecutionTime.start();
+ }
+
+ public Context startLockRefExecutionTime() {
+ return lockRefExecutionTime.start();
+ }
+
+ public Context startExistsExecutionTime() {
+ return existsExecutionTime.start();
+ }
+
+ public Context startRemoveExecutionTime() {
+ return removeExecutionTime.start();
+ }
+
+ public Context startIsUpToDateExecutionTime() {
+ return isUpToDateExecutionTime.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 4b5aeec..4349193 100644
--- a/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDatabaseWrapper.java
+++ b/src/main/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDatabaseWrapper.java
@@ -21,6 +21,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.metrics.Timer0.Context;
import com.google.inject.Inject;
import java.util.Optional;
import org.eclipse.jgit.lib.ObjectId;
@@ -38,6 +39,7 @@
private DynamicItem<GlobalRefDatabase> sharedRefDbDynamicItem;
private final SharedRefLogger sharedRefLogger;
+ private final SharedRefDBMetrics metrics;
/**
* Constructs a {@code SharedRefDatabaseWrapper} wrapping an optional {@link GlobalRefDatabase},
@@ -46,69 +48,86 @@
* @param sharedRefLogger logger of shared ref-db operations.
*/
@Inject
- public SharedRefDatabaseWrapper(SharedRefLogger sharedRefLogger) {
+ public SharedRefDatabaseWrapper(SharedRefLogger sharedRefLogger, SharedRefDBMetrics metrics) {
this.sharedRefLogger = sharedRefLogger;
+ this.metrics = metrics;
}
@VisibleForTesting
public SharedRefDatabaseWrapper(
- DynamicItem<GlobalRefDatabase> sharedRefDbDynamicItem, SharedRefLogger sharedRefLogger) {
- this.sharedRefLogger = sharedRefLogger;
+ DynamicItem<GlobalRefDatabase> sharedRefDbDynamicItem,
+ SharedRefLogger sharedRefLogger,
+ SharedRefDBMetrics metrics) {
+ this(sharedRefLogger, metrics);
this.sharedRefDbDynamicItem = sharedRefDbDynamicItem;
}
@Override
public boolean isUpToDate(Project.NameKey project, Ref ref) throws GlobalRefDbLockException {
- return sharedRefDb().isUpToDate(project, ref);
+ try (Context context = metrics.startIsUpToDateExecutionTime()) {
+ return sharedRefDb().isUpToDate(project, ref);
+ }
}
/** {@inheritDoc}. The operation is logged upon success. */
@Override
public boolean compareAndPut(Project.NameKey project, Ref currRef, ObjectId newRefValue)
throws GlobalRefDbSystemError {
- boolean succeeded = sharedRefDb().compareAndPut(project, currRef, newRefValue);
- if (succeeded) {
- sharedRefLogger.logRefUpdate(project.get(), currRef, newRefValue);
+ try (Context context = metrics.startCompareAndPutExecutionTime()) {
+ boolean succeeded = sharedRefDb().compareAndPut(project, currRef, newRefValue);
+ if (succeeded) {
+ sharedRefLogger.logRefUpdate(project.get(), currRef, newRefValue);
+ }
+ return succeeded;
}
- return succeeded;
}
/** {@inheritDoc} the operation is logged upon success. */
@Override
public <T> boolean compareAndPut(Project.NameKey project, String refName, T currValue, T newValue)
throws GlobalRefDbSystemError {
- boolean succeeded = sharedRefDb().compareAndPut(project, refName, currValue, newValue);
- if (succeeded) {
- sharedRefLogger.logRefUpdate(project.get(), refName, currValue, newValue);
+ try (Context context = metrics.startCompareAndPutExecutionTime()) {
+ boolean succeeded = sharedRefDb().compareAndPut(project, refName, currValue, newValue);
+ if (succeeded) {
+ sharedRefLogger.logRefUpdate(project.get(), refName, currValue, newValue);
+ }
+ return succeeded;
}
- return succeeded;
}
/** {@inheritDoc}. The operation is logged. */
@Override
public AutoCloseable lockRef(Project.NameKey project, String refName)
throws GlobalRefDbLockException {
- AutoCloseable locker = sharedRefDb().lockRef(project, refName);
- sharedRefLogger.logLockAcquisition(project.get(), refName);
- return locker;
+ try (Context context = metrics.startLockRefExecutionTime()) {
+ AutoCloseable locker = sharedRefDb().lockRef(project, refName);
+ sharedRefLogger.logLockAcquisition(project.get(), refName);
+ return locker;
+ }
}
@Override
public boolean exists(Project.NameKey project, String refName) {
- return sharedRefDb().exists(project, refName);
+ try (Context context = metrics.startExistsExecutionTime()) {
+ return sharedRefDb().exists(project, refName);
+ }
}
/** {@inheritDoc}. The operation is logged. */
@Override
public void remove(Project.NameKey project) throws GlobalRefDbSystemError {
- sharedRefDb().remove(project);
- sharedRefLogger.logProjectDelete(project.get());
+ try (Context context = metrics.startRemoveExecutionTime()) {
+ sharedRefDb().remove(project);
+ sharedRefLogger.logProjectDelete(project.get());
+ }
}
@Override
public <T> Optional<T> get(Project.NameKey nameKey, String s, Class<T> clazz)
throws GlobalRefDbSystemError {
- return sharedRefDb().get(nameKey, s, clazz);
+ try (Context context = metrics.startGetExecutionTime()) {
+ return sharedRefDb().get(nameKey, s, clazz);
+ }
}
private GlobalRefDatabase sharedRefDb() {
diff --git a/src/test/java/com/gerritforge/gerrit/globalrefdb/validation/RefUpdateValidatorTest.java b/src/test/java/com/gerritforge/gerrit/globalrefdb/validation/RefUpdateValidatorTest.java
index f937286..3c63d13 100644
--- a/src/test/java/com/gerritforge/gerrit/globalrefdb/validation/RefUpdateValidatorTest.java
+++ b/src/test/java/com/gerritforge/gerrit/globalrefdb/validation/RefUpdateValidatorTest.java
@@ -50,6 +50,8 @@
@Mock SharedRefLogger sharedRefLogger;
+ @Mock SharedRefDBMetrics sharedRefDBMetrics;
+
@Mock RefDatabase localRefDb;
@Mock ValidationMetrics validationMetrics;
@@ -90,7 +92,8 @@
@Test
public void validationShouldSucceedWhenSharedRefDbIsNoop() throws Exception {
- SharedRefDatabaseWrapper noopSharedRefDbWrapper = new SharedRefDatabaseWrapper(sharedRefLogger);
+ SharedRefDatabaseWrapper noopSharedRefDbWrapper =
+ new SharedRefDatabaseWrapper(sharedRefLogger, sharedRefDBMetrics);
Result result =
newRefUpdateValidator(noopSharedRefDbWrapper)
diff --git a/src/test/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDatabaseWrapperTest.java b/src/test/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDatabaseWrapperTest.java
new file mode 100644
index 0000000..884090e
--- /dev/null
+++ b/src/test/java/com/gerritforge/gerrit/globalrefdb/validation/SharedRefDatabaseWrapperTest.java
@@ -0,0 +1,94 @@
+// Copyright (C) 2023 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.gerritforge.gerrit.globalrefdb.validation;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.metrics.Timer0.Context;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class SharedRefDatabaseWrapperTest {
+
+ @Mock private SharedRefDBMetrics metrics;
+ @Mock SharedRefLogger sharedRefLogger;
+ @Mock private Context context;
+ @Mock private Ref ref;
+
+ private SharedRefDatabaseWrapper objectUnderTest;
+ private String refName = "refs/heads/master";
+ private Project.NameKey projectName = Project.nameKey("test_project");
+
+ @Before
+ public void setup() {
+ when(metrics.startCompareAndPutExecutionTime()).thenReturn(context);
+ when(metrics.startLockRefExecutionTime()).thenReturn(context);
+ when(metrics.startGetExecutionTime()).thenReturn(context);
+ when(metrics.startExistsExecutionTime()).thenReturn(context);
+ when(metrics.startIsUpToDateExecutionTime()).thenReturn(context);
+ when(metrics.startRemoveExecutionTime()).thenReturn(context);
+ objectUnderTest = new SharedRefDatabaseWrapper(sharedRefLogger, metrics);
+ }
+
+ @Test
+ public void shouldUpdateCompareAndPutExecutionTimeMetricWhenCompareAndPut() {
+ objectUnderTest.compareAndPut(projectName, refName, ObjectId.zeroId(), ObjectId.zeroId());
+ verify(metrics).startCompareAndPutExecutionTime();
+ verify(context).close();
+ }
+
+ @Test
+ public void shouldUpdateLockRefExecutionTimeMetricWhenLockRefIsCalled() {
+ objectUnderTest.lockRef(projectName, refName);
+ verify(metrics).startLockRefExecutionTime();
+ verify(context).close();
+ }
+
+ @Test
+ public void shouldUpdateIsUpToDateExecutionTimeMetricWhenIsUpToDate() {
+ objectUnderTest.isUpToDate(projectName, ref);
+ verify(metrics).startIsUpToDateExecutionTime();
+ verify(context).close();
+ }
+
+ @Test
+ public void shouldUpdateExistsExecutionTimeMetricWhenExistsIsCalled() {
+ objectUnderTest.exists(projectName, refName);
+ verify(metrics).startExistsExecutionTime();
+ verify(context).close();
+ }
+
+ @Test
+ public void shouldUpdateGetExecutionTimeMetricWhenGetIsCalled() {
+ objectUnderTest.get(projectName, refName, String.class);
+ verify(metrics).startGetExecutionTime();
+ verify(context).close();
+ }
+
+ @Test
+ public void shouldUpdateRemoveExecutionTimeMetricWhenRemoveCalled() {
+ objectUnderTest.remove(projectName);
+ verify(metrics).startRemoveExecutionTime();
+ verify(context).close();
+ }
+}