Author: enis Date: Thu May 16 18:06:26 2013 New Revision: 1483485 URL: http://svn.apache.org/r1483485 Log: HBASE-8505 References to split daughters should not be deleted separately from parent META entry
Modified: hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/master/CatalogJanitor.java hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/util/Bytes.java hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/client/TestMetaScanner.java hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java Modified: hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java URL: http://svn.apache.org/viewvc/hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java?rev=1483485&r1=1483484&r2=1483485&view=diff ============================================================================== --- hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java (original) +++ hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java Thu May 16 18:06:26 2013 @@ -21,7 +21,6 @@ import java.io.IOException; import java.io.InterruptedIOException; import java.net.ConnectException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.apache.commons.logging.Log; @@ -358,25 +357,6 @@ public class MetaEditor { } } - /** - * Deletes daughters references in offlined split parent. - * @param catalogTracker - * @param parent Parent row we're to remove daughter reference from - * @throws NotAllMetaRegionsOnlineException - * @throws IOException - */ - public static void deleteDaughtersReferencesInParent(CatalogTracker catalogTracker, - final HRegionInfo parent) - throws NotAllMetaRegionsOnlineException, IOException { - Delete delete = new Delete(parent.getRegionName()); - delete.deleteColumns(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER); - delete.deleteColumns(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER); - deleteFromMetaTable(catalogTracker, delete); - LOG.info("Deleted daughters references, qualifier=" + Bytes.toStringBinary(HConstants.SPLITA_QUALIFIER) + - " and qualifier=" + Bytes.toStringBinary(HConstants.SPLITB_QUALIFIER) + - ", from parent " + parent.getRegionNameAsString()); - } - public static HRegionInfo getHRegionInfo( Result data) throws IOException { byte [] bytes = Modified: hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java URL: http://svn.apache.org/viewvc/hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java?rev=1483485&r1=1483484&r2=1483485&view=diff ============================================================================== --- hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java (original) +++ hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/client/MetaScanner.java Thu May 16 18:06:26 2013 @@ -403,32 +403,36 @@ public class MetaScanner { * seen by this scanner as well, so we block until they are added to the META table. Even * though we are waiting for META entries, ACID semantics in HBase indicates that this * scanner might not see the new rows. So we manually query the daughter rows */ - HRegionInfo splitA = Writables.getHRegionInfo(rowResult.getValue(HConstants.CATALOG_FAMILY, + HRegionInfo splitA = Writables.getHRegionInfoOrNull(rowResult.getValue(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER)); - HRegionInfo splitB = Writables.getHRegionInfo(rowResult.getValue(HConstants.CATALOG_FAMILY, + HRegionInfo splitB = Writables.getHRegionInfoOrNull(rowResult.getValue(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER)); HTable metaTable = getMetaTable(); long start = System.currentTimeMillis(); - Result resultA = getRegionResultBlocking(metaTable, blockingTimeout, - splitA.getRegionName()); - if (resultA != null) { - processRow(resultA); - daughterRegions.add(splitA.getRegionName()); - } else { - throw new RegionOfflineException("Split daughter region " + - splitA.getRegionNameAsString() + " cannot be found in META."); + if (splitA != null) { + Result resultA = getRegionResultBlocking(metaTable, blockingTimeout, + splitA.getRegionName()); + if (resultA != null) { + processRow(resultA); + daughterRegions.add(splitA.getRegionName()); + } else { + throw new RegionOfflineException("Split daughter region " + + splitA.getRegionNameAsString() + " cannot be found in META."); + } } long rem = blockingTimeout - (System.currentTimeMillis() - start); - Result resultB = getRegionResultBlocking(metaTable, rem, - splitB.getRegionName()); - if (resultB != null) { - processRow(resultB); - daughterRegions.add(splitB.getRegionName()); - } else { - throw new RegionOfflineException("Split daughter region " + - splitB.getRegionNameAsString() + " cannot be found in META."); + if (splitB != null) { + Result resultB = getRegionResultBlocking(metaTable, rem, + splitB.getRegionName()); + if (resultB != null) { + processRow(resultB); + daughterRegions.add(splitB.getRegionName()); + } else { + throw new RegionOfflineException("Split daughter region " + + splitB.getRegionNameAsString() + " cannot be found in META."); + } } } @@ -437,9 +441,7 @@ public class MetaScanner { private Result getRegionResultBlocking(HTable metaTable, long timeout, byte[] regionName) throws IOException { - if (LOG.isDebugEnabled()) { - LOG.debug("blocking until region is in META: " + Bytes.toStringBinary(regionName)); - } + boolean logged = false; long start = System.currentTimeMillis(); while (System.currentTimeMillis() - start < timeout) { Get get = new Get(regionName); @@ -450,6 +452,12 @@ public class MetaScanner { return result; } try { + if (!logged) { + if (LOG.isDebugEnabled()) { + LOG.debug("blocking until region is in META: " + Bytes.toStringBinary(regionName)); + } + logged = true; + } Thread.sleep(10); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); Modified: hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/master/CatalogJanitor.java URL: http://svn.apache.org/viewvc/hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/master/CatalogJanitor.java?rev=1483485&r1=1483484&r2=1483485&view=diff ============================================================================== --- hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/master/CatalogJanitor.java (original) +++ hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/master/CatalogJanitor.java Thu May 16 18:06:26 2013 @@ -41,7 +41,8 @@ import org.apache.hadoop.hbase.HTableDes import org.apache.hadoop.hbase.Server; import org.apache.hadoop.hbase.backup.HFileArchiver; import org.apache.hadoop.hbase.catalog.MetaEditor; -import org.apache.hadoop.hbase.catalog.MetaReader; +import org.apache.hadoop.hbase.client.MetaScanner; +import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.regionserver.Store; import org.apache.hadoop.hbase.regionserver.StoreFile; @@ -107,9 +108,10 @@ class CatalogJanitor extends Chore { final Map<HRegionInfo, Result> splitParents = new TreeMap<HRegionInfo, Result>(new SplitParentFirstComparator()); // This visitor collects split parents and counts rows in the .META. table - MetaReader.Visitor visitor = new MetaReader.Visitor() { + + MetaScannerVisitor visitor = new MetaScanner.BlockingMetaScannerVisitor(server.getConfiguration()) { @Override - public boolean visit(Result r) throws IOException { + public boolean processRowInternal(Result r) throws IOException { if (r == null || r.isEmpty()) return true; count.incrementAndGet(); HRegionInfo info = getHRegionInfo(r); @@ -119,8 +121,9 @@ class CatalogJanitor extends Chore { return true; } }; + // Run full scan of .META. catalog table passing in our custom visitor - MetaReader.fullScan(this.server.getCatalogTracker(), visitor); + MetaScanner.metaScan(server.getConfiguration(), visitor); return new Pair<Integer, Map<HRegionInfo, Result>>(count.get(), splitParents); } @@ -164,6 +167,7 @@ class CatalogJanitor extends Chore { * daughters. */ static class SplitParentFirstComparator implements Comparator<HRegionInfo> { + Comparator<byte[]> rowEndKeyComparator = new Bytes.RowEndKeyComparator(); @Override public int compare(HRegionInfo left, HRegionInfo right) { // This comparator differs from the one HRegionInfo in that it sorts @@ -178,19 +182,9 @@ class CatalogJanitor extends Chore { result = Bytes.compareTo(left.getStartKey(), right.getStartKey()); if (result != 0) return result; // Compare end keys. - result = Bytes.compareTo(left.getEndKey(), right.getEndKey()); - if (result != 0) { - if (left.getStartKey().length != 0 - && left.getEndKey().length == 0) { - return -1; // left is last region - } - if (right.getStartKey().length != 0 - && right.getEndKey().length == 0) { - return 1; // right is the last region - } - return -result; // Flip the result so parent comes first. - } - return result; + result = rowEndKeyComparator.compare(left.getEndKey(), right.getEndKey()); + + return -result; // Flip the result so parent comes first. } } @@ -235,8 +229,6 @@ class CatalogJanitor extends Chore { if (hasNoReferences(a) && hasNoReferences(b)) { LOG.debug("Deleting region " + parent.getRegionNameAsString() + " because daughter splits no longer hold references"); - // wipe out daughter references from parent region in meta - removeDaughtersFromParent(parent); // This latter regionOffline should not be necessary but is done for now // until we let go of regionserver to master heartbeats. See HBASE-3368. @@ -279,16 +271,6 @@ class CatalogJanitor extends Chore { } /** - * Remove mention of daughters from parent row. - * @param parent - * @throws IOException - */ - private void removeDaughtersFromParent(final HRegionInfo parent) - throws IOException { - MetaEditor.deleteDaughtersReferencesInParent(this.server.getCatalogTracker(), parent); - } - - /** * Checks if a daughter region -- either splitA or splitB -- still holds * references to parent. * @param parent Parent region name. Modified: hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/util/Bytes.java URL: http://svn.apache.org/viewvc/hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/util/Bytes.java?rev=1483485&r1=1483484&r2=1483485&view=diff ============================================================================== --- hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/util/Bytes.java (original) +++ hbase/branches/0.94/src/main/java/org/apache/hadoop/hbase/util/Bytes.java Thu May 16 18:06:26 2013 @@ -33,7 +33,6 @@ import java.security.PrivilegedAction; import java.util.Comparator; import java.util.Iterator; -import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.HConstants; @@ -124,6 +123,34 @@ public class Bytes { } /** + * A {@link ByteArrayComparator} that treats the empty array as the largest value. + * This is useful for comparing row end keys for regions. + */ + // TODO: unfortunately, HBase uses byte[0] as both start and end keys for region + // boundaries. Thus semantically, we should treat empty byte array as the smallest value + // while comparing row keys, start keys etc; but as the largest value for comparing + // region boundaries for endKeys. + public static class RowEndKeyComparator extends ByteArrayComparator { + @Override + public int compare(byte[] left, byte[] right) { + return compare(left, 0, left.length, right, 0, right.length); + } + @Override + public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) { + if (b1 == b2 && s1 == s2 && l1 == l2) { + return 0; + } + if (l1 == 0) { + return l2; //0 or positive + } + if (l2 == 0) { + return -1; + } + return super.compare(b1, s1, l1, b2, s2, l2); + } + } + + /** * Pass this to TreeMaps where byte [] are keys. */ public static Comparator<byte []> BYTES_COMPARATOR = @@ -339,9 +366,9 @@ public class Bytes { */ public static String toStringBinary(final byte [] b, int off, int len) { StringBuilder result = new StringBuilder(); - for (int i = off; i < off + len ; ++i ) { + for (int i = off; i < off + len ; ++i ) { int ch = b[i] & 0xFF; - if ( (ch >= '0' && ch <= '9') + if ( (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || " `~!@#$%^&*()-_=+[]{}|;:'\",.<>/?".indexOf(ch) >= 0 ) { Modified: hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/client/TestMetaScanner.java URL: http://svn.apache.org/viewvc/hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/client/TestMetaScanner.java?rev=1483485&r1=1483484&r2=1483485&view=diff ============================================================================== --- hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/client/TestMetaScanner.java (original) +++ hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/client/TestMetaScanner.java Thu May 16 18:06:26 2013 @@ -19,39 +19,55 @@ */ package org.apache.hadoop.hbase.client; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.math.BigDecimal; +import java.util.List; +import java.util.NavigableMap; +import java.util.Random; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.*; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.MediumTests; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.catalog.CatalogTracker; +import org.apache.hadoop.hbase.catalog.MetaEditor; import org.apache.hadoop.hbase.util.Bytes; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import org.apache.hadoop.hbase.util.StoppableImplementation; +import org.apache.hadoop.hbase.util.Threads; +import org.apache.hadoop.util.StringUtils; +import org.junit.After; +import org.junit.Assert; import org.junit.Test; import org.junit.experimental.categories.Category; - -import static org.mockito.Mockito.*; - @Category(MediumTests.class) public class TestMetaScanner { final Log LOG = LogFactory.getLog(getClass()); private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); - @BeforeClass - public static void setUpBeforeClass() throws Exception { + public void setUp() throws Exception { TEST_UTIL.startMiniCluster(1); } - /** - * @throws java.lang.Exception - */ - @AfterClass - public static void tearDownAfterClass() throws Exception { + @After + public void tearDown() throws Exception { TEST_UTIL.shutdownMiniCluster(); } @Test public void testMetaScanner() throws Exception { LOG.info("Starting testMetaScanner"); + setUp(); final byte[] TABLENAME = Bytes.toBytes("testMetaScanner"); final byte[] FAMILY = Bytes.toBytes("family"); TEST_UTIL.createTable(TABLENAME, FAMILY); @@ -64,29 +80,29 @@ public class TestMetaScanner { Bytes.toBytes("region_b")}); // Make sure all the regions are deployed TEST_UTIL.countRows(table); - - MetaScanner.MetaScannerVisitor visitor = + + MetaScanner.MetaScannerVisitor visitor = mock(MetaScanner.MetaScannerVisitor.class); doReturn(true).when(visitor).processRow((Result)anyObject()); // Scanning the entire table should give us three rows MetaScanner.metaScan(conf, visitor, TABLENAME); verify(visitor, times(3)).processRow((Result)anyObject()); - + // Scanning the table with a specified empty start row should also // give us three META rows reset(visitor); doReturn(true).when(visitor).processRow((Result)anyObject()); MetaScanner.metaScan(conf, visitor, TABLENAME, HConstants.EMPTY_BYTE_ARRAY, 1000); verify(visitor, times(3)).processRow((Result)anyObject()); - + // Scanning the table starting in the middle should give us two rows: // region_a and region_b reset(visitor); doReturn(true).when(visitor).processRow((Result)anyObject()); MetaScanner.metaScan(conf, visitor, TABLENAME, Bytes.toBytes("region_ac"), 1000); verify(visitor, times(2)).processRow((Result)anyObject()); - + // Scanning with a limit of 1 should only give us one row reset(visitor); doReturn(true).when(visitor).processRow((Result)anyObject()); @@ -95,6 +111,138 @@ public class TestMetaScanner { table.close(); } + @Test + public void testConcurrentMetaScannerAndCatalogJanitor() throws Throwable { + /* TEST PLAN: start with only one region in a table. Have a splitter + * thread and metascanner threads that continously scan the meta table for regions. + * CatalogJanitor from master will run frequently to clean things up + */ + TEST_UTIL.getConfiguration().setLong("hbase.catalogjanitor.interval", 500); + setUp(); + + final long runtime = 30 * 1000; //30 sec + LOG.info("Starting testConcurrentMetaScannerAndCatalogJanitor"); + final byte[] TABLENAME = Bytes.toBytes("testConcurrentMetaScannerAndCatalogJanitor"); + final byte[] FAMILY = Bytes.toBytes("family"); + TEST_UTIL.createTable(TABLENAME, FAMILY); + final CatalogTracker catalogTracker = mock(CatalogTracker.class); + when(catalogTracker.getConnection()).thenReturn(TEST_UTIL.getHBaseAdmin().getConnection()); + + class RegionMetaSplitter extends StoppableImplementation implements Runnable { + Random random = new Random(); + Throwable ex = null; + @Override + public void run() { + while (!isStopped()) { + try { + List<HRegionInfo> regions = MetaScanner.listAllRegions( + TEST_UTIL.getConfiguration(), false); + + //select a random region + HRegionInfo parent = regions.get(random.nextInt(regions.size())); + if (parent == null || !Bytes.equals(TABLENAME, parent.getTableName())) { + continue; + } + + long startKey = 0, endKey = Long.MAX_VALUE; + byte[] start = parent.getStartKey(); + byte[] end = parent.getEndKey(); + if (!Bytes.equals(HConstants.EMPTY_START_ROW, parent.getStartKey())) { + startKey = Bytes.toLong(parent.getStartKey()); + } + if (!Bytes.equals(HConstants.EMPTY_END_ROW, parent.getEndKey())) { + endKey = Bytes.toLong(parent.getEndKey()); + } + if (startKey == endKey) { + continue; + } + + long midKey = BigDecimal.valueOf(startKey).add(BigDecimal.valueOf(endKey)) + .divideToIntegralValue(BigDecimal.valueOf(2)).longValue(); + + HRegionInfo splita = new HRegionInfo(TABLENAME, + start, + Bytes.toBytes(midKey)); + HRegionInfo splitb = new HRegionInfo(TABLENAME, + Bytes.toBytes(midKey), + end); + + MetaEditor.offlineParentInMeta(catalogTracker, parent, splita, splitb); + Threads.sleep(100); + MetaEditor.addDaughter(catalogTracker, splitb, null); + MetaEditor.addDaughter(catalogTracker, splita, null); + + Threads.sleep(random.nextInt(200)); + } catch (Throwable e) { + ex = e; + Assert.fail(StringUtils.stringifyException(e)); + } + } + } + void rethrowExceptionIfAny() throws Throwable { + if (ex != null) { throw ex; } + } + } + + class MetaScannerVerifier extends StoppableImplementation implements Runnable { + Random random = new Random(); + Throwable ex = null; + @Override + public void run() { + while(!isStopped()) { + try { + NavigableMap<HRegionInfo, ServerName> regions = + MetaScanner.allTableRegions(TEST_UTIL.getConfiguration(), TABLENAME, false); + + LOG.info("-------"); + byte[] lastEndKey = HConstants.EMPTY_START_ROW; + for (HRegionInfo hri: regions.navigableKeySet()) { + long startKey = 0, endKey = Long.MAX_VALUE; + if (!Bytes.equals(HConstants.EMPTY_START_ROW, hri.getStartKey())) { + startKey = Bytes.toLong(hri.getStartKey()); + } + if (!Bytes.equals(HConstants.EMPTY_END_ROW, hri.getEndKey())) { + endKey = Bytes.toLong(hri.getEndKey()); + } + LOG.info("start:" + startKey + " end:" + endKey + " hri:" + hri); + Assert.assertTrue(Bytes.equals(lastEndKey, hri.getStartKey())); + lastEndKey = hri.getEndKey(); + } + Assert.assertTrue(Bytes.equals(lastEndKey, HConstants.EMPTY_END_ROW)); + LOG.info("-------"); + Threads.sleep(10 + random.nextInt(50)); + } catch (Throwable e) { + ex = e; + Assert.fail(StringUtils.stringifyException(e)); + } + } + } + void rethrowExceptionIfAny() throws Throwable { + if (ex != null) { throw ex; } + } + } + + RegionMetaSplitter regionMetaSplitter = new RegionMetaSplitter(); + MetaScannerVerifier metaScannerVerifier = new MetaScannerVerifier(); + + Thread regionMetaSplitterThread = new Thread(regionMetaSplitter); + Thread metaScannerVerifierThread = new Thread(metaScannerVerifier); + + regionMetaSplitterThread.start(); + metaScannerVerifierThread.start(); + + Threads.sleep(runtime); + + regionMetaSplitter.stop("test finished"); + metaScannerVerifier.stop("test finished"); + + regionMetaSplitterThread.join(); + metaScannerVerifierThread.join(); + + regionMetaSplitter.rethrowExceptionIfAny(); + metaScannerVerifier.rethrowExceptionIfAny(); + } + @org.junit.Rule public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu = new org.apache.hadoop.hbase.ResourceCheckerJUnitRule(); Modified: hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java URL: http://svn.apache.org/viewvc/hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java?rev=1483485&r1=1483484&r2=1483485&view=diff ============================================================================== --- hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java (original) +++ hbase/branches/0.94/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java Thu May 16 18:06:26 2013 @@ -59,8 +59,8 @@ import org.apache.hadoop.hbase.client.Re import org.apache.hadoop.hbase.executor.ExecutorService; import org.apache.hadoop.hbase.io.Reference; import org.apache.hadoop.hbase.ipc.CoprocessorProtocol; -import org.apache.hadoop.hbase.master.CatalogJanitor.SplitParentFirstComparator; import org.apache.hadoop.hbase.ipc.HRegionInterface; +import org.apache.hadoop.hbase.master.CatalogJanitor.SplitParentFirstComparator; import org.apache.hadoop.hbase.regionserver.Store; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.FSUtils; @@ -242,24 +242,24 @@ public class TestCatalogJanitor { public HTableDescriptor remove(String tablename) throws IOException { return null; } - + @Override public Map<String, HTableDescriptor> getAll() throws IOException { return null; } - + @Override public HTableDescriptor get(byte[] tablename) throws IOException { return get(Bytes.toString(tablename)); } - + @Override public HTableDescriptor get(String tablename) throws IOException { return createHTableDescriptor(); } - + @Override public void add(HTableDescriptor htd) throws IOException { } @@ -589,6 +589,87 @@ public class TestCatalogJanitor { } @Test + public void testSplitParentFirstComparator() { + SplitParentFirstComparator comp = new SplitParentFirstComparator(); + final HTableDescriptor htd = createHTableDescriptor(); + + /* Region splits: + * + * rootRegion --- firstRegion --- firstRegiona + * | |- firstRegionb + * | + * |- lastRegion --- lastRegiona --- lastRegionaa + * | |- lastRegionab + * |- lastRegionb + * + * rootRegion : [] - [] + * firstRegion : [] - bbb + * lastRegion : bbb - [] + * firstRegiona : [] - aaa + * firstRegionb : aaa - bbb + * lastRegiona : bbb - ddd + * lastRegionb : ddd - [] + */ + + // root region + HRegionInfo rootRegion = new HRegionInfo(htd.getName(), HConstants.EMPTY_START_ROW, + HConstants.EMPTY_END_ROW, true); + HRegionInfo firstRegion = new HRegionInfo(htd.getName(), HConstants.EMPTY_START_ROW, + Bytes.toBytes("bbb"), true); + HRegionInfo lastRegion = new HRegionInfo(htd.getName(), Bytes.toBytes("bbb"), + HConstants.EMPTY_END_ROW, true); + + assertTrue(comp.compare(rootRegion, rootRegion) == 0); + assertTrue(comp.compare(firstRegion, firstRegion) == 0); + assertTrue(comp.compare(lastRegion, lastRegion) == 0); + assertTrue(comp.compare(rootRegion, firstRegion) < 0); + assertTrue(comp.compare(rootRegion, lastRegion) < 0); + assertTrue(comp.compare(firstRegion, lastRegion) < 0); + + //first region split into a, b + HRegionInfo firstRegiona = new HRegionInfo(htd.getName(), HConstants.EMPTY_START_ROW, + Bytes.toBytes("aaa"), true); + HRegionInfo firstRegionb = new HRegionInfo(htd.getName(), Bytes.toBytes("aaa"), + Bytes.toBytes("bbb"), true); + //last region split into a, b + HRegionInfo lastRegiona = new HRegionInfo(htd.getName(), Bytes.toBytes("bbb"), + Bytes.toBytes("ddd"), true); + HRegionInfo lastRegionb = new HRegionInfo(htd.getName(), Bytes.toBytes("ddd"), + HConstants.EMPTY_END_ROW, true); + + assertTrue(comp.compare(firstRegiona, firstRegiona) == 0); + assertTrue(comp.compare(firstRegionb, firstRegionb) == 0); + assertTrue(comp.compare(rootRegion, firstRegiona) < 0); + assertTrue(comp.compare(rootRegion, firstRegionb) < 0); + assertTrue(comp.compare(firstRegion, firstRegiona) < 0); + assertTrue(comp.compare(firstRegion, firstRegionb) < 0); + assertTrue(comp.compare(firstRegiona, firstRegionb) < 0); + + assertTrue(comp.compare(lastRegiona, lastRegiona) == 0); + assertTrue(comp.compare(lastRegionb, lastRegionb) == 0); + assertTrue(comp.compare(rootRegion, lastRegiona) < 0); + assertTrue(comp.compare(rootRegion, lastRegionb) < 0); + assertTrue(comp.compare(lastRegion, lastRegiona) < 0); + assertTrue(comp.compare(lastRegion, lastRegionb) < 0); + assertTrue(comp.compare(lastRegiona, lastRegionb) < 0); + + assertTrue(comp.compare(firstRegiona, lastRegiona) < 0); + assertTrue(comp.compare(firstRegiona, lastRegionb) < 0); + assertTrue(comp.compare(firstRegionb, lastRegiona) < 0); + assertTrue(comp.compare(firstRegionb, lastRegionb) < 0); + + HRegionInfo lastRegionaa = new HRegionInfo(htd.getName(), Bytes.toBytes("bbb"), + Bytes.toBytes("ccc"), false); + HRegionInfo lastRegionab = new HRegionInfo(htd.getName(), Bytes.toBytes("ccc"), + Bytes.toBytes("ddd"), false); + + assertTrue(comp.compare(lastRegiona, lastRegionaa) < 0); + assertTrue(comp.compare(lastRegiona, lastRegionab) < 0); + assertTrue(comp.compare(lastRegionaa, lastRegionab) < 0); + + } + + @Test public void testArchiveOldRegion() throws Exception { String table = "table"; HBaseTestingUtility htu = new HBaseTestingUtility();