You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by jx...@apache.org on 2013/07/30 18:53:35 UTC

svn commit: r1508518 - in /hbase/branches/0.95: hbase-client/src/main/java/org/apache/hadoop/hbase/master/ hbase-server/src/main/java/org/apache/hadoop/hbase/master/ hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/ hbase-server/src/te...

Author: jxiang
Date: Tue Jul 30 16:53:35 2013
New Revision: 1508518

URL: http://svn.apache.org/r1508518
Log:
HBASE-9052 Prevent split/merged region from assigning again

Modified:
    hbase/branches/0.95/hbase-client/src/main/java/org/apache/hadoop/hbase/master/RegionState.java
    hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java
    hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java
    hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DeleteTableHandler.java
    hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DisableTableHandler.java
    hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java
    hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionMergeTransactionOnCluster.java
    hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java

Modified: hbase/branches/0.95/hbase-client/src/main/java/org/apache/hadoop/hbase/master/RegionState.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-client/src/main/java/org/apache/hadoop/hbase/master/RegionState.java?rev=1508518&r1=1508517&r2=1508518&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-client/src/main/java/org/apache/hadoop/hbase/master/RegionState.java (original)
+++ hbase/branches/0.95/hbase-client/src/main/java/org/apache/hadoop/hbase/master/RegionState.java Tue Jul 30 16:53:35 2013
@@ -126,6 +126,10 @@ public class RegionState implements org.
     return state == State.SPLITTING;
   }
 
+  public boolean isSplit() {
+    return state == State.SPLIT;
+  }
+
   public boolean isFailedOpen() {
     return state == State.FAILED_OPEN;
   }
@@ -138,6 +142,10 @@ public class RegionState implements org.
     return state == State.MERGING;
   }
 
+  public boolean isMerged() {
+    return state == State.MERGED;
+  }
+
   public boolean isOpenOrMergingOnServer(final ServerName sn) {
     return isOnServer(sn) && (isOpened() || isMerging());
   }

Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java?rev=1508518&r1=1508517&r2=1508518&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java Tue Jul 30 16:53:35 2013
@@ -62,6 +62,7 @@ import org.apache.hadoop.hbase.TableNotF
 import org.apache.hadoop.hbase.executor.EventHandler;
 import org.apache.hadoop.hbase.executor.EventType;
 import org.apache.hadoop.hbase.executor.ExecutorService;
+import org.apache.hadoop.hbase.master.RegionState.State;
 import org.apache.hadoop.hbase.master.balancer.FavoredNodeAssignmentHelper;
 import org.apache.hadoop.hbase.master.balancer.FavoredNodeLoadBalancer;
 import org.apache.hadoop.hbase.master.handler.ClosedRegionHandler;
