diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/MetaTableAccessor.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/MetaTableAccessor.java index 3d40c70..cc9453c 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/MetaTableAccessor.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/MetaTableAccessor.java @@ -1655,7 +1655,8 @@ public class MetaTableAccessor { */ public static void splitRegion(final Connection connection, HRegionInfo parent, HRegionInfo splitA, HRegionInfo splitB, - ServerName sn, int regionReplication) throws IOException { + ServerName snp, ServerName sna, ServerName snb, + int regionReplication) throws IOException { Table meta = getMetaHTable(connection); try { HRegionInfo copyOfParent = new HRegionInfo(parent); @@ -1670,8 +1671,12 @@ public class MetaTableAccessor { Put putA = makePutFromRegionInfo(splitA); Put putB = makePutFromRegionInfo(splitB); - addLocation(putA, sn, 1, -1, splitA.getReplicaId()); //new regions, openSeqNum = 1 is fine. - addLocation(putB, sn, 1, -1, splitB.getReplicaId()); + if (sna != null) { + addLocation(putA, sna, 1, -1, splitA.getReplicaId()); //new regions, openSeqNum = 1 is fine. + } + if (snb != null) { + addLocation(putB, snb, 1, -1, splitB.getReplicaId()); + } // Add empty locations for region replicas of daughters so that number of replicas can be // cached whenever the primary region is looked up from meta diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java index 1b71cb4..2a0a138 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java @@ -1260,6 +1260,9 @@ public final class HConstants { public static final String ZK_SERVER_KERBEROS_PRINCIPAL = "hbase.zookeeper.server.kerberos.principal"; + public static final String ASSIGN_DAUGHTERS_AFTER_SPLIT = + "hbase.split.unassign"; + private HConstants() { // Can't be instantiated with this ctor. } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java index b455828..dfaaf20 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java @@ -202,6 +202,8 @@ public class AssignmentManager { private RegionStateListener regionStateListener; + private boolean shouldAssignAfterSplit; + /** * Constructs a new assignment manager. * @@ -254,6 +256,8 @@ public class AssignmentManager { this.metricsAssignmentManager = new MetricsAssignmentManager(); this.tableLockManager = tableLockManager; + this.shouldAssignAfterSplit = + conf.getBoolean(HConstants.ASSIGN_DAUGHTERS_AFTER_SPLIT, false); } /** @@ -2368,16 +2372,47 @@ public class AssignmentManager { } try { - regionStates.splitRegion(hri, a, b, serverName); + if (!shouldAssignAfterSplit) { + regionStates.splitRegion(hri, a, b, serverName, serverName, serverName); + } else { + regionStates.splitRegion(hri, a, b, serverName, null, null); + } } catch (IOException ioe) { LOG.info("Failed to record split region " + hri.getShortNameToLog()); return "Failed to record the splitting in meta"; } + + if (shouldAssignAfterSplit) { + regionOffline(hri, State.SPLIT); + try { + regionStates.prepareAssignDaughters(a, b); + } catch (IOException ex) { + LOG.info("Failed to prepare daughters for assignment: " + + "a=" + rs_a + ", b=" + rs_b, ex); + return "Failed to prepare daughters for assignment"; + } + + invokeAssign(a); + invokeAssign(b); + + Callable splitReplicasCallable = new Callable() { + @Override + public Object call() { + doSplittingOfReplicas(hri, a, b); + return null; + } + }; + threadPoolExecutorService.submit(splitReplicasCallable); + } return null; } private String onRegionSplit(final RegionState current, final HRegionInfo hri, final ServerName serverName, final RegionStateTransition transition) { + if (shouldAssignAfterSplit) { + return "Split transition should not be entered when split assign is enabled."; + } + // The region must be splitting on this server, and the daughters must be in // splitting_new state. // If current state is already split on the same server, diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStateStore.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStateStore.java index bc5173a..2ec85c5 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStateStore.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStateStore.java @@ -239,9 +239,11 @@ public class RegionStateStore { } } - void splitRegion(HRegionInfo p, - HRegionInfo a, HRegionInfo b, ServerName sn, int regionReplication) throws IOException { - MetaTableAccessor.splitRegion(server.getConnection(), p, a, b, sn, regionReplication); + void splitRegion(HRegionInfo p, HRegionInfo a, HRegionInfo b, + ServerName snp, ServerName sna, ServerName snb, + int regionReplication) throws IOException { + MetaTableAccessor.splitRegion(server.getConnection(), + p, a, b, snp, sna, snb, regionReplication); } void mergeRegions(HRegionInfo p, diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java index 3743616..e458abb 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java @@ -20,6 +20,7 @@ package org.apache.hadoop.hbase.master; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -27,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; @@ -43,6 +45,7 @@ import org.apache.hadoop.hbase.ServerLoad; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.RegionReplicaUtil; +import org.apache.hadoop.hbase.constraint.ConstraintException; import org.apache.hadoop.hbase.master.RegionState.State; import org.apache.hadoop.hbase.client.TableState; import org.apache.hadoop.hbase.util.Bytes; @@ -845,20 +848,44 @@ public class RegionStates { return regions == null ? false : regions.contains(hri); } - void splitRegion(HRegionInfo p, - HRegionInfo a, HRegionInfo b, ServerName sn) throws IOException { + public void prepareAssignDaughters(HRegionInfo a, HRegionInfo b) throws IOException { + synchronized (this) { + if (!(isRegionInState(a, State.SPLITTING_NEW) || isRegionInState(b, State.SPLITTING_NEW))) { + throw new ConstraintException("Neither regions are in SPLITTING_NEW state"); + } + updateRegionState(a, State.OFFLINE, null); + updateRegionState(b, State.OFFLINE, null); + } + } + + void splitRegion(HRegionInfo p, HRegionInfo a, HRegionInfo b, + ServerName snp, ServerName sna, ServerName snb) throws IOException { - regionStateStore.splitRegion(p, a, b, sn, getRegionReplication(p)); + regionStateStore.splitRegion(p, a, b, snp, sna, snb, getRegionReplication(p)); synchronized (this) { // After PONR, split is considered to be done. // Update server holdings to be aligned with the meta. - Set regions = serverHoldings.get(sn); + Set regions = serverHoldings.get(snp); if (regions == null) { - throw new IllegalStateException(sn + " should host some regions"); + throw new IllegalStateException(snp + " should host some regions"); } regions.remove(p); - regions.add(a); - regions.add(b); + + if (sna != null) { + regions = serverHoldings.get(sna); + if (regions == null) { + regions = Collections.newSetFromMap(new ConcurrentHashMap()); + } + regions.add(a); + } + + if (snb != null) { + regions = serverHoldings.get(snb); + if (regions == null) { + regions = Collections.newSetFromMap(new ConcurrentHashMap()); + } + regions.add(b); + } } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransactionImpl.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransactionImpl.java index 70d040e..659cdcc 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransactionImpl.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/SplitTransactionImpl.java @@ -529,7 +529,10 @@ public class SplitTransactionImpl implements SplitTransaction { } } - openDaughters(server, services, regions.getFirst(), regions.getSecond()); + if (services == null || + !services.getConfiguration().getBoolean(HConstants.ASSIGN_DAUGHTERS_AFTER_SPLIT, false)) { + openDaughters(server, services, regions.getFirst(), regions.getSecond()); + } transition(SplitTransactionPhase.BEFORE_POST_SPLIT_HOOK); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java index c2d273b..f36fba3 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java @@ -664,7 +664,7 @@ public class MiniHBaseCluster extends HBaseCluster { public List getRegions(TableName tableName) { List ret = new ArrayList(); - for (JVMClusterUtil.RegionServerThread rst : getRegionServerThreads()) { + for (JVMClusterUtil.RegionServerThread rst : getLiveRegionServerThreads()) { HRegionServer hrs = rst.getRegionServer(); for (Region region : hrs.getOnlineRegionsLocalContext()) { if (region.getTableDesc().getTableName().equals(tableName)) { diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/TestMetaTableAccessor.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/TestMetaTableAccessor.java index 7e934c0..3bcffb1 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/TestMetaTableAccessor.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/TestMetaTableAccessor.java @@ -445,7 +445,8 @@ public class TestMetaTableAccessor { List regionInfos = Lists.newArrayList(parent); MetaTableAccessor.addRegionsToMeta(connection, regionInfos, 3); - MetaTableAccessor.splitRegion(connection, parent, splitA, splitB, serverName0, 3); + MetaTableAccessor.splitRegion(connection, parent, splitA, splitB, + serverName0, serverName0, serverName0, 3); assertEmptyMetaLocation(meta, splitA.getRegionName(), 1); assertEmptyMetaLocation(meta, splitA.getRegionName(), 2); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitAssignTransactionOnCluster.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitAssignTransactionOnCluster.java new file mode 100644 index 0000000..1b0994a --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitAssignTransactionOnCluster.java @@ -0,0 +1,34 @@ +package org.apache.hadoop.hbase.regionserver; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.HConstants; +import org.junit.BeforeClass; +import org.junit.Test; + + +public class TestSplitAssignTransactionOnCluster extends TestSplitTransactionOnCluster { + private static final Log LOG = + LogFactory.getLog(TestSplitAssignTransactionOnCluster.class); + + @BeforeClass + public static void before() throws Exception { + TESTING_UTIL.getConfiguration().setBoolean( + HConstants.ASSIGN_DAUGHTERS_AFTER_SPLIT, true); + try { + String className = "org.apache.hadoop.hbase.regionserver.TestZKLessSplitOnCluster.KillRS"; + Class.forName(className); + TESTING_UTIL.getConfiguration().set( + RegionServerCoprocessorHost.REGION_COPROCESSOR_CONF_KEY, + className); + } catch(ClassNotFoundException ex) { + } + setupOnce(); + } + + @Test(timeout = 300000) + @Override + public void testRSSplitDaughtersAreOnlinedAfterShutdownHandling(){ + //Parent class test not applicable + } +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java index 41fbae6..7fb868c 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java @@ -47,6 +47,7 @@ import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.HRegionLocation; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.MasterNotRunningException; import org.apache.hadoop.hbase.MetaTableAccessor; @@ -65,6 +66,7 @@ import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.Mutation; import org.apache.hadoop.hbase.client.Put; +import org.apache.hadoop.hbase.client.RegionLocator; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; @@ -129,6 +131,10 @@ public class TestSplitTransactionOnCluster { new HBaseTestingUtility(); @BeforeClass public static void before() throws Exception { + setupOnce(); + } + + public static void setupOnce() throws Exception { TESTING_UTIL.getConfiguration().setInt("hbase.balancer.period", 60000); TESTING_UTIL.startMiniCluster(1, NB_SERVERS, null, MyMaster.class, null); } @@ -266,6 +272,9 @@ public class TestSplitTransactionOnCluster { SplitTransactionImpl st = new SplitTransactionImpl(region, Bytes.toBytes("row3")); assertTrue(st.prepare()); st.execute(regionServer, regionServer); + //verify regions are online + for (Result r : t.getScanner(new Scan())) { + } assertEquals(2, cluster.getRegions(tableName).size()); } @@ -659,12 +668,19 @@ public class TestSplitTransactionOnCluster { assertEquals("The specified table should present.", true, tableExists); Map rit = cluster.getMaster().getAssignmentManager().getRegionStates() .getRegionsInTransition(); - assertTrue(rit.size() == 3); + if (TESTING_UTIL.getConfiguration().getBoolean( + HConstants.ASSIGN_DAUGHTERS_AFTER_SPLIT, false)) { + for (Result r : t.getScanner(new Scan())) { + } + assertEquals(2, rit.size()); + } else { + assertEquals(3, rit.size()); + } cluster.getMaster().getAssignmentManager().regionOffline(st.getFirstDaughter()); cluster.getMaster().getAssignmentManager().regionOffline(st.getSecondDaughter()); cluster.getMaster().getAssignmentManager().regionOffline(region.getRegionInfo()); rit = cluster.getMaster().getAssignmentManager().getRegionStates().getRegionsInTransition(); - assertTrue(rit.size() == 0); + assertEquals(0, rit.size()); } finally { admin.setBalancerRunning(true, false); cluster.getMaster().setCatalogJanitorEnabled(true); @@ -776,7 +792,7 @@ public class TestSplitTransactionOnCluster { final TableName tableName = TableName.valueOf("testSplitRegionWithNoStoreFiles"); // Create table then get the single region for our new table. - createTableAndWait(tableName, HConstants.CATALOG_FAMILY); + Table table = createTableAndWait(tableName, HConstants.CATALOG_FAMILY); List regions = cluster.getRegions(tableName); HRegionInfo hri = getAndCheckSingleTableRegion(regions); ensureTableRegionNotOnSameServerAsMeta(admin, hri); @@ -812,10 +828,13 @@ public class TestSplitTransactionOnCluster { fail("Split execution should have succeeded with no exceptions thrown"); } + //verify regions are online + for (Result r : table.getScanner(new Scan())) { + } // Postcondition: split the table with no store files into two regions, but still have not // store files List daughters = cluster.getRegions(tableName); - assertTrue(daughters.size() == 2); + assertEquals(2, daughters.size()); // check dirs HBaseFsck.debugLsr(conf, new Path("/")); @@ -860,7 +879,7 @@ public class TestSplitTransactionOnCluster { LOG.info("Starting testSplitAndRestartingMaster"); final TableName tableName = TableName.valueOf("testSplitAndRestartingMaster"); // Create table then get the single region for our new table. - createTableAndWait(tableName, HConstants.CATALOG_FAMILY); + Table table = createTableAndWait(tableName, HConstants.CATALOG_FAMILY); List regions = cluster.getRegions(tableName); HRegionInfo hri = getAndCheckSingleTableRegion(regions); ensureTableRegionNotOnSameServerAsMeta(admin, hri); @@ -888,9 +907,11 @@ public class TestSplitTransactionOnCluster { } // Postcondition + for (Result r : table.getScanner(new Scan())) { + } List daughters = cluster.getRegions(tableName); LOG.info("xxx " + regions.size() + AssignmentManager.TEST_SKIP_SPLIT_HANDLING); - assertTrue(daughters.size() == 2); + assertEquals(2, daughters.size()); } finally { MyMasterRpcServices.enabled.set(false); admin.setBalancerRunning(true, false); @@ -930,7 +951,7 @@ public class TestSplitTransactionOnCluster { Table table2 = null; try { table1 = TESTING_UTIL.getConnection().getTable(firstTable); - table2 = TESTING_UTIL.getConnection().getTable(firstTable); + table2 = TESTING_UTIL.getConnection().getTable(secondTable); insertData(firstTable, admin, table1); insertData(secondTable, admin, table2); admin.split(firstTable, "row2".getBytes()); @@ -1143,14 +1164,30 @@ public class TestSplitTransactionOnCluster { private void split(final HRegionInfo hri, final HRegionServer server, final int regionCount) throws IOException, InterruptedException { this.admin.splitRegion(hri.getRegionName()); - for (int i = 0; ProtobufUtil.getOnlineRegions( - server.getRSRpcServices()).size() <= regionCount && i < 300; i++) { - LOG.debug("Waiting on region to split"); - Thread.sleep(100); - } - assertFalse("Waited too long for split", - ProtobufUtil.getOnlineRegions(server.getRSRpcServices()).size() <= regionCount); + if (!TESTING_UTIL.getConfiguration().getBoolean( + HConstants.ASSIGN_DAUGHTERS_AFTER_SPLIT, false)) { + for (int i = 0; ProtobufUtil.getOnlineRegions( + server.getRSRpcServices()).size() <= regionCount && i < 300; i++) { + LOG.debug("Waiting on region to split"); + Thread.sleep(100); + } + assertFalse("Waited too long for split", + ProtobufUtil.getOnlineRegions(server.getRSRpcServices()).size() <= regionCount); + } else { + RegionLocator locator = admin.getConnection().getRegionLocator(hri.getTable()); + boolean found = false; + for (int i = 0; i < 300; i++) { + HRegionLocation location = locator.getRegionLocation(hri.getStartKey(), true); + if (location.getRegionInfo().getEncodedName().equals(hri.getEncodedName())) { + found = true; + break; + } + LOG.debug("Waiting on region to split"); + Thread.sleep(100); + } + assertTrue(found); + } } /** @@ -1325,6 +1362,7 @@ public class TestSplitTransactionOnCluster { public static class MockedRegionObserver extends BaseRegionObserver { private SplitTransactionImpl st = null; private PairOfSameType daughterRegions = null; + private Region parentRegion = null; @Override public void preSplitBeforePONR(ObserverContext ctx, @@ -1340,6 +1378,7 @@ public class TestSplitTransactionOnCluster { break; } } + parentRegion = region; st = new SplitTransactionImpl((HRegion) region, splitKey); if (!st.prepare()) { LOG.error("Prepare for the table " + region.getTableDesc().getNameAsString() @@ -1373,6 +1412,21 @@ public class TestSplitTransactionOnCluster { throws IOException { RegionCoprocessorEnvironment environment = ctx.getEnvironment(); HRegionServer rs = (HRegionServer) environment.getRegionServerServices(); + if (TESTING_UTIL.getConfiguration().getBoolean(HConstants.ASSIGN_DAUGHTERS_AFTER_SPLIT, + false)) { + AssignmentManager am = TESTING_UTIL.getHBaseCluster().getMaster().getAssignmentManager(); + am.getRegionStates().regionOffline(parentRegion.getRegionInfo(), RegionState.State.SPLIT); + am.getRegionStates().prepareAssignDaughters( + daughterRegions.getFirst().getRegionInfo(), + daughterRegions.getSecond().getRegionInfo()); + am.assign(daughterRegions.getFirst().getRegionInfo()); + am.assign(daughterRegions.getSecond().getRegionInfo()); + try { + TESTING_UTIL.waitUntilNoRegionsInTransition(60000); + } catch (Exception e) { + throw new IllegalStateException("", e); + } + } st.stepsAfterPONR(rs, rs, daughterRegions, null); } }