You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by rm...@apache.org on 2026/10/09 12:46:51 UTC

(hbase) 01/01: HBASE-30467 Exempt scanner close requests from quotas (#8749) (#8751)

This is an automated email from the ASF dual-hosted git repository.

rmdmattingly pushed a commit to branch HBASE-30467-branch-2.6
in repository https://gitbox.apache.org/repos/asf/hbase.git

commit eb9bb5e531bfc215627b627cc026b2a6dae07814
Author: Ray Mattingly <rm...@apache.org>
AuthorDate: Fri Oct 9 08:44:34 2026 -0400

    HBASE-30467 Exempt scanner close requests from quotas (#8749) (#8751)
    
    Signed-off-by: Ray Mattingly <rm...@apache.org>
    Co-authored-by: Ma Zhengxuan <49...@users.noreply.github.com>
---
 .../hbase/quotas/RegionServerRpcQuotaManager.java  |  6 +++
 .../hadoop/hbase/quotas/TestAtomicReadQuota.java   | 48 ++++++++++++++++++++++
 2 files changed, 54 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 34fc57cb081..b2716c50e14 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
@@ -181,6 +181,12 @@ public class RegionServerRpcQuotaManager implements RpcQuotaManager, Configurati
     final ClientProtos.ScanRequest scanRequest, long maxScannerResultSize,
     long maxBlockBytesScanned, long prevBlockBytesScannedDifference)
     throws IOException, RpcThrottlingException {
+    if (
+      scanRequest.hasScannerId() && scanRequest.getCloseScanner()
+        && scanRequest.getNumberOfRows() == 0 && !scanRequest.getRenew()
+    ) {
+      return NoopOperationQuota.get();
+    }
     Optional<User> user = RpcServer.getRequestUser();
     UserGroupInformation ugi;
     if (user.isPresent()) {
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/quotas/TestAtomicReadQuota.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/quotas/TestAtomicReadQuota.java
index 7b3b73895bb..b66d427517b 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/quotas/TestAtomicReadQuota.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/quotas/TestAtomicReadQuota.java
@@ -17,6 +17,10 @@
  */
 package org.apache.hadoop.hbase.quotas;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -33,7 +37,10 @@ import org.apache.hadoop.hbase.client.Put;
 import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException;
 import org.apache.hadoop.hbase.client.RowMutations;
+import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.hbase.ipc.HBaseRpcControllerImpl;
+import org.apache.hadoop.hbase.regionserver.RSRpcServices;
 import org.apache.hadoop.hbase.security.User;
 import org.apache.hadoop.hbase.testclassification.MediumTests;
 import org.apache.hadoop.hbase.testclassification.RegionServerTests;
@@ -46,6 +53,11 @@ import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException;
+
+import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanResponse;
+
 @Tag(RegionServerTests.TAG)
 @Tag(MediumTests.TAG)
 public class TestAtomicReadQuota {
@@ -76,6 +88,42 @@ public class TestAtomicReadQuota {
     TEST_UTIL.waitTableAvailable(TABLE_NAME);
   }
 
+  @Test
+  public void testCloseScannerWhenReadQuotaIsExhausted() throws Exception {
+    setupGenericQuota();
+    try {
+      RSRpcServices rpcServices = TEST_UTIL.getHBaseCluster().getRegionServer(0).getRSRpcServices();
+      int scannerCount = rpcServices.getScannersCount();
+      byte[] regionName = TEST_UTIL.getAdmin().getRegions(TABLE_NAME).get(0).getRegionName();
+      ScanResponse opened = rpcServices.scan(new HBaseRpcControllerImpl(),
+        RequestConverter.buildScanRequest(regionName, new Scan(), 0, false));
+      long scannerId = opened.getScannerId();
+      assertEquals(scannerCount + 1, rpcServices.getScannersCount());
+
+      ServiceException throttled =
+        assertThrows(ServiceException.class, () -> rpcServices.scan(new HBaseRpcControllerImpl(),
+          RequestConverter.buildScanRequest(scannerId, 1, true, false)));
+      assertInstanceOf(RpcThrottlingException.class, throttled.getCause());
+      assertEquals(scannerCount + 1, rpcServices.getScannersCount());
+
+      ServiceException renewThrottled =
+        assertThrows(ServiceException.class, () -> rpcServices.scan(new HBaseRpcControllerImpl(),
+          RequestConverter.buildScanRequest(scannerId, 0, true, 0, false, true, -1)));
+      assertInstanceOf(RpcThrottlingException.class, renewThrottled.getCause());
+
+      ServiceException openThrottled =
+        assertThrows(ServiceException.class, () -> rpcServices.scan(new HBaseRpcControllerImpl(),
+          RequestConverter.buildScanRequest(regionName, new Scan(), 0, true)));
+      assertInstanceOf(RpcThrottlingException.class, openThrottled.getCause());
+
+      rpcServices.scan(new HBaseRpcControllerImpl(),
+        RequestConverter.buildScanRequest(scannerId, 0, true, false));
+      assertEquals(scannerCount, rpcServices.getScannersCount());
+    } finally {
+      cleanupQuota();
+    }
+  }
+
   @Test
   public void testIncrementCountedAgainstReadCapacity() throws Exception {
     setupGenericQuota();