diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HRegionInfo.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HRegionInfo.java index 80b7c42561d..918f1463a51 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/HRegionInfo.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HRegionInfo.java @@ -148,6 +148,9 @@ public class HRegionInfo implements Comparable { private static final int MAX_REPLICA_ID = 0xFFFF; public static final int DEFAULT_REPLICA_ID = 0; + + public static final String INVALID_REGION_NAME_FORMAT_MESSAGE = "Invalid regionName format"; + /** * Does region name contain its encoded name? * @param regionName region name @@ -575,7 +578,8 @@ public class HRegionInfo implements Comparable { } } if (offset == -1) { - throw new IOException("Invalid regionName format: " + Bytes.toStringBinary(regionName)); + throw new IOException(INVALID_REGION_NAME_FORMAT_MESSAGE + + ": " + Bytes.toStringBinary(regionName)); } byte[] tableName = new byte[offset]; System.arraycopy(regionName, 0, tableName, 0, offset); @@ -606,7 +610,8 @@ public class HRegionInfo implements Comparable { } } if (offset == -1) { - throw new IOException("Invalid regionName format: " + Bytes.toStringBinary(regionName)); + throw new IOException(INVALID_REGION_NAME_FORMAT_MESSAGE + + ": " + Bytes.toStringBinary(regionName)); } byte [] startKey = HConstants.EMPTY_BYTE_ARRAY; if(offset != tableName.length + 1) { diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java index ae0ec35180a..b40f30e2c95 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Admin.java @@ -768,13 +768,13 @@ public interface Admin extends Abortable, Closeable { /** * Merge two regions. Asynchronous operation. * - * @param encodedNameOfRegionA encoded name of region a - * @param encodedNameOfRegionB encoded name of region b + * @param nameOfRegionA encoded or full name of region a + * @param nameOfRegionB encoded or full name of region b * @param forcible true if do a compulsory merge, otherwise we will only merge two adjacent * regions * @throws IOException */ - void mergeRegions(final byte[] encodedNameOfRegionA, final byte[] encodedNameOfRegionB, + void mergeRegions(final byte[] nameOfRegionA, final byte[] nameOfRegionB, final boolean forcible) throws IOException; /** diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java index 3b808440cf7..b2c7f51223a 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java @@ -2434,22 +2434,40 @@ public class HBaseAdmin implements Admin { }); } + private boolean isEncodedRegionName(byte[] regionName) throws IOException { + try { + HRegionInfo.parseRegionName(regionName); + return false; + } catch (IOException e) { + if (StringUtils.stringifyException(e) + .contains(HRegionInfo.INVALID_REGION_NAME_FORMAT_MESSAGE)) { + return true; + } + throw e; + } + } + /** * Merge two regions. Asynchronous operation. - * @param encodedNameOfRegionA encoded name of region a - * @param encodedNameOfRegionB encoded name of region b + * @param nameOfRegionA encoded or full name of region a + * @param nameOfRegionB encoded or full name of region b * @param forcible true if do a compulsory merge, otherwise we will only merge * two adjacent regions * @throws IOException */ @Override - public void mergeRegions(final byte[] encodedNameOfRegionA, - final byte[] encodedNameOfRegionB, final boolean forcible) + public void mergeRegions(final byte[] nameOfRegionA, + final byte[] nameOfRegionB, final boolean forcible) throws IOException { - Pair pair = getRegion(encodedNameOfRegionA); + final byte[] encodedNameOfRegionA = isEncodedRegionName(nameOfRegionA) ? + nameOfRegionA : HRegionInfo.encodeRegionName(nameOfRegionA).getBytes(); + final byte[] encodedNameOfRegionB = isEncodedRegionName(nameOfRegionB) ? + nameOfRegionB : HRegionInfo.encodeRegionName(nameOfRegionB).getBytes(); + + Pair pair = getRegion(nameOfRegionA); if (pair != null && pair.getFirst().getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID) throw new IllegalArgumentException("Can't invoke merge on non-default regions directly"); - pair = getRegion(encodedNameOfRegionB); + pair = getRegion(nameOfRegionB); if (pair != null && pair.getFirst().getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID) throw new IllegalArgumentException("Can't invoke merge on non-default regions directly"); executeCallable(new MasterCallable(getConnection()) { diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAdmin1.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAdmin1.java index a3652200d1d..6b68b5ec50f 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAdmin1.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAdmin1.java @@ -1407,4 +1407,43 @@ public class TestAdmin1 { } } } + + @Test + public void testMergeRegions() throws Exception { + TableName tableName = TableName.valueOf("testMergeWithFullRegionName"); + HColumnDescriptor cd = new HColumnDescriptor("d"); + HTableDescriptor td = new HTableDescriptor(tableName); + td.addFamily(cd); + byte[][] splitRows = new byte[2][]; + splitRows[0] = new byte[]{(byte)'3'}; + splitRows[1] = new byte[]{(byte)'6'}; + try { + TEST_UTIL.createTable(td, splitRows); + TEST_UTIL.waitTableAvailable(tableName); + + List tableRegions; + HRegionInfo regionA; + HRegionInfo regionB; + + // merge with full name + tableRegions = admin.getTableRegions(tableName); + assertEquals(3, admin.getTableRegions(tableName).size()); + regionA = tableRegions.get(0); + regionB = tableRegions.get(1); + admin.mergeRegions(regionA.getRegionName(), regionB.getRegionName(), false); + Thread.sleep(1000); + assertEquals(2, admin.getTableRegions(tableName).size()); + + // merge with encoded name + tableRegions = admin.getTableRegions(tableName); + regionA = tableRegions.get(0); + regionB = tableRegions.get(1); + admin.mergeRegions(regionA.getEncodedNameAsBytes(), regionB.getEncodedNameAsBytes(), false); + Thread.sleep(1000); + assertEquals(1, admin.getTableRegions(tableName).size()); + } finally { + this.admin.disableTable(tableName); + this.admin.deleteTable(tableName); + } + } }