This is an automated email from the ASF dual-hosted git repository.
danny0405 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hudi.git
The following commit(s) were added to refs/heads/master by this push:
new 99186095f12c [HUDI-9742] Add lock release metrics (#13757)
99186095f12c is described below
commit 99186095f12cbc9eddd1e9a00d0eda5f3c8aea3a
Author: Alex R <[email protected]>
AuthorDate: Sun Aug 24 19:47:39 2025 -0700
[HUDI-9742] Add lock release metrics (#13757)
---
.../hudi/client/transaction/lock/LockManager.java | 1 +
.../lock/metrics/HoodieLockMetrics.java | 10 ++-
.../client/transaction/TestHoodieLockMetrics.java | 85 ++++++++++++++++++++++
3 files changed, 95 insertions(+), 1 deletion(-)
diff --git
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/LockManager.java
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/LockManager.java
index 57ed6df45afb..b1be2ab30783 100644
---
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/LockManager.java
+++
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/LockManager.java
@@ -101,6 +101,7 @@ public class LockManager implements Serializable,
AutoCloseable {
} catch (HoodieException e) {
LOG.error(String.format("Exception encountered when updating lock
metrics: %s", e));
}
+ metrics.updateLockReleaseSuccessMetric();
close();
}
diff --git
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/metrics/HoodieLockMetrics.java
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/metrics/HoodieLockMetrics.java
index 924cef8bf917..29bacad81767 100644
---
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/metrics/HoodieLockMetrics.java
+++
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/metrics/HoodieLockMetrics.java
@@ -39,6 +39,7 @@ public class HoodieLockMetrics {
public static final String LOCK_ACQUIRE_ATTEMPTS_COUNTER_NAME =
"lock.acquire.attempts";
public static final String LOCK_ACQUIRE_SUCCESS_COUNTER_NAME =
"lock.acquire.success";
public static final String LOCK_ACQUIRE_FAILURES_COUNTER_NAME =
"lock.acquire.failure";
+ public static final String LOCK_RELEASE_SUCCESS_COUNTER_NAME =
"lock.release.success";
public static final String LOCK_ACQUIRE_DURATION_TIMER_NAME =
"lock.acquire.duration";
public static final String LOCK_REQUEST_LATENCY_TIMER_NAME =
"lock.request.latency";
private final HoodieWriteConfig writeConfig;
@@ -49,6 +50,7 @@ public class HoodieLockMetrics {
private transient Counter lockAttempts;
private transient Counter successfulLockAttempts;
private transient Counter failedLockAttempts;
+ private transient Counter lockReleaseSuccess;
private transient Timer lockDuration;
private transient Timer lockApiRequestDuration;
private static final Object REGISTRY_LOCK = new Object();
@@ -65,7 +67,7 @@ public class HoodieLockMetrics {
lockAttempts =
registry.counter(getMetricsName(LOCK_ACQUIRE_ATTEMPTS_COUNTER_NAME));
successfulLockAttempts =
registry.counter(getMetricsName(LOCK_ACQUIRE_SUCCESS_COUNTER_NAME));
failedLockAttempts =
registry.counter(getMetricsName(LOCK_ACQUIRE_FAILURES_COUNTER_NAME));
-
+ lockReleaseSuccess =
registry.counter(getMetricsName(LOCK_RELEASE_SUCCESS_COUNTER_NAME));
lockDuration = createTimerForMetrics(registry,
LOCK_ACQUIRE_DURATION_TIMER_NAME);
lockApiRequestDuration = createTimerForMetrics(registry,
LOCK_REQUEST_LATENCY_TIMER_NAME);
}
@@ -125,4 +127,10 @@ public class HoodieLockMetrics {
updateMetric(lockDurationTimer, lockDuration, "held");
}
}
+
+ public void updateLockReleaseSuccessMetric() {
+ if (isMetricsEnabled) {
+ lockReleaseSuccess.inc();
+ }
+ }
}
diff --git
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/TestHoodieLockMetrics.java
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/TestHoodieLockMetrics.java
index e4260f4d4e3e..0cf1de5653a2 100644
---
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/TestHoodieLockMetrics.java
+++
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/TestHoodieLockMetrics.java
@@ -19,15 +19,20 @@
package org.apache.hudi.client.transaction;
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.MetricRegistry;
import org.apache.hudi.client.transaction.lock.metrics.HoodieLockMetrics;
import org.apache.hudi.config.HoodieWriteConfig;
import org.apache.hudi.config.metrics.HoodieMetricsConfig;
import org.apache.hudi.metrics.MetricsReporterType;
import org.apache.hudi.storage.HoodieStorage;
+import org.apache.hudi.metrics.Metrics;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.mock;
public class TestHoodieLockMetrics {
@@ -67,4 +72,84 @@ public class TestHoodieLockMetrics {
assertDoesNotThrow(lockMetrics::updateLockAcquiredMetric);
}
+ @Test
+ public void testLockReleaseSuccessMetric() {
+ // Test that lock release success metric is properly tracked
+ HoodieStorage storage = mock(HoodieStorage.class);
+ HoodieMetricsConfig metricsConfig =
HoodieMetricsConfig.newBuilder().withPath("/test")
+
.withReporterType(MetricsReporterType.INMEMORY.name()).withLockingMetrics(true).build();
+ HoodieWriteConfig writeConfig = HoodieWriteConfig.newBuilder()
+ .forTable("testTable").withPath("/test/path")
+ .withMetricsConfig(metricsConfig)
+ .build();
+ HoodieLockMetrics lockMetrics = new HoodieLockMetrics(writeConfig,
storage);
+
+ // Get the metrics registry to verify counter values
+ Metrics metrics = Metrics.getInstance(metricsConfig, storage);
+ MetricRegistry registry = metrics.getRegistry();
+
+ // Verify the lock release success counter exists
+ String metricName = writeConfig.getMetricReporterMetricsNamePrefix() + "."
+ HoodieLockMetrics.LOCK_RELEASE_SUCCESS_COUNTER_NAME;
+ Counter lockReleaseSuccessCounter = registry.getCounters().get(metricName);
+ assertNotNull(lockReleaseSuccessCounter, "Lock release success counter
should exist");
+
+ long initialCount = lockReleaseSuccessCounter.getCount();
+
+ // Simulate successful lock release
+ lockMetrics.updateLockReleaseSuccessMetric();
+ assertEquals(initialCount + 1, lockReleaseSuccessCounter.getCount(), "Lock
release success counter should increment by 1");
+
+ // Simulate multiple successful lock releases
+ lockMetrics.updateLockReleaseSuccessMetric();
+ lockMetrics.updateLockReleaseSuccessMetric();
+ assertEquals(initialCount + 3, lockReleaseSuccessCounter.getCount(), "Lock
release success counter should increment by 3 total");
+ }
+
+ @Test
+ public void testLockLifecycleWithReleaseSuccess() {
+ // Test complete lock lifecycle including acquisition and successful
release
+ HoodieStorage storage = mock(HoodieStorage.class);
+ HoodieMetricsConfig metricsConfig =
HoodieMetricsConfig.newBuilder().withPath("/test")
+
.withReporterType(MetricsReporterType.INMEMORY.name()).withLockingMetrics(true).build();
+ HoodieWriteConfig writeConfig = HoodieWriteConfig.newBuilder()
+ .forTable("testTable").withPath("/test/path")
+ .withMetricsConfig(metricsConfig)
+ .build();
+ HoodieLockMetrics lockMetrics = new HoodieLockMetrics(writeConfig,
storage);
+
+ // Get the metrics registry to verify counter values
+ Metrics metrics = Metrics.getInstance(metricsConfig, storage);
+ MetricRegistry registry = metrics.getRegistry();
+
+ String acquireMetricName =
writeConfig.getMetricReporterMetricsNamePrefix() + "." +
HoodieLockMetrics.LOCK_ACQUIRE_SUCCESS_COUNTER_NAME;
+ String releaseMetricName =
writeConfig.getMetricReporterMetricsNamePrefix() + "." +
HoodieLockMetrics.LOCK_RELEASE_SUCCESS_COUNTER_NAME;
+ Counter lockAcquiredCounter =
registry.getCounters().get(acquireMetricName);
+ Counter lockReleaseSuccessCounter =
registry.getCounters().get(releaseMetricName);
+
+ long initialAcquireCount = lockAcquiredCounter.getCount();
+ long initialReleaseCount = lockReleaseSuccessCounter.getCount();
+
+ // Simulate complete lock lifecycle
+ lockMetrics.startLockApiTimerContext();
+ lockMetrics.updateLockAcquiredMetric();
+ assertEquals(initialAcquireCount + 1, lockAcquiredCounter.getCount(),
"Lock acquired counter should increment by 1");
+ assertEquals(initialReleaseCount, lockReleaseSuccessCounter.getCount(),
"Lock release success counter should not change yet");
+
+ // Now release the lock successfully
+ lockMetrics.updateLockReleaseSuccessMetric();
+ lockMetrics.updateLockHeldTimerMetrics();
+ assertEquals(initialAcquireCount + 1, lockAcquiredCounter.getCount(),
"Lock acquired counter should still be incremented by 1");
+ assertEquals(initialReleaseCount + 1,
lockReleaseSuccessCounter.getCount(), "Lock release success counter should
increment by 1");
+
+ // Verify metrics balance for multiple cycles
+ for (int i = 0; i < 5; i++) {
+ lockMetrics.startLockApiTimerContext();
+ lockMetrics.updateLockAcquiredMetric();
+ lockMetrics.updateLockReleaseSuccessMetric();
+ lockMetrics.updateLockHeldTimerMetrics();
+ }
+
+ assertEquals(initialAcquireCount + 6, lockAcquiredCounter.getCount(),
"Lock acquired counter should increment by 6 total");
+ assertEquals(initialReleaseCount + 6,
lockReleaseSuccessCounter.getCount(), "Lock release success counter should
increment by 6 total");
+ }
}