From 581af1b086b65f89f402b4bcb42773880f3e3137 Mon Sep 17 00:00:00 2001 From: Michael Stack Date: Thu, 25 Aug 2011 17:29:54 +0000 Subject: [PATCH] HBASE-4217 HRS.closeRegion should be able to close regions with only the encoded name git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1161658 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES.txt | 2 + .../hadoop/hbase/client/HBaseAdmin.java | 39 +++++ .../hadoop/hbase/ipc/HRegionInterface.java | 15 ++ .../hbase/regionserver/HRegionServer.java | 31 ++++ src/main/ruby/hbase/admin.rb | 19 ++- src/main/ruby/shell/commands/close_region.rb | 27 ++- .../apache/hadoop/hbase/client/TestAdmin.java | 157 ++++++++++++++++++ 7 files changed, 278 insertions(+), 12 deletions(-) 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{