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

zhangduo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hbase-operator-tools.git


The following commit(s) were added to refs/heads/master by this push:
     new fd37d47  HBASE-29674 [hbase-operator-tools] Remove ClusterConnection 
in HBaseFsckRepair.closeRegionSilentlyAndWait (#155)
fd37d47 is described below

commit fd37d4700acf0c9beaad6f67e79e580ebaf7bb81
Author: Duo Zhang <[email protected]>
AuthorDate: Mon Oct 27 22:28:51 2025 +0800

    HBASE-29674 [hbase-operator-tools] Remove ClusterConnection in 
HBaseFsckRepair.closeRegionSilentlyAndWait (#155)
    
    Co-authored-by: Copilot <[email protected]>
    Co-authored-by: Peter Somogyi <[email protected]>
    
    Signed-off-by: Peter Somogyi <[email protected]>
---
 .../org/apache/hbase/HBCKRegionServerAdmin.java    | 146 +++++++++++++++++++++
 .../org/apache/hbase/hbck1/HBaseFsckRepair.java    |  34 ++++-
 2 files changed, 176 insertions(+), 4 deletions(-)

diff --git 
a/hbase-hbck2/src/main/java/org/apache/hbase/HBCKRegionServerAdmin.java 
b/hbase-hbck2/src/main/java/org/apache/hbase/HBCKRegionServerAdmin.java
new file mode 100644
index 0000000..82755fc
--- /dev/null
+++ b/hbase-hbck2/src/main/java/org/apache/hbase/HBCKRegionServerAdmin.java
@@ -0,0 +1,146 @@
+/*
+ * 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.hbase;
+
+import static org.apache.hadoop.hbase.HConstants.DEFAULT_HBASE_RPC_TIMEOUT;
+import static org.apache.hadoop.hbase.HConstants.HBASE_RPC_TIMEOUT_KEY;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.client.Connection;
+import org.apache.hadoop.hbase.client.RegionInfo;
+import org.apache.hadoop.hbase.ipc.HBaseRpcController;
+import org.apache.hadoop.hbase.ipc.RpcClient;
+import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
+import org.apache.hadoop.hbase.security.User;
+import org.apache.hadoop.hbase.util.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException;
+
+import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService.BlockingInterface;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetRegionInfoRequest;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetRegionInfoResponse;
+import 
org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType;
+
+/**
+ * A helper class for doing some admin operations directly on region server
+ */
+public class HBCKRegionServerAdmin {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(HBCKRegionServerAdmin.class);
+
+  private final RpcControllerFactory rpcControllerFactory;
+
+  private final AdminService.BlockingInterface stub;
+
+  private final ServerName server;
+
+  // for HBase 2.x
+  private Pair<RpcControllerFactory, AdminService.BlockingInterface> 
init2(Connection conn,
+    ServerName server) throws IOException {
+    try {
+      Class<?> clusterConnClazz = 
Class.forName("org.apache.hadoop.hbase.client.ClusterConnection");
+      Method getRpcControllerFactoryMethod =
+        clusterConnClazz.getDeclaredMethod("getRpcControllerFactory");
+      getRpcControllerFactoryMethod.setAccessible(true);
+      RpcControllerFactory rpcControllerFactory =
+        (RpcControllerFactory) getRpcControllerFactoryMethod.invoke(conn);
+      Method getAdminMethod = clusterConnClazz.getDeclaredMethod("getAdmin", 
ServerName.class);
+      getAdminMethod.setAccessible(true);
+      AdminService.BlockingInterface stub = (BlockingInterface) 
getAdminMethod.invoke(conn, server);
+      return Pair.newPair(rpcControllerFactory, stub);
+    } catch (ClassNotFoundException e) {
+      LOG.debug("No ClusterConnection, should be HBase 3+", e);
+      return null;
+    } catch (NoSuchMethodException | IllegalAccessException | 
InvocationTargetException e) {
+      throw new IOException(e);
+    }
+  }
+
+  // for HBase 3+
+  private Pair<RpcControllerFactory, AdminService.BlockingInterface> 
init3(Connection conn,
+    ServerName server) throws IOException {
+    try {
+      Method toAsyncConnectionMethod = 
conn.getClass().getMethod("toAsyncConnection");
+      toAsyncConnectionMethod.setAccessible(true);
+      Object asyncConn = toAsyncConnectionMethod.invoke(conn);
+      Field rpcControllerFactoryField =
+        asyncConn.getClass().getDeclaredField("rpcControllerFactory");
+      rpcControllerFactoryField.setAccessible(true);
+      RpcControllerFactory rpcControllerFactory =
+        (RpcControllerFactory) rpcControllerFactoryField.get(asyncConn);
+      Field rpcClientField = 
asyncConn.getClass().getDeclaredField("rpcClient");
+      rpcClientField.setAccessible(true);
+      RpcClient rpcClient = (RpcClient) rpcClientField.get(asyncConn);
+      Field userField = asyncConn.getClass().getDeclaredField("user");
+      userField.setAccessible(true);
+      User user = (User) userField.get(asyncConn);
+      int rpcTimeoutMs =
+        conn.getConfiguration().getInt(HBASE_RPC_TIMEOUT_KEY, 
DEFAULT_HBASE_RPC_TIMEOUT);
+      AdminService.BlockingInterface stub = AdminService
+        .newBlockingStub(rpcClient.createBlockingRpcChannel(server, user, 
rpcTimeoutMs));
+      return Pair.newPair(rpcControllerFactory, stub);
+    } catch (NoSuchMethodException | IllegalAccessException | 
InvocationTargetException
+      | NoSuchFieldException e) {
+      throw new IOException(e);
+    }
+  }
+
+  public HBCKRegionServerAdmin(Connection conn, ServerName server) throws 
IOException {
+    this.server = server;
+    Pair<RpcControllerFactory, AdminService.BlockingInterface> pair = 
init2(conn, server);
+    if (pair == null) {
+      pair = init3(conn, server);
+    }
+    rpcControllerFactory = pair.getFirst();
+    stub = pair.getSecond();
+  }
+
+  public void closeRegion(byte[] regionName) throws IOException {
+    HBaseRpcController hrc = rpcControllerFactory.newController();
+    try {
+      stub.closeRegion(hrc, ProtobufUtil.buildCloseRegionRequest(server, 
regionName));
+    } catch (ServiceException e) {
+      throw ProtobufUtil.getRemoteException(e);
+    }
+  }
+
+  public RegionInfo getRegionInfo(byte[] regionName) throws IOException {
+    HBaseRpcController hrc = rpcControllerFactory.newController();
+    try {
+      GetRegionInfoRequest request = RegionInfo.isEncodedRegionName(regionName)
+        ? GetRegionInfoRequest.newBuilder()
+          
.setRegion(RequestConverter.buildRegionSpecifier(RegionSpecifierType.ENCODED_REGION_NAME,
+            regionName))
+          .build()
+        : RequestConverter.buildGetRegionInfoRequest(regionName);
+      GetRegionInfoResponse response = stub.getRegionInfo(hrc, request);
+      return ProtobufUtil.toRegionInfo(response.getRegionInfo());
+    } catch (ServiceException e) {
+      throw ProtobufUtil.getRemoteException(e);
+    }
+  }
+}
diff --git 
a/hbase-hbck2/src/main/java/org/apache/hbase/hbck1/HBaseFsckRepair.java 
b/hbase-hbck2/src/main/java/org/apache/hbase/hbck1/HBaseFsckRepair.java
index ebde4bf..63c1ac6 100644
--- a/hbase-hbck2/src/main/java/org/apache/hbase/hbck1/HBaseFsckRepair.java
+++ b/hbase-hbck2/src/main/java/org/apache/hbase/hbck1/HBaseFsckRepair.java
@@ -25,22 +25,23 @@ import java.util.Random;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hbase.ClusterMetrics.Option;
+import org.apache.hadoop.hbase.NotServingRegionException;
 import org.apache.hadoop.hbase.ServerName;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.ZooKeeperConnectionException;
 import org.apache.hadoop.hbase.client.Admin;
-import org.apache.hadoop.hbase.client.ClusterConnection;
 import org.apache.hadoop.hbase.client.Connection;
 import org.apache.hadoop.hbase.client.ConnectionFactory;
 import org.apache.hadoop.hbase.client.Put;
 import org.apache.hadoop.hbase.client.RegionInfo;
 import org.apache.hadoop.hbase.client.Table;
 import org.apache.hadoop.hbase.client.TableDescriptor;
+import org.apache.hadoop.hbase.ipc.RemoteWithExtrasException;
 import org.apache.hadoop.hbase.master.RegionState;
-import org.apache.hadoop.hbase.master.ServerManager;
 import org.apache.hadoop.hbase.regionserver.HRegion;
 import org.apache.hadoop.hbase.util.CommonFSUtils;
 import org.apache.hbase.HBCKMetaTableAccessor;
+import org.apache.hbase.HBCKRegionServerAdmin;
 import org.apache.zookeeper.KeeperException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -139,8 +140,33 @@ public final class HBaseFsckRepair {
   public static void closeRegionSilentlyAndWait(Connection connection, 
ServerName server,
     RegionInfo region) throws IOException, InterruptedException {
     long timeout = 
connection.getConfiguration().getLong("hbase.hbck.close.timeout", 120000);
-    ServerManager.closeRegionSilentlyAndWait((ClusterConnection) connection, 
server, region,
-      timeout);
+    HBCKRegionServerAdmin admin = new HBCKRegionServerAdmin(connection, 
server);
+    admin.closeRegion(region.getRegionName());
+    if (timeout < 0) {
+      return;
+    }
+    long expiration = timeout + System.currentTimeMillis();
+    while (System.currentTimeMillis() < expiration) {
+      try {
+        RegionInfo rsRegion = admin.getRegionInfo(region.getRegionName());
+        if (rsRegion == null) {
+          return;
+        }
+      } catch (IOException ioe) {
+        if (
+          ioe instanceof NotServingRegionException
+            || (ioe instanceof RemoteWithExtrasException && 
((RemoteWithExtrasException) ioe)
+              .unwrapRemoteException() instanceof NotServingRegionException)
+        ) {
+          // no need to retry again
+          return;
+        }
+        LOG.warn("Exception when retrieving regioninfo from: {}", 
region.getRegionNameAsString(),
+          ioe);
+      }
+      Thread.sleep(1000);
+    }
+    throw new IOException("Region " + region + " failed to close within 
timeout " + timeout);
   }
 
   /**

Reply via email to