@@ -1417,10 +1418,7 @@ public class AssignmentManager extends Z
    * @param regionInfo
    */
   public void regionOffline(final HRegionInfo regionInfo) {
-    regionStates.regionOffline(regionInfo);
-    removeClosedRegion(regionInfo);
-    // remove the region plan as well just in case.
-    clearRegionPlan(regionInfo);
+    regionOffline(regionInfo, null);
   }
 
   public void offlineDisabledRegion(HRegionInfo regionInfo) {
@@ -2357,7 +2355,7 @@ public class AssignmentManager extends Z
   public boolean waitForAssignment(HRegionInfo regionInfo)
       throws InterruptedException {
     while (!regionStates.isRegionAssigned(regionInfo)) {
-      if (regionStates.isRegionFailedToOpen(regionInfo)
+      if (regionStates.isRegionInState(regionInfo, State.FAILED_OPEN)
           || this.server.isStopped()) {
         return false;
       }
@@ -3104,7 +3102,7 @@ public class AssignmentManager extends Z
    */
   public void handleSplitReport(final ServerName sn, final HRegionInfo parent,
       final HRegionInfo a, final HRegionInfo b) {
-    regionOffline(parent);
+    regionOffline(parent, State.SPLIT);
     regionOnline(a, sn);
     regionOnline(b, sn);
 
@@ -3128,8 +3126,8 @@ public class AssignmentManager extends Z
    */
   public void handleRegionsMergeReport(final ServerName sn,
       final HRegionInfo merged, final HRegionInfo a, final HRegionInfo b) {
-    regionOffline(a);
-    regionOffline(b);
+    regionOffline(a, State.MERGED);
+    regionOffline(b, State.MERGED);
     regionOnline(merged, sn);
 
     // There's a possibility that the region was merging while a user asked
@@ -3240,4 +3238,16 @@ public class AssignmentManager extends Z
     regionStates.updateRegionState(merging_b, RegionState.State.MERGING);
     return true;
   }
+
+  /**
+   * A region is offline.  The new state should be the specified one,
+   * if not null.  If the specified state is null, the new state is Offline.
+   * The specified state can be Split/Merged/Offline/null only.
+   */
+  private void regionOffline(final HRegionInfo regionInfo, final State state) {
+    regionStates.regionOffline(regionInfo, state);
+    removeClosedRegion(regionInfo);
+    // remove the region plan as well just in case.
+    clearRegionPlan(regionInfo);
+  }
 }

Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java?rev=1508518&r1=1508517&r2=1508518&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java Tue Jul 30 16:53:35 2013
@@ -42,6 +42,8 @@ import org.apache.hadoop.hbase.zookeeper
 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
 import org.apache.zookeeper.KeeperException;
 
+import com.google.common.base.Preconditions;
+
 /**
  * Region state accountant. It holds the states of all regions in the memory.
  * In normal scenario, it should match the meta table and the true region states.
@@ -136,21 +138,13 @@ public class RegionStates {
   }
 
   /**
-   * @return True if specified region failed to open.
+   * @return True if specified region is in specified state
    */
-  public synchronized boolean isRegionFailedToOpen(final HRegionInfo hri) {
-    RegionState regionState = getRegionTransitionState(hri);
-    State state = regionState != null ? regionState.getState() : null;
-    return state == State.FAILED_OPEN;
-  }
-
-  /**
-   * @return True if specified region failed to close.
-   */
-  public synchronized boolean isRegionFailedToClose(final HRegionInfo hri) {
-    RegionState regionState = getRegionTransitionState(hri);
-    State state = regionState != null ? regionState.getState() : null;
-    return state == State.FAILED_CLOSE;
+  public synchronized boolean isRegionInState(
+      final HRegionInfo hri, final State state) {
+    RegionState regionState = getRegionState(hri);
+    State s = regionState != null ? regionState.getState() : null;
+    return s == state;
   }
 
   /**
@@ -315,20 +309,42 @@ public class RegionStates {
   /**
    * A region is offline, won't be in transition any more.
    */
-  public synchronized void regionOffline(final HRegionInfo hri) {
+  public void regionOffline(final HRegionInfo hri) {
+    regionOffline(hri, null);
+  }
+
+  /**
+   * A region is offline, won't be in transition any more.
+   * Its state should be the specified expected state, which
+   * can be Split/Merged/Offline/null(=Offline) only.
+   */
+  public synchronized void regionOffline(
+      final HRegionInfo hri, final State expectedState) {
+    Preconditions.checkArgument(expectedState == null
+      || expectedState == State.OFFLINE || expectedState == State.SPLIT
+      || expectedState == State.MERGED, "Offlined region should be in state"
+        + " OFFLINE/SPLIT/MERGED instead of " + expectedState);
     String regionName = hri.getEncodedName();
     RegionState oldState = regionStates.get(regionName);
     if (oldState == null) {
       LOG.warn("Offline a region not in RegionStates: " + hri.getShortNameToLog());
-    } else {
+    } else if (LOG.isDebugEnabled()) {
       State state = oldState.getState();
       ServerName sn = oldState.getServerName();
-      if (state != State.OFFLINE || sn != null) {
-        LOG.debug("Offline a region " + hri.getShortNameToLog() + " with current state=" + state +
-          ", expected state=OFFLINE" + ", assigned to server: " + sn + ", expected null");
+      if (state != State.OFFLINE
+          && state != State.SPLITTING && state != State.MERGING) {
+        LOG.debug("Offline a region " + hri.getShortNameToLog() + " with current state="
+          + state + ", expected state=OFFLINE/SPLITTING/MERGING");
+      }
+      if (sn != null && state == State.OFFLINE) {
+        LOG.debug("Offline a region " + hri.getShortNameToLog()
+          + " with current state=OFFLINE, assigned to server: "
+          + sn + ", expected null");
       }
     }
-    updateRegionState(hri, State.OFFLINE);
+    State newState = expectedState;
+    if (newState == null) newState = State.OFFLINE;
+    updateRegionState(hri, newState);
     regionsInTransition.remove(regionName);
 
     ServerName oldServerName = regionAssignments.remove(hri);

Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DeleteTableHandler.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DeleteTableHandler.java?rev=1508518&r1=1508517&r2=1508518&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DeleteTableHandler.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DeleteTableHandler.java Tue Jul 30 16:53:35 2013
@@ -37,6 +37,7 @@ import org.apache.hadoop.hbase.master.Ma
 import org.apache.hadoop.hbase.master.MasterFileSystem;
 import org.apache.hadoop.hbase.master.MasterServices;
 import org.apache.hadoop.hbase.master.RegionStates;
+import org.apache.hadoop.hbase.master.RegionState.State;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.Threads;
 import org.apache.zookeeper.KeeperException;
@@ -73,7 +74,7 @@ public class DeleteTableHandler extends 
     for (HRegionInfo region : regions) {
       long done = System.currentTimeMillis() + waitTime;
       while (System.currentTimeMillis() < done) {
-        if (states.isRegionFailedToOpen(region)) {
+        if (states.isRegionInState(region, State.FAILED_OPEN)) {
           am.regionOffline(region);
         }
         if (!states.isRegionInTransition(region)) break;

Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DisableTableHandler.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DisableTableHandler.java?rev=1508518&r1=1508517&r2=1508518&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DisableTableHandler.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DisableTableHandler.java Tue Jul 30 16:53:35 2013
@@ -39,6 +39,7 @@ import org.apache.hadoop.hbase.master.HM
 import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
 import org.apache.hadoop.hbase.master.RegionStates;
 import org.apache.hadoop.hbase.master.TableLockManager;
+import org.apache.hadoop.hbase.master.RegionState.State;
 import org.apache.hadoop.hbase.master.TableLockManager.TableLock;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.zookeeper.KeeperException;
@@ -203,7 +204,9 @@ public class DisableTableHandler extends
       RegionStates regionStates = assignmentManager.getRegionStates();
       for (HRegionInfo region: regions) {
         if (regionStates.isRegionInTransition(region)
-            && !regionStates.isRegionFailedToClose(region)) continue;
+            && !regionStates.isRegionInState(region, State.FAILED_CLOSE)) {
+          continue;
+        }
         final HRegionInfo hri = region;
         pool.execute(Trace.wrap(new Runnable() {
           public void run() {

Modified: hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java?rev=1508518&r1=1508517&r2=1508518&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java (original)
+++ hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java Tue Jul 30 16:53:35 2013
@@ -380,10 +380,12 @@ public class TestAssignmentManager {
       RegionPlan plan = new RegionPlan(REGIONINFO, SERVERNAME_A, SERVERNAME_B);
       am.balance(plan);
 
+      RegionStates regionStates = am.getRegionStates();
       // Must be failed to close since the server is fake
-      assertTrue(am.getRegionStates().isRegionFailedToClose(REGIONINFO));
+      assertTrue(regionStates.isRegionInTransition(REGIONINFO)
+        && regionStates.isRegionInState(REGIONINFO, State.FAILED_CLOSE));
       // Move it back to pending_close
-      am.getRegionStates().updateRegionState(REGIONINFO, State.PENDING_CLOSE);
+      regionStates.updateRegionState(REGIONINFO, State.PENDING_CLOSE);
 
       // Now fake the region closing successfully over on the regionserver; the
       // regionserver will have set the region in CLOSED state.  This will
@@ -413,7 +415,7 @@ public class TestAssignmentManager {
         ZKAssign.transitionNodeOpened(this.watcher, REGIONINFO, SERVERNAME_B, versionid);
       assertNotSame(-1, versionid);
       // Wait on the handler removing the OPENED znode.
-      while(am.getRegionStates().isRegionInTransition(REGIONINFO)) Threads.sleep(1);
+      while(regionStates.isRegionInTransition(REGIONINFO)) Threads.sleep(1);
     } finally {
       executor.shutdown();
       am.shutdown();

Modified: hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionMergeTransactionOnCluster.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionMergeTransactionOnCluster.java?rev=1508518&r1=1508517&r2=1508518&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionMergeTransactionOnCluster.java (original)
+++ hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionMergeTransactionOnCluster.java Tue Jul 30 16:53:35 2013
@@ -26,6 +26,7 @@ import static org.junit.Assert.fail;
 import java.io.IOException;
 import java.util.List;
 
+import org.apache.commons.lang.math.RandomUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.fs.FileSystem;
@@ -37,6 +38,7 @@ import org.apache.hadoop.hbase.HTableDes
 import org.apache.hadoop.hbase.LargeTests;
 import org.apache.hadoop.hbase.MiniHBaseCluster;
 import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.UnknownRegionException;
 import org.apache.hadoop.hbase.catalog.MetaReader;
 import org.apache.hadoop.hbase.client.HBaseAdmin;
 import org.apache.hadoop.hbase.client.HTable;
@@ -45,11 +47,14 @@ import org.apache.hadoop.hbase.client.Re
 import org.apache.hadoop.hbase.client.ResultScanner;
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.exceptions.MergeRegionException;
-import org.apache.hadoop.hbase.UnknownRegionException;
+import org.apache.hadoop.hbase.master.AssignmentManager;
 import org.apache.hadoop.hbase.master.HMaster;
+import org.apache.hadoop.hbase.master.RegionState.State;
 import org.apache.hadoop.hbase.master.RegionStates;
 import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
 import org.apache.hadoop.hbase.util.Pair;
+import org.apache.hadoop.hbase.util.PairOfSameType;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -98,7 +103,7 @@ public class TestRegionMergeTransactionO
   public static void afterAllTests() throws Exception {
     TEST_UTIL.shutdownMiniCluster();
   }
-  
+
   @Test
   public void testWholesomeMerge() throws Exception {
     LOG.info("Starting testWholesomeMerge");
@@ -111,11 +116,31 @@ public class TestRegionMergeTransactionO
         INITIAL_REGION_NUM - 1);
 
     // Merge 2nd and 3th region
-    mergeRegionsAndVerifyRegionNum(master, tableName, 1, 2,
+    PairOfSameType<HRegionInfo> mergedRegions =
+      mergeRegionsAndVerifyRegionNum(master, tableName, 1, 2,
         INITIAL_REGION_NUM - 2);
 
     verifyRowCount(table, ROWSIZE);
 
+    // Randomly choose one of the two merged regions
+    HRegionInfo hri = RandomUtils.nextBoolean() ?
+      mergedRegions.getFirst() : mergedRegions.getSecond();
+    MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
+    AssignmentManager am = cluster.getMaster().getAssignmentManager();
+    RegionStates regionStates = am.getRegionStates();
+    long start = EnvironmentEdgeManager.currentTimeMillis();
+    while (!regionStates.isRegionInState(hri, State.MERGED)) {
+      assertFalse("Timed out in waiting one merged region to be in state MERGED",
+        EnvironmentEdgeManager.currentTimeMillis() - start > 60000);
+      Thread.sleep(500);
+    }
+
+    // We should not be able to assign it again
+    am.assign(hri, true, true);
+    assertFalse("Merged region should not be in transition again",
+      regionStates.isRegionInTransition(hri)
+        && regionStates.isRegionInState(hri, State.MERGED));
+
     table.close();
   }
 
@@ -246,20 +271,27 @@ public class TestRegionMergeTransactionO
     }
   }
 
-  private void mergeRegionsAndVerifyRegionNum(HMaster master, byte[] tablename,
+  private PairOfSameType<HRegionInfo> mergeRegionsAndVerifyRegionNum(
+      HMaster master, byte[] tablename,
       int regionAnum, int regionBnum, int expectedRegionNum) throws Exception {
-    requestMergeRegion(master, tablename, regionAnum, regionBnum);
+    PairOfSameType<HRegionInfo> mergedRegions =
+      requestMergeRegion(master, tablename, regionAnum, regionBnum);
     waitAndVerifyRegionNum(master, tablename, expectedRegionNum);
+    return mergedRegions;
   }
 
-  private void requestMergeRegion(HMaster master, byte[] tablename,
+  private PairOfSameType<HRegionInfo> requestMergeRegion(
+      HMaster master, byte[] tablename,
       int regionAnum, int regionBnum) throws Exception {
     List<Pair<HRegionInfo, ServerName>> tableRegions = MetaReader
         .getTableRegionsAndLocations(master.getCatalogTracker(),
             Bytes.toString(tablename));
+    HRegionInfo regionA = tableRegions.get(regionAnum).getFirst();
+    HRegionInfo regionB = tableRegions.get(regionBnum).getFirst();
     TEST_UTIL.getHBaseAdmin().mergeRegions(
-        tableRegions.get(regionAnum).getFirst().getEncodedNameAsBytes(),
-        tableRegions.get(regionBnum).getFirst().getEncodedNameAsBytes(), false);
+      regionA.getEncodedNameAsBytes(),
+      regionB.getEncodedNameAsBytes(), false);
+    return new PairOfSameType<HRegionInfo>(regionA, regionB);
   }
 
   private void waitAndVerifyRegionNum(HMaster master, byte[] tablename,

Modified: hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java?rev=1508518&r1=1508517&r2=1508518&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java (original)
+++ hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java Tue Jul 30 16:53:35 2013
@@ -34,6 +34,9 @@ import java.util.concurrent.CountDownLat
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.Abortable;
 import org.apache.hadoop.hbase.HBaseIOException;
 import org.apache.hadoop.hbase.HBaseTestingUtility;
 import org.apache.hadoop.hbase.HColumnDescriptor;
@@ -58,12 +61,17 @@ import org.apache.hadoop.hbase.client.Re
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.exceptions.DeserializationException;
 import org.apache.hadoop.hbase.executor.EventType;
+import org.apache.hadoop.hbase.master.AssignmentManager;
 import org.apache.hadoop.hbase.master.HMaster;
 import org.apache.hadoop.hbase.master.RegionState;
+import org.apache.hadoop.hbase.master.RegionState.State;
 import org.apache.hadoop.hbase.master.RegionStates;
 import org.apache.hadoop.hbase.master.handler.SplitRegionHandler;
 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
 import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
+import org.apache.hadoop.hbase.util.FSUtils;
+import org.apache.hadoop.hbase.util.HBaseFsck;
 import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
 import org.apache.hadoop.hbase.util.Threads;
 import org.apache.hadoop.hbase.zookeeper.ZKAssign;
@@ -72,10 +80,9 @@ import org.apache.hadoop.hbase.zookeeper
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.KeeperException.NodeExistsException;
 import org.apache.zookeeper.data.Stat;
-import org.apache.hadoop.hbase.Abortable;
+import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Assert;
-import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -746,6 +753,59 @@ public class TestSplitTransactionOnClust
     admin.flush(tableName);
   }
 
+  /**
+   * After a region is split, it should not be able to assign again
+   */
+  @Test
+  public void testSplitRegionNotAssignable() throws Exception {
+    final byte[] tableName = Bytes.toBytes("testSplitRegionNotAssignable");
+    // Create table then get the single region for our new table.
+    HTable t = createTableAndWait(tableName, Bytes.toBytes("cf"));
+    try {
+      List<HRegion> regions = cluster.getRegions(tableName);
+      int regionServerIndex = cluster.getServerWith(regions.get(0).getRegionName());
+      HRegionServer regionServer = cluster.getRegionServer(regionServerIndex);
+      insertData(tableName, admin, t);
+      // Turn off balancer so it doesn't cut in and mess up our placements.
+      admin.setBalancerRunning(false, true);
+      // Turn off the meta scanner so it don't remove parent on us.
+      cluster.getMaster().setCatalogJanitorEnabled(false);
+      final HRegion region = findSplittableRegion(regions);
+      assertTrue("not able to find a splittable region", region != null);
+
+      // Now split.
+      SplitTransaction st = new MockedSplitTransaction(region, Bytes.toBytes("row2"));
+      try {
+        st.prepare();
+        st.execute(regionServer, regionServer);
+      } catch (IOException e) {
+        fail("Split execution should have succeeded with no exceptions thrown");
+      }
+
+      List<HRegion> daughters = cluster.getRegions(tableName);
+      assertTrue(daughters.size() == regions.size() + 1);
+
+      HRegionInfo hri = region.getRegionInfo(); // split parent
+      AssignmentManager am = cluster.getMaster().getAssignmentManager();
+      RegionStates regionStates = am.getRegionStates();
+      long start = EnvironmentEdgeManager.currentTimeMillis();
+      while (!regionStates.isRegionInState(hri, State.SPLIT)) {
+        assertFalse("Timed out in waiting split parent to be in state SPLIT",
+          EnvironmentEdgeManager.currentTimeMillis() - start > 60000);
+        Thread.sleep(500);
+      }
+
+      // We should not be able to assign it again
+      am.assign(hri, true, true);
+      assertFalse("Split region should not be in transition again",
+        regionStates.isRegionInTransition(hri)
+          && regionStates.isRegionInState(hri, State.SPLIT));
+    } finally {
+      admin.setBalancerRunning(true, false);
+      cluster.getMaster().setCatalogJanitorEnabled(true);
+    }
+  }
+
   private void testSplitBeforeSettingSplittingInZKInternals() throws Exception {
     final byte[] tableName = Bytes.toBytes("testSplitBeforeSettingSplittingInZK");
     try {