diff --git src/main/java/org/apache/hadoop/hbase/master/HMaster.java src/main/java/org/apache/hadoop/hbase/master/HMaster.java index 9d21903..2048991 100644 --- src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -39,6 +39,7 @@ import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.Abortable; import org.apache.hadoop.hbase.Chore; import org.apache.hadoop.hbase.ClusterStatus; import org.apache.hadoop.hbase.HColumnDescriptor; @@ -405,8 +406,9 @@ implements HMasterInterface, HMasterRegionInterface, MasterServices, Server { this.balancer = LoadBalancerFactory.getLoadBalancer(conf); zooKeeper.registerListenerFirst(assignmentManager); - this.regionServerTracker = new RegionServerTracker(zooKeeper, this, - this.serverManager); + this.regionServerTracker = + createRegionServerTracker(this.zooKeeper, this, this.serverManager); + this.regionServerTracker.start(); this.drainingServerTracker = new DrainingServerTracker(zooKeeper, this, @@ -432,6 +434,18 @@ implements HMasterInterface, HMasterRegionInterface, MasterServices, Server { ", cluster-up flag was=" + wasUp); } + /** + * Used testing + * @param zkw + * @param a + * @param sm + * @return Instance of RegionServerTracker + */ + public RegionServerTracker createRegionServerTracker(final ZooKeeperWatcher zkw, + final Abortable a, final ServerManager sm) { + return new RegionServerTracker(zkw, a, sm); + } + // Check if we should stop every second. private Sleeper stopSleeper = new Sleeper(1000, this); private void loop() { @@ -512,8 +526,7 @@ implements HMasterInterface, HMasterRegionInterface, MasterServices, Server { // TODO: Should do this in background rather than block master startup status.setStatus("Splitting logs after master startup"); - this.fileSystemManager. - splitLogAfterStartup(this.serverManager.getOnlineServers().keySet()); + splitLogAfterStartup(this.fileSystemManager, this.serverManager); // Make sure root and meta assigned before proceeding. assignRootAndMeta(status); @@ -560,6 +573,16 @@ implements HMasterInterface, HMasterRegionInterface, MasterServices, Server { } /** + * Used in tests + * @param mfs + * @param sm + */ + public void splitLogAfterStartup(final MasterFileSystem mfs, + final ServerManager sm) { + mfs. splitLogAfterStartup(sm.getOnlineServers().keySet()); + } + + /** * Check -ROOT- and .META. are assigned. If not, * assign them. * @throws InterruptedException diff --git src/test/java/org/apache/hadoop/hbase/TestTest.java src/test/java/org/apache/hadoop/hbase/TestTest.java new file mode 100644 index 0000000..5da456e --- /dev/null +++ src/test/java/org/apache/hadoop/hbase/TestTest.java @@ -0,0 +1,94 @@ +package org.apache.hadoop.hbase; + + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.master.HMaster; +import org.apache.hadoop.hbase.master.MasterFileSystem; +import org.apache.hadoop.hbase.master.ServerManager; +import org.apache.hadoop.hbase.util.Threads; +import org.apache.hadoop.hbase.zookeeper.RegionServerTracker; +import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; +import org.apache.zookeeper.KeeperException; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestTest { + private static final HBaseTestingUtility TESTUTIL = + new HBaseTestingUtility(); + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + // Set it so that this test runs with my custom master + TESTUTIL.getConfiguration().setClass(HConstants.MASTER_IMPL, + TestingMaster.class, HMaster.class); + // Start up the cluster. + TESTUTIL.startMiniCluster(); + } + + @Test + public void testtest() { + // This test does nothing currently. + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + TESTUTIL.shutdownMiniCluster(); + } + + /** + * An HMaster instance used in this test. If 'TestingMaster.sleep' is set + * in the Configuration, then we'll sleep after log is split and we'll also + * return a custom RegionServerTracker. + */ + public static class TestingMaster extends HMaster { + public TestingMaster(Configuration conf) throws IOException, + KeeperException, InterruptedException { + super(conf); + } + + @Override + public void splitLogAfterStartup(MasterFileSystem mfs, ServerManager sm) { + super.splitLogAfterStartup(mfs, sm); + // If "TestingMaster.sleep" is set, sleep after log split. + if (getConfiguration().getBoolean("TestingMaster.sleep", false)) { + int duration = + getConfiguration().getInt("TestingMaster.sleep.duration", 0); + Threads.sleep(duration); + } + } + + @Override + public RegionServerTracker createRegionServerTracker(final ZooKeeperWatcher zkw, + final Abortable a, final ServerManager sm) { + // If "TestingMaster.sleep", then return our custom RegionServerTracker + return getConfiguration().getBoolean("TestingMaster.sleep", false)? + new GatedNodeDeleteRegionServerTracker(zkw, a, sm): + super.createRegionServerTracker(zkw, a, sm); + } + } + + /** + * A RegionServerTracker whose delete we can stall. + * On nodeDeleted, it will block until the data member gate is cleared. + */ + static class GatedNodeDeleteRegionServerTracker extends RegionServerTracker { + final AtomicBoolean gate = new AtomicBoolean(true); + + public GatedNodeDeleteRegionServerTracker(ZooKeeperWatcher watcher, + Abortable abortable, ServerManager serverManager) { + super(watcher, abortable, serverManager); + } + + @Override + public void nodeDeleted(String path) { + if (path.startsWith(watcher.rsZNode)) { + while(this.gate.get()) Threads.sleep(1); + } + super.nodeDeleted(path); + } + } +} \ No newline at end of file