Index: hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/ServerShutdownHandler.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/ServerShutdownHandler.java (revision 1354388) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/ServerShutdownHandler.java (working copy) @@ -439,7 +439,8 @@ if (daughter == null) return 0; if (isDaughterMissing(catalogTracker, daughter)) { LOG.info("Fixup; missing daughter " + daughter.getRegionNameAsString()); - MetaEditor.addDaughter(catalogTracker, daughter, null); + ServerName parentServerName = MetaReader.getServerNameFromCatalogResult(result); + MetaEditor.addDaughter(catalogTracker, daughter, parentServerName); // TODO: Log WARN if the regiondir does not exist in the fs. If its not // there then something wonky about the split -- things will keep going Index: hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java =================================================================== --- hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java (revision 1354388) +++ hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestSplitTransactionOnCluster.java (working copy) @@ -26,20 +26,37 @@ import static org.junit.Assert.assertFalse; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.NavigableMap; +import java.util.Set; +import java.util.Map.Entry; 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.catalog.CatalogTracker; +import org.apache.hadoop.hbase.catalog.MetaReader; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.HBaseAdmin; import org.apache.hadoop.hbase.client.HTable; +import org.apache.hadoop.hbase.client.Put; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.executor.ExecutorService; import org.apache.hadoop.hbase.executor.EventHandler.EventType; +import org.apache.hadoop.hbase.master.AssignmentManager; import org.apache.hadoop.hbase.master.HMaster; +import org.apache.hadoop.hbase.master.LoadBalancer; +import org.apache.hadoop.hbase.master.MasterServices; +import org.apache.hadoop.hbase.master.ServerManager; +import org.apache.hadoop.hbase.master.balancer.DefaultLoadBalancer; +import org.apache.hadoop.hbase.master.handler.ServerShutdownHandler; 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.Writables; import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; import org.apache.hadoop.hbase.util.Threads; import org.apache.hadoop.hbase.zookeeper.ZKAssign; @@ -68,6 +85,19 @@ private HBaseAdmin admin = null; private MiniHBaseCluster cluster = null; private static final int NB_SERVERS = 2; + + private final static byte[][] SPLITS = new byte[][] { Bytes.toBytes("A"), + Bytes.toBytes("B"), Bytes.toBytes("C") }; + // one row per region. + private final static byte[][] ROWKEYS= new byte[][] { + Bytes.toBytes("00"), Bytes.toBytes("50"), Bytes.toBytes("A0"), Bytes.toBytes("A5"), + Bytes.toBytes("B0"), Bytes.toBytes("B5"), Bytes.toBytes("C0"), Bytes.toBytes("C5") }; + + private final static byte[] FAM = Bytes.toBytes("fam"); + + private HTable tbl; + + public static int count = 0; private static final HBaseTestingUtility TESTING_UTIL = new HBaseTestingUtility(); @@ -177,6 +207,143 @@ } } + @Test(timeout = 30000) + public void testShouldNotAssignDaughterRegionTwice() throws Exception { + String table = "testShouldNotAssignDaughterRegionTwice"; + Configuration conf = TESTING_UTIL.getConfiguration(); + setupTable(table); + + // make sure data in regions, if in hlog only there is no data loss + TESTING_UTIL.getHBaseAdmin().flush(table); + HRegionLocation location = tbl.getRegionLocation("B"); + + // Create a new meta entry to fake it as a split parent. + HTable meta = new HTable(conf, HTableDescriptor.META_TABLEDESC.getName()); + HRegionInfo hri = location.getRegionInfo(); + + HRegionInfo a = new HRegionInfo(tbl.getTableName(), Bytes.toBytes("B"), Bytes.toBytes("BM")); + HRegionInfo b = new HRegionInfo(tbl.getTableName(), Bytes.toBytes("BM"), Bytes.toBytes("C")); + Put p = new Put(hri.getRegionName()); + hri.setOffline(true); + hri.setSplit(true); + p.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER, Writables.getBytes(hri)); + p.add(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER, Writables.getBytes(a)); + p.add(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER, Writables.getBytes(b)); + meta.put(p); + meta.flushCommits(); + TESTING_UTIL.getHBaseAdmin().flush(HConstants.META_TABLE_NAME); + Collection servers = TESTING_UTIL.getHBaseAdmin().getClusterStatus().getServers(); + ServerName name = null; + for (ServerName serverName : servers) { + if (location.getHostnamePort().equals(serverName.getHostAndPort())) { + name = serverName; + break; + } + } + + boolean flag = false; + MockedServerManager serverManager = new MockedServerManager(TESTING_UTIL.getHBaseCluster() + .getMaster(), TESTING_UTIL.getHBaseCluster().getMaster(), flag); + + AssignmentManagerWithExtrasForTesting am = new AssignmentManagerWithExtrasForTesting( + TESTING_UTIL.getHBaseCluster().getMaster(), serverManager, TESTING_UTIL.getHBaseCluster() + .getMaster().getCatalogTracker(), new DefaultLoadBalancer(), TESTING_UTIL.getHBaseCluster().getMaster() + .getExecutorService()); + NavigableMap serverUserRegions = MetaReader.getServerUserRegions( + TESTING_UTIL.getHBaseCluster().getMaster().getCatalogTracker(), name); + Set> entrySet = serverUserRegions.entrySet(); + for (Entry entry : entrySet) { + if (entry.getKey().equals(hri)) { + + ServerShutdownHandler.fixupDaughters(entry.getValue(), am, TESTING_UTIL.getHBaseCluster() + .getMaster().getCatalogTracker()); + + while (count <= 1) { + Thread.sleep(5); + } + + ServerShutdownHandler.fixupDaughters(entry.getValue(), am, TESTING_UTIL.getHBaseCluster() + .getMaster().getCatalogTracker()); + + assertEquals( + "The assignment of daughter should happen only once each for Daughter A and Daughter B", + 2, count); + + } + } + } + + class AssignmentManagerWithExtrasForTesting extends AssignmentManager { + private final ExecutorService es; + private final CatalogTracker ct; + + public AssignmentManagerWithExtrasForTesting(final Server master, + final ServerManager serverManager, final CatalogTracker catalogTracker, + final LoadBalancer balancer, final ExecutorService service) throws KeeperException, + IOException { + super(master, serverManager, catalogTracker, balancer, service, null); + this.es = service; + this.ct = catalogTracker; + } + + @Override + public void assign(HRegionInfo region, boolean setOfflineInZK) { + super.assign(region, setOfflineInZK); + } + + @Override + public void assign(HRegionInfo region, boolean setOfflineInZK, boolean forceNewPlan, + boolean hijack) { + count++; + // TODO Auto-generated method stub + super.assign(region, setOfflineInZK, forceNewPlan, hijack); + } + + } + + class MockedServerManager extends ServerManager { + + boolean flag = false; + + public MockedServerManager(Server master, MasterServices services, boolean flag) + throws ZooKeeperConnectionException { + super(master, services); + this.flag = flag; + } + + @Override + public RegionOpeningState sendRegionOpen(ServerName server, HRegionInfo region, + int versionOfOfflineNode) throws IOException { + return RegionOpeningState.OPENED; + } + + } + + /** + * Setup a clean table before we start mucking with it. + * + * @throws IOException + * @throws InterruptedException + * @throws KeeperException + */ + HTable setupTable(String tablename) throws Exception { + HTableDescriptor desc = new HTableDescriptor(tablename); + HColumnDescriptor hcd = new HColumnDescriptor(Bytes.toString(FAM)); + desc.addFamily(hcd); // If a table has no CF's it doesn't get checked + TESTING_UTIL.getHBaseAdmin().createTable(desc, SPLITS); + tbl = new HTable(TESTING_UTIL.getConfiguration(), tablename); + + List puts = new ArrayList(); + for (byte[] row : ROWKEYS) { + Put p = new Put(row); + p.add(FAM, Bytes.toBytes("val"), row); + puts.add(p); + } + tbl.put(puts); + tbl.flushCommits(); + return tbl; + } + @Test (timeout = 300000) public void testExistingZnodeBlocksSplitAndWeRollback() throws IOException, InterruptedException, NodeExistsException, KeeperException, ServiceException { final byte [] tableName =