This is an automated email from the ASF dual-hosted git repository.
cconnell pushed a commit to branch branch-2
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/branch-2 by this push:
new b502a111e71 HBASE-29469 Add metrics with more detail for
RpcThrottlingExceptions (#7214)
b502a111e71 is described below
commit b502a111e7116b0110c01758b8fec90dbec38c01
Author: Siddharth Khillon <[email protected]>
AuthorDate: Tue Aug 12 06:30:33 2025 -0700
HBASE-29469 Add metrics with more detail for RpcThrottlingExceptions (#7214)
Co-authored-by: skhillon <[email protected]>
Signed-off by: cconnell <[email protected]>
Reviewed by: kgeisz <[email protected]>
---
.../hbase/quotas/RegionServerRpcQuotaManager.java | 8 +
.../hbase/regionserver/MetricsRegionServer.java | 16 ++
.../metrics/MetricsThrottleExceptions.java | 80 ++++++
.../regionserver/TestMetricsRegionServer.java | 42 +++
.../metrics/TestMetricsThrottleExceptions.java | 294 +++++++++++++++++++++
5 files changed, 440 insertions(+)
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/RegionServerRpcQuotaManager.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/RegionServerRpcQuotaManager.java
index 03fbfde47a1..958793dcdf0 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/RegionServerRpcQuotaManager.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/quotas/RegionServerRpcQuotaManager.java
@@ -196,6 +196,10 @@ public class RegionServerRpcQuotaManager implements
RpcQuotaManager, Configurati
} catch (RpcThrottlingException e) {
LOG.debug("Throttling exception for user=" + ugi.getUserName() + "
table=" + table + " scan="
+ scanRequest.getScannerId() + ": " + e.getMessage());
+
+ rsServices.getMetrics().recordThrottleException(e.getType(),
ugi.getShortUserName(),
+ table.getNameAsString());
+
throw e;
}
return quota;
@@ -269,6 +273,10 @@ public class RegionServerRpcQuotaManager implements
RpcQuotaManager, Configurati
} catch (RpcThrottlingException e) {
LOG.debug("Throttling exception for user=" + ugi.getUserName() + "
table=" + table
+ " numWrites=" + numWrites + " numReads=" + numReads + ": " +
e.getMessage());
+
+ rsServices.getMetrics().recordThrottleException(e.getType(),
ugi.getShortUserName(),
+ table.getNameAsString());
+
throw e;
}
return quota;
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServer.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServer.java
index a0bf25dc2ea..580f7787499 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServer.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServer.java
@@ -23,6 +23,8 @@ import org.apache.hadoop.hbase.metrics.Meter;
import org.apache.hadoop.hbase.metrics.MetricRegistries;
import org.apache.hadoop.hbase.metrics.MetricRegistry;
import org.apache.hadoop.hbase.metrics.Timer;
+import org.apache.hadoop.hbase.quotas.RpcThrottlingException;
+import org.apache.hadoop.hbase.regionserver.metrics.MetricsThrottleExceptions;
import org.apache.yetus.audience.InterfaceAudience;
import org.apache.yetus.audience.InterfaceStability;
@@ -47,6 +49,7 @@ public class MetricsRegionServer {
private MetricsRegionServerQuotaSource quotaSource;
private MetricRegistry metricRegistry;
+ private MetricsThrottleExceptions throttleMetrics;
private Timer bulkLoadTimer;
// Incremented once for each call to Scan#nextRaw
private Meter serverReadQueryMeter;
@@ -78,6 +81,8 @@ public class MetricsRegionServer {
serverReadQueryMeter = metricRegistry.meter("ServerReadQueryPerSecond");
serverWriteQueryMeter =
metricRegistry.meter("ServerWriteQueryPerSecond");
}
+
+ throttleMetrics = new MetricsThrottleExceptions(metricRegistry);
}
MetricsRegionServer(MetricsRegionServerWrapper regionServerWrapper,
@@ -296,4 +301,15 @@ public class MetricsRegionServer {
serverSource.incrScannerLeaseExpired();
}
+ /**
+ * Record a throttle exception with contextual information.
+ * @param throttleType the type of throttle exception from
RpcThrottlingException.Type enum
+ * @param user the user who triggered the throttle
+ * @param table the table that was being accessed
+ */
+ public void recordThrottleException(RpcThrottlingException.Type
throttleType, String user,
+ String table) {
+ throttleMetrics.recordThrottleException(throttleType, user, table);
+ }
+
}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/MetricsThrottleExceptions.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/MetricsThrottleExceptions.java
new file mode 100644
index 00000000000..ddf451ab2e2
--- /dev/null
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/metrics/MetricsThrottleExceptions.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.hadoop.hbase.regionserver.metrics;
+
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
+import org.apache.hadoop.hbase.quotas.RpcThrottlingException;
+import org.apache.yetus.audience.InterfaceAudience;
+
[email protected]
+public class MetricsThrottleExceptions {
+
+ /**
+ * The name of the metrics
+ */
+ private static final String METRICS_NAME = "ThrottleExceptions";
+
+ /**
+ * The name of the metrics context that metrics will be under.
+ */
+ private static final String METRICS_CONTEXT = "regionserver";
+
+ /**
+ * Description
+ */
+ private static final String METRICS_DESCRIPTION = "Metrics about RPC
throttling exceptions";
+
+ /**
+ * The name of the metrics context that metrics will be under in jmx
+ */
+ private static final String METRICS_JMX_CONTEXT = "RegionServer,sub=" +
METRICS_NAME;
+
+ private final MetricRegistry registry;
+
+ public MetricsThrottleExceptions(MetricRegistry sharedRegistry) {
+ registry = sharedRegistry;
+ }
+
+ /**
+ * Record a throttle exception with contextual information.
+ * @param throttleType the type of throttle exception
+ * @param user the user who triggered the throttle
+ * @param table the table that was being accessed
+ */
+ public void recordThrottleException(RpcThrottlingException.Type
throttleType, String user,
+ String table) {
+ String metricName = qualifyThrottleMetric(throttleType, user, table);
+ registry.counter(metricName).increment();
+ }
+
+ private static String qualifyThrottleMetric(RpcThrottlingException.Type
throttleType, String user,
+ String table) {
+ return String.format("RpcThrottlingException_Type_%s_User_%s_Table_%s",
throttleType.name(),
+ sanitizeMetricName(user), sanitizeMetricName(table));
+ }
+
+ private static String sanitizeMetricName(String name) {
+ if (name == null) {
+ return "unknown";
+ }
+ // Only replace characters that are problematic for JMX ObjectNames
+ // Keep meaningful characters like hyphens, periods, etc.
+ return name.replaceAll("[,=:*?\"\\n]", "_");
+ }
+
+}
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestMetricsRegionServer.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestMetricsRegionServer.java
index 2186aa7cc4d..ce78aa40ab7 100644
---
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestMetricsRegionServer.java
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestMetricsRegionServer.java
@@ -26,11 +26,14 @@ import static org.mockito.Mockito.when;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.CompatibilityFactory;
import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.metrics.MetricRegistries;
+import org.apache.hadoop.hbase.quotas.RpcThrottlingException;
import org.apache.hadoop.hbase.regionserver.metrics.MetricsTableRequests;
import org.apache.hadoop.hbase.test.MetricsAssertHelper;
import org.apache.hadoop.hbase.testclassification.RegionServerTests;
import org.apache.hadoop.hbase.testclassification.SmallTests;
import org.apache.hadoop.hbase.util.JvmPauseMonitor;
+import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
@@ -68,6 +71,12 @@ public class TestMetricsRegionServer {
serverSource = rsm.getMetricsSource();
}
+ @After
+ public void tearDown() {
+ // Clean up global registries after each test to avoid interference
+ MetricRegistries.global().clear();
+ }
+
@Test
public void testWrapperSource() {
HELPER.assertTag("serverName", "test", serverSource);
@@ -314,4 +323,37 @@ public class TestMetricsRegionServer {
HELPER.assertGauge("activeScanners", 0, serverSource);
}
+ @Test
+ public void testThrottleExceptionMetricsIntegration() {
+ // Record different types of throttle exceptions
+
rsm.recordThrottleException(RpcThrottlingException.Type.NumRequestsExceeded,
"alice", "users");
+ rsm.recordThrottleException(RpcThrottlingException.Type.WriteSizeExceeded,
"bob", "logs");
+ rsm.recordThrottleException(RpcThrottlingException.Type.ReadSizeExceeded,
"charlie",
+ "metadata");
+
+ // Record the same exception multiple times to test increment
+
rsm.recordThrottleException(RpcThrottlingException.Type.NumRequestsExceeded,
"alice", "users");
+
rsm.recordThrottleException(RpcThrottlingException.Type.NumRequestsExceeded,
"alice", "users");
+
+ // Verify the specific counters were created and have correct values using
HELPER
+
HELPER.assertCounter("RpcThrottlingException_Type_NumRequestsExceeded_User_alice_Table_users",
+ 3L, serverSource);
+
HELPER.assertCounter("RpcThrottlingException_Type_WriteSizeExceeded_User_bob_Table_logs",
1L,
+ serverSource);
+
HELPER.assertCounter("RpcThrottlingException_Type_ReadSizeExceeded_User_charlie_Table_metadata",
+ 1L, serverSource);
+
+ // Test metric name sanitization through the integration
+
rsm.recordThrottleException(RpcThrottlingException.Type.RequestSizeExceeded,
+ "user.with@special", "table:with,problematic=chars");
+ HELPER.assertCounter(
+
"RpcThrottlingException_Type_RequestSizeExceeded_User_user.with@special_Table_table_with_problematic_chars",
+ 1L, serverSource);
+
+ // Test null handling through the integration
+
rsm.recordThrottleException(RpcThrottlingException.Type.ReadCapacityUnitExceeded,
null, null);
+ HELPER.assertCounter(
+
"RpcThrottlingException_Type_ReadCapacityUnitExceeded_User_unknown_Table_unknown",
1L,
+ serverSource);
+ }
}
diff --git
a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/metrics/TestMetricsThrottleExceptions.java
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/metrics/TestMetricsThrottleExceptions.java
new file mode 100644
index 00000000000..0fa02c42a32
--- /dev/null
+++
b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/metrics/TestMetricsThrottleExceptions.java
@@ -0,0 +1,294 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.hadoop.hbase.regionserver.metrics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.metrics.Counter;
+import org.apache.hadoop.hbase.metrics.Metric;
+import org.apache.hadoop.hbase.metrics.MetricRegistries;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
+import org.apache.hadoop.hbase.metrics.MetricRegistryInfo;
+import org.apache.hadoop.hbase.quotas.RpcThrottlingException;
+import org.apache.hadoop.hbase.testclassification.RegionServerTests;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.junit.After;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category({ RegionServerTests.class, SmallTests.class })
+public class TestMetricsThrottleExceptions {
+
+ @ClassRule
+ public static final HBaseClassTestRule CLASS_RULE =
+ HBaseClassTestRule.forClass(TestMetricsThrottleExceptions.class);
+
+ private MetricRegistry testRegistry;
+ private MetricsThrottleExceptions throttleMetrics;
+
+ @After
+ public void cleanup() {
+ // Clean up global registries after each test to avoid interference
+ MetricRegistries.global().clear();
+ }
+
+ @Test
+ public void testBasicThrottleMetricsRecording() {
+ setupTestMetrics();
+
+ // Record a throttle exception
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.NumRequestsExceeded,
+ "alice", "users");
+
+ // Verify the counter exists and has correct value
+ Optional<Metric> metric =
+
testRegistry.get("RpcThrottlingException_Type_NumRequestsExceeded_User_alice_Table_users");
+ assertTrue("Counter metric should be present", metric.isPresent());
+ assertTrue("Metric should be a counter", metric.get() instanceof Counter);
+
+ Counter counter = (Counter) metric.get();
+ assertEquals("Counter should have count of 1", 1, counter.getCount());
+ }
+
+ @Test
+ public void testMultipleThrottleTypes() {
+ setupTestMetrics();
+
+ // Record different types of throttle exceptions
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.NumRequestsExceeded,
+ "alice", "users");
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.WriteSizeExceeded,
"bob",
+ "logs");
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.ReadSizeExceeded,
"charlie",
+ "metadata");
+
+ // Verify all three counters were created
+ verifyCounter(testRegistry,
+
"RpcThrottlingException_Type_NumRequestsExceeded_User_alice_Table_users", 1);
+ verifyCounter(testRegistry,
"RpcThrottlingException_Type_WriteSizeExceeded_User_bob_Table_logs",
+ 1);
+ verifyCounter(testRegistry,
+
"RpcThrottlingException_Type_ReadSizeExceeded_User_charlie_Table_metadata", 1);
+ }
+
+ @Test
+ public void testCounterIncrement() {
+ setupTestMetrics();
+
+ // Record the same throttle exception multiple times
+ String metricName =
"RpcThrottlingException_Type_NumRequestsExceeded_User_alice_Table_users";
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.NumRequestsExceeded,
+ "alice", "users");
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.NumRequestsExceeded,
+ "alice", "users");
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.NumRequestsExceeded,
+ "alice", "users");
+
+ // Verify the counter incremented correctly
+ verifyCounter(testRegistry, metricName, 3);
+ }
+
+ @Test
+ public void testMetricNameSanitization() {
+ setupTestMetrics();
+
+ // Test that meaningful characters are preserved (hyphens, periods, etc.)
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.WriteSizeExceeded,
+ "user.name@company", "my-table-prod");
+
+ // Verify meaningful characters are preserved, only JMX-problematic chars
are replaced
+ String expectedMetricName =
+
"RpcThrottlingException_Type_WriteSizeExceeded_User_user.name@company_Table_my-table-prod";
+ verifyCounter(testRegistry, expectedMetricName, 1);
+
+ // Test that JMX-problematic characters are sanitized
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.ReadSizeExceeded,
+ "user,with=bad:chars*", "table?with\"quotes");
+ String problematicMetricName =
+
"RpcThrottlingException_Type_ReadSizeExceeded_User_user_with_bad_chars__Table_table_with_quotes";
+ verifyCounter(testRegistry, problematicMetricName, 1);
+ }
+
+ @Test
+ public void testNullHandling() {
+ setupTestMetrics();
+
+ // Test null user and table names
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.NumRequestsExceeded,
null,
+ null);
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.WriteSizeExceeded,
"alice",
+ null);
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.ReadSizeExceeded,
null,
+ "users");
+
+ // Verify null values are replaced with "unknown"
+ verifyCounter(testRegistry,
+
"RpcThrottlingException_Type_NumRequestsExceeded_User_unknown_Table_unknown",
1);
+ verifyCounter(testRegistry,
+
"RpcThrottlingException_Type_WriteSizeExceeded_User_alice_Table_unknown", 1);
+ verifyCounter(testRegistry,
+ "RpcThrottlingException_Type_ReadSizeExceeded_User_unknown_Table_users",
1);
+ }
+
+ @Test
+ public void testConcurrentAccess() throws InterruptedException {
+ setupTestMetrics();
+
+ int numThreads = 10;
+ int incrementsPerThread = 100;
+
+ ExecutorService executor = Executors.newFixedThreadPool(numThreads);
+ CountDownLatch startLatch = new CountDownLatch(1);
+ CountDownLatch doneLatch = new CountDownLatch(numThreads);
+ AtomicInteger exceptions = new AtomicInteger(0);
+
+ // Create multiple threads that increment the same counter concurrently
+ for (int i = 0; i < numThreads; i++) {
+ executor.submit(() -> {
+ try {
+ startLatch.await();
+ for (int j = 0; j < incrementsPerThread; j++) {
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.NumRequestsExceeded,
+ "alice", "users");
+ }
+ } catch (Exception e) {
+ exceptions.incrementAndGet();
+ } finally {
+ doneLatch.countDown();
+ }
+ });
+ }
+
+ // Start all threads at once
+ startLatch.countDown();
+
+ // Wait for all threads to complete
+ boolean completed = doneLatch.await(30, TimeUnit.SECONDS);
+ assertTrue("All threads should complete within timeout", completed);
+ assertEquals("No exceptions should occur during concurrent access", 0,
exceptions.get());
+
+ // Verify the final counter value
+ verifyCounter(testRegistry,
+ "RpcThrottlingException_Type_NumRequestsExceeded_User_alice_Table_users",
+ numThreads * incrementsPerThread);
+
+ executor.shutdown();
+ }
+
+ @Test
+ public void testCommonTableNamePatterns() {
+ setupTestMetrics();
+
+ // Test common HBase table name patterns that should be preserved
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.NumRequestsExceeded,
+ "service-user", "my-app-logs");
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.WriteSizeExceeded,
+ "batch.process", "namespace:table-name");
+
throttleMetrics.recordThrottleException(RpcThrottlingException.Type.ReadSizeExceeded,
+ "user_123", "test_table_v2");
+
+ // Verify common patterns are preserved correctly (note: colon gets
replaced with underscore)
+ verifyCounter(testRegistry,
+
"RpcThrottlingException_Type_NumRequestsExceeded_User_service-user_Table_my-app-logs",
1);
+ verifyCounter(testRegistry,
+
"RpcThrottlingException_Type_WriteSizeExceeded_User_batch.process_Table_namespace_table-name",
+ 1);
+ verifyCounter(testRegistry,
+
"RpcThrottlingException_Type_ReadSizeExceeded_User_user_123_Table_test_table_v2",
1);
+ }
+
+ @Test
+ public void testAllThrottleExceptionTypes() {
+ setupTestMetrics();
+
+ // Test all 13 throttle exception types from RpcThrottlingException.Type
enum
+ RpcThrottlingException.Type[] throttleTypes =
RpcThrottlingException.Type.values();
+
+ // Record one exception for each type
+ for (RpcThrottlingException.Type throttleType : throttleTypes) {
+ throttleMetrics.recordThrottleException(throttleType, "testuser",
"testtable");
+ }
+
+ // Verify all counters were created with correct values
+ for (RpcThrottlingException.Type throttleType : throttleTypes) {
+ String expectedMetricName =
+ "RpcThrottlingException_Type_" + throttleType.name() +
"_User_testuser_Table_testtable";
+ verifyCounter(testRegistry, expectedMetricName, 1);
+ }
+ }
+
+ @Test
+ public void testMultipleInstances() {
+ setupTestMetrics();
+
+ // Test that multiple instances of MetricsThrottleExceptions work with the
same registry
+ MetricsThrottleExceptions metrics1 = new
MetricsThrottleExceptions(testRegistry);
+ MetricsThrottleExceptions metrics2 = new
MetricsThrottleExceptions(testRegistry);
+
+ // Record different exceptions on each instance
+
metrics1.recordThrottleException(RpcThrottlingException.Type.NumRequestsExceeded,
"alice",
+ "table1");
+
metrics2.recordThrottleException(RpcThrottlingException.Type.WriteSizeExceeded,
"bob",
+ "table2");
+
+ // Verify both counters exist in the shared registry
+ verifyCounter(testRegistry,
+
"RpcThrottlingException_Type_NumRequestsExceeded_User_alice_Table_table1", 1);
+ verifyCounter(testRegistry,
+ "RpcThrottlingException_Type_WriteSizeExceeded_User_bob_Table_table2",
1);
+ }
+
+ /**
+ * Helper method to set up test metrics registry and instance
+ */
+ private void setupTestMetrics() {
+ MetricRegistryInfo registryInfo = getRegistryInfo();
+ testRegistry = MetricRegistries.global().create(registryInfo);
+ throttleMetrics = new MetricsThrottleExceptions(testRegistry);
+ }
+
+ /**
+ * Helper method to verify a counter exists and has the expected value
+ */
+ private void verifyCounter(MetricRegistry registry, String metricName, long
expectedCount) {
+ Optional<Metric> metric = registry.get(metricName);
+ assertTrue("Counter metric '" + metricName + "' should be present",
metric.isPresent());
+ assertTrue("Metric should be a counter", metric.get() instanceof Counter);
+
+ Counter counter = (Counter) metric.get();
+ assertEquals("Counter '" + metricName + "' should have expected count",
expectedCount,
+ counter.getCount());
+ }
+
+ /**
+ * Helper method to create the expected MetricRegistryInfo for
ThrottleExceptions
+ */
+ private MetricRegistryInfo getRegistryInfo() {
+ return new MetricRegistryInfo("ThrottleExceptions", "Metrics about RPC
throttling exceptions",
+ "RegionServer,sub=ThrottleExceptions", "regionserver", false);
+ }
+}