You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by en...@apache.org on 2013/05/16 20:06:27 UTC

svn commit: r1483485 - in /hbase/branches/0.94/src: main/java/org/apache/hadoop/hbase/catalog/ main/java/org/apache/hadoop/hbase/client/ main/java/org/apache/hadoop/hbase/master/ main/java/org/apache/hadoop/hbase/util/ test/java/org/apache/hadoop/hbase...

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();