diff --git a/CHANGES.txt b/CHANGES.txt
index 42dc088ca4e..3ea4b6e93e3 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -478,6 +478,8 @@ Release 0.90.5 - Unreleased
Campbell)
HBASE-4095 Hlog may not be rolled in a long time if checkLowReplication's
request of LogRoll is blocked (Jieshan Bean)
+ HBASE-4217 HRS.closeRegion should be able to close regions with only
+ the encoded name (ramkrishna.s.vasudevan)
IMPROVEMENT
HBASE-4205 Enhance HTable javadoc (Eric Charles)
diff --git a/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java b/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java
index b5d65ce976a..ab35641e636 100644
--- a/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java
+++ b/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java
@@ -1009,6 +1009,45 @@ public class HBaseAdmin implements Abortable, Closeable {
}
}
+ /**
+ * For expert-admins. Runs close on the regionserver. Closes a region based on
+ * the encoded region name. The region server name is mandatory. If the
+ * servername is provided then based on the online regions in the specified
+ * regionserver the specified region will be closed. The master will not be
+ * informed of the close. Note that the regionname is the encoded regionname.
+ *
+ * @param encodedRegionName
+ * The encoded region name; i.e. the hash that makes up the region
+ * name suffix: e.g. if regionname is
+ * TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396.
+ * , then the encoded region name is:
+ * 527db22f95c8a9e0116f0cc13c680396
.
+ * @param serverName
+ * The servername of the regionserver. A server name is made of host,
+ * port and startcode. This is mandatory. Here is an example:
+ * host187.example.com,60020,1289493121758
+ * @return true if the region was closed, false if not.
+ * @throws IOException
+ * if a remote or network exception occurs
+ */
+ public boolean closeRegionWithEncodedRegionName(final String encodedRegionName,
+ final String serverName) throws IOException {
+ byte[] encodedRegionNameInBytes = Bytes.toBytes(encodedRegionName);
+ if (null == serverName || ("").equals(serverName.trim())) {
+ throw new IllegalArgumentException(
+ "The servername cannot be null or empty.");
+ }
+ ServerName sn = new ServerName(serverName);
+ HRegionInterface rs = this.connection.getHRegionConnection(
+ sn.getHostname(), sn.getPort());
+ // Close the region without updating zk state.
+ boolean isRegionClosed = rs.closeRegion(encodedRegionNameInBytes, false);
+ if (false == isRegionClosed) {
+ LOG.error("Not able to close the region " + encodedRegionName + ".");
+ }
+ return isRegionClosed;
+ }
+
/**
* Close a region. For expert-admins Runs close on the regionserver. The
* master will not be informed of the close.
diff --git a/src/main/java/org/apache/hadoop/hbase/ipc/HRegionInterface.java b/src/main/java/org/apache/hadoop/hbase/ipc/HRegionInterface.java
index c1a7c347dc8..8d8908c01ab 100644
--- a/src/main/java/org/apache/hadoop/hbase/ipc/HRegionInterface.java
+++ b/src/main/java/org/apache/hadoop/hbase/ipc/HRegionInterface.java
@@ -366,6 +366,21 @@ public interface HRegionInterface extends VersionedProtocol, Stoppable, Abortabl
*/
public boolean closeRegion(final HRegionInfo region, final boolean zk)
throws IOException;
+
+ /**
+ * Closes the region in the RS with the specified encoded regionName and will
+ * use or not use ZK during the close according to the specified flag. Note
+ * that the encoded region name is in byte format.
+ *
+ * @param encodedRegionName
+ * in bytes
+ * @param zk
+ * true if to use zookeeper, false if need not.
+ * @return true if region is closed, false if not.
+ * @throws IOException
+ */
+ public boolean closeRegion(byte[] encodedRegionName, final boolean zk)
+ throws IOException;
// Region administrative methods
diff --git a/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java b/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
index 3efba7f7fca..58f0350f6f1 100644
--- a/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
+++ b/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
@@ -2361,6 +2361,12 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler,
}
return closeRegion(region, false, zk);
}
+
+ @Override
+ @QosPriority(priority=HIGH_QOS)
+ public boolean closeRegion(byte[] encodedRegionName, boolean zk) throws IOException {
+ return closeRegion(encodedRegionName, false, zk);
+ }
/**
* @param region Region to close
@@ -2389,6 +2395,29 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler,
this.service.submit(crh);
return true;
}
+
+ /**
+ * @param encodedRegionName
+ * encodedregionName to close
+ * @param abort
+ * True if we are aborting
+ * @param zk
+ * True if we are to update zk about the region close; if the close
+ * was orchestrated by master, then update zk. If the close is being
+ * run by the regionserver because its going down, don't update zk.
+ * @return True if closed a region.
+ */
+ protected boolean closeRegion(byte[] encodedRegionName, final boolean abort,
+ final boolean zk) throws IOException {
+ String encodedRegionNameStr = Bytes.toString(encodedRegionName);
+ HRegion region = this.getFromOnlineRegions(encodedRegionNameStr);
+ if (null != region) {
+ return closeRegion(region.getRegionInfo(), abort, zk);
+ }
+ LOG.error("The specified region name" + encodedRegionNameStr
+ + " does not exist to close the region.");
+ return false;
+ }
// Manual remote region administration RPCs
@@ -3031,4 +3060,6 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler,
return c.getBlockCacheColumnFamilySummaries(this.conf);
}
+
+
}
diff --git a/src/main/ruby/hbase/admin.rb b/src/main/ruby/hbase/admin.rb
index acabaa81c6c..7f5aec81c19 100644
--- a/src/main/ruby/hbase/admin.rb
+++ b/src/main/ruby/hbase/admin.rb
@@ -209,13 +209,18 @@ module Hbase
@admin.createTable(htd, splits)
end
end
-
+
#----------------------------------------------------------------------------------------------
- # Closes a region
- def close_region(region_name, server = nil)
- @admin.closeRegion(region_name, server)
+ # Closes a region.
+ # If server name is nil, we presume region_name is full region name (HRegionInfo.getRegionName).
+ # If server name is not nil, we presume it is the region's encoded name (HRegionInfo.getEncodedName)
+ def close_region(region_name, server)
+ if (server == nil || !closeEncodedRegion?(region_name, server))
+ @admin.closeRegion(region_name, server)
+ end
end
+ #----------------------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------------
# Assign a region
def assign(region_name, force)
@@ -389,6 +394,12 @@ module Hbase
@admin.isTableEnabled(table_name)
end
+ #----------------------------------------------------------------------------------------------
+ #Is supplied region name is encoded region name
+ def closeEncodedRegion?(region_name, server)
+ @admin.closeRegionWithEncodedRegionName(region_name, server)
+ end
+
#----------------------------------------------------------------------------------------------
# Return a new HColumnDescriptor made of passed args
def hcd(arg, htd)
diff --git a/src/main/ruby/shell/commands/close_region.rb b/src/main/ruby/shell/commands/close_region.rb
index 302c832f92a..f1010ac6ca0 100644
--- a/src/main/ruby/shell/commands/close_region.rb
+++ b/src/main/ruby/shell/commands/close_region.rb
@@ -23,14 +23,25 @@ module Shell
class CloseRegion < Command
def help
return <<-EOF
-Close a single region. Optionally specify regionserver 'servername' where
-A server name is its host, port plus startcode. For example:
-host187.example.com,60020,1289493121758 (find servername in master ui or
-when you do detailed status in shell). Connects to the regionserver and
-runs close on hosting regionserver. The close is done without the master's
-involvement (It will not know of the close). Once closed, region will stay
-closed. Use assign to reopen/reassign. Use unassign or move to assign the
-region elsewhere on cluster. Use with caution. For experts only. Examples:
+Close a single region. Ask the master to close a region out on the cluster
+or if 'SERVER_NAME' is supplied, ask the designated hosting regionserver to
+close the region directly. Closing a region, the master expects 'REGIONNAME'
+to be a fully qualified region name. When asking the hosting regionserver to
+directly close a region, you pass the regions' encoded name only. A region
+name looks like this:
+
+ TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396.
+
+The trailing period is part of the regionserver name. A region's encoded name
+is the hash at the end of a region name; e.g. 527db22f95c8a9e0116f0cc13c680396
+(without the period). A 'SERVER_NAME' is its host, port plus startcode. For
+example: host187.example.com,60020,1289493121758 (find servername in master ui
+or when you do detailed status in shell). This command will end up running
+close on the region hosting regionserver. The close is done without the
+master's involvement (It will not know of the close). Once closed, region will
+stay closed. Use assign to reopen/reassign. Use unassign or move to assign
+the region elsewhere on cluster. Use with caution. For experts only.
+Examples:
hbase> close_region 'REGIONNAME'
hbase> close_region 'REGIONNAME', 'SERVER_NAME'
diff --git a/src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java b/src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java
index 5965919320a..f040655bf4a 100644
--- a/src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java
+++ b/src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java
@@ -23,6 +23,7 @@ package org.apache.hadoop.hbase.client;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.ArrayList;
@@ -35,6 +36,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HConstants;
@@ -49,7 +51,11 @@ import org.apache.hadoop.hbase.executor.EventHandler;
import org.apache.hadoop.hbase.executor.EventHandler.EventType;
import org.apache.hadoop.hbase.executor.ExecutorService;
import org.apache.hadoop.hbase.master.MasterServices;
+import org.apache.hadoop.hbase.regionserver.HRegionServer;
import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.zookeeper.ZKAssign;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import org.apache.zookeeper.KeeperException;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -904,6 +910,157 @@ public class TestAdmin {
new HTable(TEST_UTIL.getConfiguration(),
"testTableNotFoundExceptionWithoutAnyTables");
}
+ @Test
+ public void testShouldCloseTheRegionBasedOnTheEncodedRegionName()
+ throws Exception {
+ byte[] TABLENAME = Bytes.toBytes("TestHBACloseRegion");
+ HBaseAdmin admin = createTable(TABLENAME);
+
+ HRegionInfo info = null;
+ HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLENAME);
+ List onlineRegions = rs.getOnlineRegions();
+ for (HRegionInfo regionInfo : onlineRegions) {
+ if (!regionInfo.isMetaRegion() && !regionInfo.isRootRegion()) {
+ info = regionInfo;
+ admin.closeRegionWithEncodedRegionName(regionInfo.getEncodedName(), rs
+ .getServerName().getServerName());
+ }
+ }
+ Thread.sleep(1000);
+ onlineRegions = rs.getOnlineRegions();
+ assertFalse("The region should not be present in online regions list.",
+ onlineRegions.contains(info));
+ }
+
+ @Test
+ public void testCloseRegionIfInvalidRegionNameIsPassed() throws Exception {
+ byte[] TABLENAME = Bytes.toBytes("TestHBACloseRegion1");
+ HBaseAdmin admin = createTable(TABLENAME);
+
+ HRegionInfo info = null;
+ HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLENAME);
+ List onlineRegions = rs.getOnlineRegions();
+ for (HRegionInfo regionInfo : onlineRegions) {
+ if (!regionInfo.isMetaRegion() && !regionInfo.isRootRegion()) {
+ if (regionInfo.getRegionNameAsString().contains("TestHBACloseRegion1")) {
+ info = regionInfo;
+ admin.closeRegionWithEncodedRegionName("sample", rs.getServerName()
+ .getServerName());
+ }
+ }
+ }
+ onlineRegions = rs.getOnlineRegions();
+ assertTrue("The region should be present in online regions list.",
+ onlineRegions.contains(info));
+ }
+
+ @Test
+ public void testCloseRegionThatFetchesTheHRIFromMeta() throws Exception {
+ byte[] TABLENAME = Bytes.toBytes("TestHBACloseRegion2");
+ HBaseAdmin admin = createTable(TABLENAME);
+
+ HRegionInfo info = null;
+ HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLENAME);
+ List onlineRegions = rs.getOnlineRegions();
+ for (HRegionInfo regionInfo : onlineRegions) {
+ if (!regionInfo.isMetaRegion() && !regionInfo.isRootRegion()) {
+
+ if (regionInfo.getRegionNameAsString().contains("TestHBACloseRegion2")) {
+ info = regionInfo;
+ admin.closeRegion(regionInfo.getRegionNameAsString(), rs
+ .getServerName().getServerName());
+ }
+ }
+ }
+ Thread.sleep(1000);
+ onlineRegions = rs.getOnlineRegions();
+ assertFalse("The region should not be present in online regions list.",
+ onlineRegions.contains(info));
+ }
+
+ @Test
+ public void testCloseRegionWhenServerNameIsNull() throws Exception {
+ byte[] TABLENAME = Bytes.toBytes("TestHBACloseRegion3");
+ HBaseAdmin admin = createTable(TABLENAME);
+
+ HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLENAME);
+
+ try {
+ List onlineRegions = rs.getOnlineRegions();
+ for (HRegionInfo regionInfo : onlineRegions) {
+ if (!regionInfo.isMetaRegion() && !regionInfo.isRootRegion()) {
+ if (regionInfo.getRegionNameAsString()
+ .contains("TestHBACloseRegion3")) {
+ admin.closeRegionWithEncodedRegionName(regionInfo.getEncodedName(),
+ null);
+ }
+ }
+ }
+ fail("The test should throw exception if the servername passed is null.");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+
+ @Test
+ public void testCloseRegionWhenServerNameIsEmpty() throws Exception {
+ byte[] TABLENAME = Bytes.toBytes("TestHBACloseRegion3");
+ HBaseAdmin admin = createTable(TABLENAME);
+
+ HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLENAME);
+
+ try {
+ List onlineRegions = rs.getOnlineRegions();
+ for (HRegionInfo regionInfo : onlineRegions) {
+ if (!regionInfo.isMetaRegion() && !regionInfo.isRootRegion()) {
+ if (regionInfo.getRegionNameAsString()
+ .contains("TestHBACloseRegion3")) {
+ admin.closeRegionWithEncodedRegionName(regionInfo.getEncodedName(),
+ " ");
+ }
+ }
+ }
+ fail("The test should throw exception if the servername passed is empty.");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testCloseRegionWhenEncodedRegionNameIsNotGiven() throws Exception {
+ byte[] TABLENAME = Bytes.toBytes("TestHBACloseRegion4");
+ HBaseAdmin admin = createTable(TABLENAME);
+
+ HRegionInfo info = null;
+ HRegionServer rs = TEST_UTIL.getRSForFirstRegionInTable(TABLENAME);
+
+ List onlineRegions = rs.getOnlineRegions();
+ for (HRegionInfo regionInfo : onlineRegions) {
+ if (!regionInfo.isMetaRegion() && !regionInfo.isRootRegion()) {
+ if (regionInfo.getRegionNameAsString().contains("TestHBACloseRegion4")) {
+ info = regionInfo;
+ admin.closeRegionWithEncodedRegionName(regionInfo
+ .getRegionNameAsString(), rs.getServerName().getServerName());
+ }
+ }
+ }
+ onlineRegions = rs.getOnlineRegions();
+ assertTrue("The region should be present in online regions list.",
+ onlineRegions.contains(info));
+ }
+
+ private HBaseAdmin createTable(byte[] TABLENAME) throws IOException {
+
+ Configuration config = TEST_UTIL.getConfiguration();
+ HBaseAdmin admin = new HBaseAdmin(config);
+
+ HTableDescriptor htd = new HTableDescriptor(TABLENAME);
+ HColumnDescriptor hcd = new HColumnDescriptor("value");
+
+ htd.addFamily(hcd);
+ admin.createTable(htd, null);
+ return admin;
+ }
+
@Test
public void testHundredsOfTable() throws IOException{